weiqi7777

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

0
阅读(2081)

信号量是操作系统中比较重要的东西了。用来协调各个进程对同一个资源的使用。本质上,信号量是一个计数器。当需要资源的时候,若信号量不为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所属头文件

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所属头文件

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 #include #include #include 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 #include #include 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进程才能执行。

Baidu
map