1、信号量的基本概念

信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源的同步访问。

临界资源可以简单的理解为在某一时刻只能由一个进程或线程进行操作的资源,这里的资源

可以是一段代码、一个变量或某种硬件资源。信号量的值大于或等于0时表示可供并发进程使用的

资源实体数;小于0时代表正在等待使用临界资源的进程数。

注意:这里的信号量跟信号是没有关系的。

与消息队列类似,linux内核也为每个信号量维护了一个semid_ds 数据结构实例,

在文件/usr/include/linux/sem.h中

struct semid_ds {
struct ipc_perm sem_perm; /* permissions .. see ipc.h */
__kernel_time_t sem_otime; /* last semop time */
__kernel_time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
struct sem_queue *sem_pending; /* pending operations to be processed */
struct sem_queue **sem_pending_last; /* last pending operation */
struct sem_undo *undo; /* undo requests on this array */
unsigned short sem_nsems; /* no. of semaphores in array */
};

2、信号量的创建与使用

linux下使用semget创建或打开信号量集,

int semget(key_t key, int nsems, int semflg);

该函数执行成功则返回一个信号量集的标识符,失败返回-1。返回的参数key是由ftok得到的键值;

第二个参数nsems指明要创建的信号量集包含的信号量个数。如果只是打开信号量,把nsems设置为0即可。该参数只在创建信号量集时有效。

第三个参数semflg为操作标识,可取如下值:

0:取信号量集标识符,若不存在则函数会报错

IPC_CREAT:当semflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符

IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错

上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限

错误代码:

EACCESS:没有权限

EEXIST:信号量集已经存在,无法创建

EIDRM:信号量集已经删除

ENOENT:信号量集不存在,同时semflg没有设置IPC_CREAT标志

ENOMEM:没有足够的内存创建新的信号量集

ENOSPC:超出限制

如果用semget创建了一个新的信号量集对象时,则semid_ds结构成员变量的值设置如下:

Ÿ        sem_otime设置为0。

Ÿ        sem_ctime设置为当前时间。

Ÿ        msg_qbytes设成系统的限制值。

Ÿ        sem_nsems设置为nsems参数的数值。

Ÿ        semflg的读写权限写入sem_perm.mode中。

Ÿ        sem_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。

--------------------------------------------------

3、信号量的操作

信号量的值与相应资源的使用情况有关,当它的值大于0时,表示当前可用资源的数量,当他的值小于0时,其绝对值表示等待

使用这个资源的进程个数。信号量的值仅能由PV操作来改变。

在linux下,pv操作通过调用函数semop实现。该函数定义在头文件sys/sem.h中,原型:

int semop(int semid, struct sembuf *sops, unsigned nsops);

对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作

semid:信号量集标识符

sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:

struct sembuf {

short semnum; /*信号量集合中的信号量编号,0代表第1个信号量*/

short val;/*若val>0进行V操作信号量值加val,表示进程释放控制的资源 */

/*若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误*/

/*若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误*/

short flag;  /*0 设置信号量的默认操作*/

/*IPC_NOWAIT设置信号量操作不等待*/

/*SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值*/

};

nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

错误代码:

E2BIG:一次对信号量个数的操作超过了系统限制

EACCESS:权限不够

EAGAIN:使用了IPC_NOWAIT,但操作不能继续进行

EFAULT:sops指向的地址无效

EIDRM:信号量集已经删除

sops为指向sembuf数组,定义所要进行的操作序列。下面是信号量操作举例。

struct sembuf sem_get={0,-1,IPC_NOWAIT}; /*将信号量对象中序号为0的信号量减1*/

struct sembuf sem_get={0,1,IPC_NOWAIT};  /*将信号量对象中序号为0的信号量加1*/

struct sembuf sem_get={0,0,0};           /*进程被阻塞,直到对应的信号量值为0*/

flag一般为0,若flag包含IPC_NOWAIT,则该操作为非阻塞操作。若flag包含SEM_UNDO,则当进程退出的时候会还原该进程的信号量操作,这个标志在某些情况下是很有用的,

比如某进程做了P操作得到资源,但还没来得及做V操作时就异常退出了,此时,其他进程就只能都阻塞在P操作上,于是造成了死锁。若采取SEM_UNDO标志,就可以避免因为进程异常退出而造成的死锁。

4、semctl (得到一个信号量集标识符或创建一个信号量集对象)

int semctl(int semid, int semnum, int cmd, union semun arg)

semid:信号量集标识符

semnum:信号量集数组上的下标,表示某一个信号量

cmd:

命令

解   释

IPC_STAT

从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中

IPC_SET

设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值

IPC_RMID

从内核中删除信号量集合

GETALL

从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中

GETNCNT

返回当前等待资源的进程个数

GETPID

返回最后一个执行系统调用semop()进程的PID

GETVAL

返回信号量集合内单个信号量的值

GETZCNT

返回当前等待100%资源利用的进程个数

SETALL

与GETALL正好相反

SETVAL

用联合体中val成员的值设置信号量集合中单个信号量的值

arg:

union semun {

short val;          /*SETVAL用的值*/

struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/

unsigned short* array; /*SETALL、GETALL用的数组值*/

struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/

} arg;

5、信号量的应用实例:

server创建一个信号量集,并对信号量循环减1,相当于分配资源。

client执行时jian'cha信号量,如果其值大于0代表有资源可用,继续执行,如果小于等于0代表资源已经分配完毕,进程client推出。

server:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h> #define SIZE 5 union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
}; int main()
{
key_t key;
int semid;
struct sembuf sbuf = {, -, IPC_NOWAIT};
union semun semopts; if ((key = ftok(".", )) == -)
{
perror("ftok error:");
exit();
} semid = semget(key, , IPC_CREAT|); semopts.val = SIZE; //第二个参数,信号量集数组上的下标,表示某一个信号量
if ( semctl(semid, , SETVAL, semopts) == - )
{
perror("semctl error:");
exit();
} while()
{
//第二个参数,指向进行操作的信号量集结构体数组的首地址
//第三个参数:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
if (semop(semid, &sbuf, ) == -)
{
perror("semop error:");
exit();
} sleep();
} return ;
}

client:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h> #define SIZE 5 union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
}; int main()
{
key_t key;
int semid;
int semval; if ((key = ftok(".", )) == -)
{
perror("ftok error:");
exit();
} semid = semget(key, , IPC_CREAT|); while()
{ if ( (semval = semctl(semid, , GETVAL, )) == - )
{
perror("semctl error:");
exit();
} if (semval > )
{
printf("still have %d resources can be used\n", semval);
}
else
{
printf("no more resources can be use\n");
} sleep();
} return ;
}

信号量的基本概念与使用semget,semop的更多相关文章

  1. semget() semop()

    semget() 可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集: 系统调用:semget();原型:intsemget(key_t key,int nsems, ...

  2. Linux进程间通信(五):信号量 semget()、semop()、semctl()

    这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信 -- 信号.下面 ...

  3. 信号量 Linux函数 semget();semctl();semop();(转)

    本文出自:http://blog.csdn.net/ta893115871/article/details/7505560 Linux进程通信之信号量 信号量(semaphore)是变量,是一种特殊的 ...

  4. 【转载】Linux的进程间通信-信号量

    原文:Linux的进程间通信-信号量 Linux的进程间通信-信号量 版权声明: 本文章内容在非商业使用前提下可无需授权任意转载.发布. 转载.发布请务必注明作者和其微博.微信公众号地址,以便读者询问 ...

  5. Linux 信号量互斥编程

    所谓信号量,其实就是一个数字.内核给这个数字赋予一定的含义,让它等于不同的值时所表示的意义不同.这样就可以用它来标示某种资源是否正被使用.信号的分类其实挺多的,主要还是二值和计数器.这里讨论二值 现在 ...

  6. Linux学习笔记26——信号量

    一 信号量的基本概念 信号量:它是一个特殊变量,只允许对它进行等待和发送信号这两种操作. 假设有一个信号量变量sv P(sv):用于等待,如果sv的值大于零,就给它减去1,如果它的值等于零,就挂起该进 ...

  7. Linux 进程通信之 ——信号和信号量总结

    如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一 ...

  8. Linux进程间通信(System V) --- 信号量

    信号量 IPC 原理 信号量通信机制主要用来实现进程间同步,避免并发访问共享资源.信号量可以标识系统可用资源的个数.最简单的信号量为二元信号量 下图为 Linux 信号量通信机制的概念图.在实际应用中 ...

  9. linux编程之信号量编程

    信号量当我们在多用户系统,多进程系统,或是两者混合的系统中使用线程操作编写程序时,我们经常会发现我们有段临界代码,在此处我们需要保证一个进程(或是一个线程的执行)需要排他的访问一个资源.信号量有一个复 ...

随机推荐

  1. Ubuntu apt-get和pip国内源更换

    Ubuntu apt-get和pip源更换 更新数据源为国内,是为了加速安装包的增加速度. 更换apt-get数据源 输入:sudo -s切换为root超级管理员: 执行命令:vim /etc/apt ...

  2. input的三个属性autocomplete、autocapitalize和autocorrect

    下面的input的三个属性是H5新增的属性 <input type="text" class="input-search" placeholder=&qu ...

  3. 痞子衡嵌入式:开源软件协议(MIT/BSD/Apache/LGPL/MPL/GPL)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是关于开源软件协议基本知识. 牛顿曾说过:"如果我比别人看得更远,那是因为我站在巨人的肩上".在软件开发中如果说也存在巨 ...

  4. [CF960G] Bandit Blues

    题意 给你三个正整数 \(n,a,b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满足 \(A = a ...

  5. Perl文件句柄相关常量变量

    文件句柄相关变量 对应的官方手册:http://perldoc.perl.org/perlvar.html#Variables-related-to-filehandles 默认情况下: $/:输入行 ...

  6. VS2013 OpenGL 开发程序时: error LNK2019: 无法解析的外部符号 __imp____glutInitWithExit@12,error LNK2019: 无法解析的外部符号 __imp____glutCreateWindowWithExit@8

    环境:Windows 下 OpenGL ,Used in VS2013 前言:刚接触 OpenGL 的人,第一件事当然就是配置环境,说起配置环境 OpenGL 和 DirectX 相差不多,同时也基本 ...

  7. ProtoBuf使用指南(C++)

    ProtoBuf使用指南(C++) Created: Mar 12, 2019 6:47 PM Last Edited Time: Mar 22, 2019 1:51 PM 1.安装部署 去官网(gi ...

  8. Java_冒泡排序_原理及优化

    冒泡排序及其优化 一.原理及优化原理 1.原理讲解 冒泡排序即:第一个数与第二个数进行比较,如果满足条件位置不变,再把第二个数与第三个数进行比较.不满足条件则替换位置,再把第二个数与第三个数进行比较, ...

  9. 洛谷P4492 [HAOI2018]苹果树(组合数)

    题意 题目链接 Sol 有点自闭,.我好像对组合数一窍不通(~~~~) Orz shadowice // luogu-judger-enable-o2 #include<bits/stdc++. ...

  10. mysql数据库表操作-表的主键索引和普通索引

    数据库索引就象书的目录一样,如果在字段上建立了索引,那么以索引列为查询条件时可以加快查询数据的速度.查询数据库,按主键查询是最快的,每个表只能有一个主键列,但是可以有多个普通索引列,主键列要求列的所有 ...