weiqi7777

进击吧,linux(十二) 信号量

0
阅读(2119)

     信号量是操作系统中比较重要的东西了。用来协调各个进程对同一个资源的使用。本质上,信号量是一个计数器。当需要资源的时候,若信号量不为0,信号量就减,为0的话,就将进程阻塞,直到信号量不为0。当释放资源的时候,信号量就加。通过信号量,可以实现资源的互斥和同步。

     信号量的学习,有两个函数。一个是创建信号量,一个是信号量的操作。

     信号量是和键值关联的。键值本质就是一个数字。Linux系统通过这个键值找到关联的信号量。所以,创建信号量的时候,要先设置键值。因为键值是要唯一的,所以就需要一个创建键值的方法,使创建出来的键值是唯一的。

     创建的信号量是一个信号量集合。这个集合里面,包括很多个信号量。要操作信号量的时候,要指定是操作信号量集合中的哪一个信号量。

          首先是信号量的创建

8.1创建/打开信号量集合

8.1.1函数名

   semget

8.1.2函数原形

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

8.1.3函数功能

   获取一个信号量的集合标识符,当key指定的信号量不存在的时候并且semflg包含了IPC_CREAT,就会创建一个信号量集合。

8.1.4所属头文件

   <sys/types.h> <sys/ipc.h> <sys/sem.h>

8.1.5返回值

   成功:信号量集合标识符

   失败:-1

8.1.6参数说明

   key:打开的信号量的键值

nsems:指定信号量集合包含的信号量个数

semflg:标志

-IPC_CREAT:KEY所关联的信号量不存在,就创建信号量

          创建的时候,就需要一个键值。键值,通过以下函数来获得。

                              key_t ftok(char *fname, int id)

          fname:随意一个目录。

          id:随意一个数字

          创建或者打开了信号量后,就要对信号量进行操作了

8.2操作信号量

8.2.1函数名

   semop

8.2.2函数原形

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

8.2.3函数功能

   对信号量操作,申请或者释放信号量

8.2.4所属头文件

   <sys/types.h> <sys/ipc.h> <sys/sem.h>

8.2.5返回值

   成功:0

   失败:-1

8.2.6参数说明

semid: 要操作的信号量集合的标识符

sops:    信号量操作的数组,每一个数组元素对应信号量的操作

struct sembuf{

   unsigned short sem_num; /*semaphore number*/

   short sem_op;     /*semaphore operation*/

   short sem_flg;/*operation flags*/

}

sem_num:对信号量集合里面的哪些信号操作

sem_op:信号量的操作,正数表示释放信号量,负数表示请求信号量,如果没有请求到信号量,那么进程就阻塞,等到有信号量为止。

sem_flg:

-SEM_UNDO:当信号操作失败,系统会自动的释放掉信号量

-SEM_NOWAIT:当信号操作造成进程阻塞,不阻塞程序,继续执行

nsops:要操作多少个信号量

          操作,其实就是靠一个结构体来操作的。

          下面,就是一个具体的代码,来学习上面的两个函数。有两个程序A和B,都往屏幕打印数据,但是必须要A打印完后,B才能打印。就可以使用信号量,A中创建信号量,并赋初值为1。然后申请该信号量,信号量就为0了。B中申请信号量就会失败,然后阻塞。等到A执行完毕释放信号量后,B有了信号量,继续执行。

          首先是A的代码:


#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<stdio.h>
void main()
{
	key_t key;
	int semid;
	int n;
	struct sembuf sops;
	/*创建键值*/
	key = ftok("/home",3);	
	/*创建并打开信号量*/
	semid = semget(key,1,IPC_CREAT);
	/*设置信号量*/
	semctl(semid,0,SETVAL,1);	
	/*读取信号量的值*/
	n = semctl(semid,0,GETVAL);
	printf("%d\n",n);
	/*获取信号量*/
	sops.sem_num = 0;
	sops.sem_op = -1;
	sops.sem_flg = SEM_UNDO;
	semop(semid,&sops,1);
	/*打印*/
	printf("hello ");
	/*暂停,休眠8秒钟*/
	sleep(4);
	/*打印*/
	printf("weiqi7777\n");	
	/*释放信号量*/
	sops.sem_num = 0;
	sops.sem_op = 1;
	sops.sem_flg = SEM_UNDO;
	semop(semid,&sops,1);	
}


   首先创建信号量集合。该集合只有一个信号量。该信号量集合关联一个键值,该键值通过ftok函数获得。然后将该信号量集合中的信号量值设置为1。因为只有一个信号量,所以信号量编号为0。然后对sops结构体赋值,在调用semop函数,对信号量的具体操作。

   然后是B的代码:


#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
void main()
{
	key_t key;
	int semid;
	int n;
	struct sembuf sops;
	/*创建键值*/
	key = ftok("/home",3);
	/*打开信号量*/
	semid = semget(key,1,IPC_CREAT);
	/*读取信号量值*/
	n = semctl(semid,0,GETVAL);
	printf("%d\n",n);
	/*获取信号量*/
	sops.sem_num = 0;
	sops.sem_op = -1;
	sops.sem_flg = SEM_UNDO;
	semop(semid,&sops,1);	
	/*打印*/
	printf("http://blog.chinaaet.com/weiqi7777\n");
	/*释放信号量*/
	sops.sem_num = 0;
	sops.sem_op = 1;
	sops.sem_flg = SEM_UNDO;
	semop(semid,&sops,1);
}


   B中,先打开之前创建的信号量,然后再申请信号量。最后在释放信号量。

   先运行A,在运行B。可见程序都停住了。A进程,打印信号量为1。可以申请到信号量,但是有sleep,所以就休眠了。B进程,信号量为0。申请信号量失败,程序阻塞。

   clip_image002

   等A进程休眠结束后,释放信号量,B进程得到信号量,就继续运行了。     

 clip_image004

          这里,有个奇怪的现象,A程序在执行后,就休眠了,而没有打印hello出来。照理来说,应该是打印hello后,再休眠的。通过我的测试,我认为是程序对printf这代码给优化掉了,因为只是单纯的打印字符串,就将打印的hello后面的weiqi7777给合并到一起了。所以休眠结束后,就打印了hello weiqi7777。如果将printf(“hello ”)换成printf("%d\n",n)的话,就不会有这种情况了,先打印n的值,然后再休眠。

   以上,是信号量的互斥应用。信号量还可以用于同步。不过在同步中,A进程应该将信号量的初值设置为0。然后释放信号量,这样B进程才能申请到信号量,才能继续执行。实现了A进程执行后,B进程才能执行。