博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
System V信号量-semget()、semop()和semctl()
阅读量:2288 次
发布时间:2019-05-09

本文共 4135 字,大约阅读时间需要 13 分钟。

简单介绍

这是我开这个栏目的第一篇文章,顺序也不是按照《Unix网络编程》(以下简称网编)的章节顺序往下写的,可能文章会提及一些前面章节的概念,遇到的话,我会做一些引导,读者也可以自己找找相关的内容或书籍查看。

信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。我们讨论的信号量一般都包括二值信号量和计数信号量,而提及System V信号量时指的是计数信号量集,但是提及Poix 信号量指的就是单个的计数信号量。
对于系统中每一个信号量集,内核维护了一个如下的信息结构,定义在 sys/sem.h 头文件中,

struct semid_ds {    struct ipc_perm sem_perm;           /*  operation permission struct */    struct sem *sem_base;               /*  ptr to array of semaphores in set */    ushort sem_nsems;                   /*  number of sems in set */    time_t sem_otime;                   /*  last operation time (semop)*/    time_t sem_ctime;                   /*  time of creation or last IPC_SET */};

其中,ipc_perm含有当前这个信号量的访问权限(参考网编3.3节);

sem结构是内核用于维护某个给定信号量的一组值的数据结构,需要注意的是sem_base是指向sem结构数组的指针,当前信号量集中的每一个信号量对应其中的一个数组元素,结构如下:

struct sem {    unsigned short  semval;     /* semaphore value */    pid_t       sempid;         /* pid of last operation */    unsigned short  semncnt;    /* # awaiting semval > cval */    unsigned short  semzcnt;    /* # awaiting semval == 0 */};

我们假定信号量集中有两个信号量,该信号量集可以如下图表示:

这里写图片描述
注:更详细的信息大家可以参考《Unix网络编程》

函数介绍

System V 信号量涉及到的函数有:semget()semctl()semop(),下面我会一一介绍这几个函数的使用。

semget()

#include 
int semget(key_t key, int nsems, int semflg);//返回:若成功则返回一个称为信号量标识符的整数,semop和semctl函数//都将使用它;失败返回-1

功能:创建一个信号量集或者打开一个已存在的信号量集;

参数:
key 是一个key_t类型的整数,我们通常使用ftok函数将一个已存在的路径和一个整数标识符转换成一个key_t值,称为IPC键(网编P27);
nsems:信号量集中信号量的个数,如果是打开一个信号量集,nsems可以设置成0,信号量集创建完成之后,我们不能改变其中的信号量个数;
semflg:设置改信号量集的访问权限(网编3.4、3.5节),该参数的读写权限位存入sem_perm.mode;

struct ipc_perm{         uid_t           uid;            /* [XSI] Owner's user ID */         gid_t           gid;            /* [XSI] Owner's group ID */         uid_t           cuid;           /* [XSI] Creator's user ID */         gid_t           cgid;           /* [XSI] Creator's group ID */         mode_t          mode;           /* [XSI] Read/write permission */         unsigned short  _seq;           /* Reserved for internal use */         key_t           _key;           /* Reserved for internal use */};

我们来创建一个信号量:

key = ftok(".",'s');/** * 创建一个信号量 */int sem_create(key_t key){    int semid;    semid = semget(key,1,IPC_CREAT | 0666);  //1 信号量集中只有一个信号量,semflg还可以或上 IPC_EXCL 表示创建成功之后再次创建会失败    if (semid == -1){        ERR_EXIT("semget");    }    return semid;}

先看下有没有信号量集:

这里写图片描述
运行程序:
这里写图片描述
如果加上 IPC_EXCL,再次编译运行程序:
这里写图片描述
我们看到,程序直接报错了。
我们再写一个打开信号量集的函数:

/** * 打开一个信号量 */int sem_open(key_t key){    int sem_id;    sem_id = semget(key,0,0);    if(-1 == sem_id){        ERR_EXIT("semget");    }    return sem_id;}

当实际创建一个新的信号量集时,相应的semid_ds结构的以下成员将被初始化:

1、sem_perm结构中的uid和cuid被设置成调用进程的有效用户id,gid和guid被设置成调用进程的有效组id;
2、semflg参数的读写权限位存入sem_perm.mode;
3、sem_otime被置为0,sem_ctime被置为当前时间;
4、sem_nsems被置为nests参数的值;
需要注意的是,与该集合中每个信号量关联的sem结构并不初始化,需要调用semctl函数进行初始化;

semctl()

#include 
int semctl(int semid, int semnum, int cmd, .../* union semun arg */);//成功返回非负值,失败返回-1

功能:对一个信号量执行各种控制操作;

参数:
semid:待操作的信号量集,即由semget返回的信号量集标识符;
semnum: 标识信号量集中的某一个成员(0、1…知道nsems-1);
cmd:将对信号量执行的操作(如下);
第四个参数是可选的,取决于cmd;

union semun {    int     val;                /* value for SETVAL */    struct semid_ds *buf;       /* buffer for IPC_STAT & IPC_SET */    unsigned short  *array;     /* array for GETALL & SETALL */};

GETVAL:把semval的当前值作为函数的返回值返回;

SETVAL:把semval的值设置成arg.val;
GETALL:返回信号量集中每一个成员的semval的值,这些值通过arg.array指针返回;
SETALL:设置信号量集中每一个信号量的semval的值,这些值通过arg.array指针指定;
IPC_RMID:把由semid指定的信号量集从系统中删除;
IPC_SET:设置指定信号量集中的semid_ds结构中的以下三位成员:sem_perm.uid、sem_perm.gid和sem_perm.mode,这些值来自由arg.buf参数指向的结构中的相应成员;
IPC_STAT:通过arg.buf参数返回指定信号量集中的semid_ds结构;
现在,我们对信号量集能做的操作更多了,例如:设置一个信号量的值、获取一个信号量的值或者删除一个信号量集。

// 设置一个信号量的值int sem_setval(int semid,int val){    union semun su;    su.val = val;    int ret;    ret = semctl(semid,0,SETVAL,su);    if(-1 == ret){        ERR_EXIT("sem_setval");    }    return 0;}// 获取一个信号量的值int sem_getval(int semid){    int ret;    ret = semctl(semid,0,GETVAL,0);    if (-1 == ret){        ERR_EXIT("sem_getval");    }    printf("current val is:%d\n",ret);    return 0;}// 删除一个信号量集int sem_d(int semid){    int ret;    ret = semctl(semid,0,IPC_RMID,0);    if(-1 == ret){        ERR_EXIT("sem_d");    }    return 0;}
你可能感兴趣的文章
Render to Texture(渲染到纹理)
查看>>
如何在C++中集成Lua脚本(LuaPlus篇)
查看>>
查找字符串的hash算法
查看>>
软件开发中的矛盾——一个简单的例子
查看>>
VC补遗之Profile篇
查看>>
VC补遗之Debug篇
查看>>
OO世界里的几个基本问题
查看>>
OO给我们带来了什么?
查看>>
Mysql 存储过程
查看>>
虚拟化技术比较 PV HVM
查看>>
无法捕获的C++异常
查看>>
一台机器配置多个ip地址时被动响应和主动发起的源ip选择
查看>>
E1,T1, PRI, Trunk
查看>>
Top的VIRT是什么
查看>>
Linux内核调度器 CFS调优
查看>>
CPU-bound(计算密集型) 和I/O bound(I/O密集型)
查看>>
美国生活小常识
查看>>
从美国回国探亲可带礼品大汇总
查看>>
比较全面的航空公司行李规定以及行李问题咨询
查看>>
化妆品品牌中英文对照
查看>>