信号量

信号量定义特殊的变量,只能+1v),-1p,都是原子操作(不能被打断)。

最简单的信号量是一个只有0与1两个值的变量,二值信号量。这是最为通常的形式。具有多个正数值的信号量被称之为通用信号量。

信号量的作用:通知系统产生了某种事件

Signal:改变对当前信号的响应方式(默认,忽略,自定义)

子进程状态发生改变,发送给父进程(子进程结束了,会给父进程发送)

Kill(pid_t pid,int sig)  结束进程的命令



PV操作:

P(sv)    如果sv大于0,减小sv。如果sv为0,挂起这个进程的执行。
V(sv)    如果有进程被挂起等待sv,使其恢复执行。如果没有进行被挂起等待sv,增加sv。


Linux信号量工具

信号量函数定义如下:

#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);   //初始化/删除信号量

int semget(key_t key, int num_sems, int sem_flags);    //创建信号量

int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);  //改变信号量的值(p,v)

包含sys/sem.h文件之前通常需要包含sys/types.h与sys/ipc.h文件

semget(创建一个新的信号量或是获得一个已存在的信号量键值)


int semget(key_t key, int num_sems, int sem_flags);

第一个参数key是一个用来允许不相关的进程访问相同信号量的整数值。所有的信号量是为不同的程序通过提供一个key来间接访问的,对于每一个信号量系统 生成一个信号量标识符。信号量键值只可以由semget获得,所有其他的信号量函数所用的信号量标识符都是由semget所返回的。

还有一个特殊的信号量key值,IPC_PRIVATE(通常为0),其作用是创建一个只有创建进程可以访问的信号量。这通常并没有有用的目的,而幸运的是,因为在某些Linux系统上,手册页将IPC_PRIVATE并没有阻止其他的进程访问信号量作为一个bug列出。

num_sems参数是所需要的信号量数目。这个值通常总是1。

sem_flags参数是一个标记集合,与open函数的标记十分类似。低九位是信号的权限,其作用与文件权限类似。另外,这些标记可以与 IPC_CREAT进行或操作来创建新的信号量。设置IPC_CREAT标记并且指定一个已经存在的信号量键值并不是一个错误。如果不需 要,IPC_CREAT标记只是被简单的忽略。我们可以使用IPC_CREAT与IPC_EXCL的组合来保证我们可以获得一个新的,唯一的信号量。如果 这个信号量已经存在,则会返回一个错误。

如果成功,semget函数会返回一个正数;这是用于其他信号量函数的标识符。如果失败,则会返回-1。

semop (用来改变信号量的值):


int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);

第一个参数,sem_id,是由semget函数所返回的信号量标识符。第二个参数,sem_ops,是一个指向结构数组的指针,其中的每一个结构至少包含下列成员:

struct sembuf {
    short sem_num;
    short sem_op;
    short sem_flg;
}

第一个成员,sem_num,是信号量数目,通常为0,除非我们正在使用一个信号量数组。sem_op成员是信号量的变化量值。(我们可以以任何量改变信 号量值,而不只是1)通常情况下中使用两个值,-1是我们的P操作,用来等待一个信号量变得可用,而+1是我们的V操作,用来通知一个信号量可用。

最后一个成员,sem_flg,通常设置为SEM_UNDO。这会使得操作系统跟踪当前进程对信号量所做的改变,而且如果进程终止而没有释放这个信号量, 如果信号量为这个进程所占有,这个标记可以使得操作系统自动释放这个信号量。将sem_flg设置为SEM_UNDO是一个好习惯,除非我们需要不同的行 为。如果我们确实变我们需要一个不同的值而不是SEM_UNDO,一致性是十分重要的,否则我们就会变得十分迷惑,当我们的进程退出时,内核是否会尝试清 理我们的信号量。

semop的所用动作会同时作用,从而避免多个信号量的使用所引起的竞争条件。我们可以在手册页中了解关于semop处理更为详细的信息。

semctl (允许信号量信息的直接控制):


int semctl(int sem_id, int sem_num, int command, ...);

第一个参数,sem_id,是由semget所获得的信号量标识符。sem_num参数是信号量数目。当我们使用信号量数组时会用到这个参数。通常,如果 这是第一个且是唯一的一个信号量,这个值为0。command参数是要执行的动作,而如果提供了额外的参数,则是union semun,根据X/OPEN规范,这个参数至少包括下列参数:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
}

许多版本的Linux在头文件(通常为sem.h)中定义了semun联合,尽管X/Open确认说我们必须定义我们自己的联合。如果我们发现我们确实需 要定义我们自己的联合,我们可以查看semctl手册页了解定义。如果有这样的情况,建议使用手册页中提供的定义,尽管这个定义与上面的有区别。

有多个不同的command值可以用于semctl。在这里我们描述两个会经常用到的值。要了解semctl功能的详细信息,我们应该查看手册页。

这两个通常的command值为:

SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
IPC_RMID:当信号量不再需要时用于删除一个信号量标识。

semctl函数依据command参数会返回不同的值。对于SETVAL与IPC_RMID,如果成功则会返回0,否则会返回-1。




eg:

sem.h

1.	#include <stdio.h>  
2.	#include <stdlib.h>  
3.	#include <unistd.h>  
4.	#include <sys/sem.h>  
5.	  
6.	union semun  
7.	{  
8.	    int val;  
9.	};  
10.	  
11.	void sem_init();  
12.	void sem_p();  
13.	void sem_v();  
14.	void sem_destroy();  

sem.c

1.	#include "sem.h"  
2.	  
3.	static int semid = 0;  
4.	  
5.	void sem_init()  
6.	{  
7.	    semid = semget((key_t)1234,1,IPC_CREAT | IPC_EXCL | 0600);  
8.	    if(semid == -1)  
9.	    {  
10.	        semid = semget((key_t)1234,1,0600);  
11.	        if(semid == -1)  
12.	        {  
13.	            perror("semget error");  
14.	        }  
15.	    }  
16.	    else  
17.	    {  
18.	        union semun a;  
19.	        a.val = 1;  
20.	        if(semctl(semid,0,SETVAL,a) == -1)  
21.	        {  
22.	            perror("semctl error\n");  
23.	        }  
24.	    }  
25.	}  
26.	  
27.	  
28.	void sem_p()  
29.	{  
30.	    struct sembuf buf;  
31.	    buf.sem_num = 0;  
32.	    buf.sem_op = -1;  
33.	    buf.sem_flg = SEM_UNDO;  
34.	    if(semop(semid,&buf,1) == -1)  
35.	    {  
36.	        perror("semop p error");  
37.	    }  
38.	}  
39.	  
40.	void sem_v()  
41.	{  
42.	    struct sembuf buf;  
43.	    buf.sem_num = 0;  
44.	    buf.sem_op = 1;  
45.	    buf.sem_flg = SEM_UNDO;  
46.	    if(semop(semid,&buf,1) == -1)  
47.	    {  
48.	        perror("semop v error");  
49.	    }  
50.	}  
51.	  
52.	void sem_destroy()  
53.	{  
54.	    if(semctl(semid,0,IPC_RMID) == -1)  
55.	    {  
56.	        perror("semctl error");  
57.	    }  
58.	}  

a.c

1.	#include <stdio.h>  
2.	#include <stdlib.h>  
3.	#include <string.h>  
4.	#include <unistd.h>  
5.	#include <assert.h>  
6.	#include <sys/sem.h>  
7.	#include "sem.h"  
8.	  
9.	int main()  
10.	{  
11.	    sem_init();  
12.	    int i = 0;  
13.	    for(;i < 5;i++)  
14.	    {  
15.	        sem_p();  
16.	        printf("A");  
17.	        fflush(stdout);  
18.	        int n = rand() % 3;  
19.	        sleep(n);  
20.	        printf("A");  
21.	        fflush(stdout);  
22.	          
23.	        sem_v();  
24.	        n = rand() % 3;  
25.	        sleep(n);  
26.	    }  
27.	    sleep(10);  
28.	    sem_destroy();  
29.	}  

b.c

1.	#include <stdio.h>  
2.	#include <stdlib.h>  
3.	#include <string.h>  
4.	#include <unistd.h>  
5.	#include "sem.h"  
6.	  
7.	int main()  
8.	{  
9.	    sem_init();  
10.	    int i = 0;  
11.	    for(;i < 5;i++)  
12.	    {  
13.	        sem_p();  
14.	        printf("B");  
15.	        fflush(stdout);  
16.	        int n = rand() % 3;  
17.	        sleep(n);  
18.	        printf("B");  
19.	        fflush(stdout);  
20.	          
21.	        sem_v();  
22.	        n = rand() % 3;  
23.	        sleep(n);  
24.	    }  
25.	}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值