Linux IPC 之信号量

信号量(也叫信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。

信号量是进程/线程同步的一种方式,有时候我们需要保护一段代码,使它每次只能被一个执行进程/线程运行,这种工作就需要一个二进制开关;

有时候需要限制一段代码可以被多少个进程/线程执行,这就需要用到关于计数信号量。信号量开关是二进制信号量的一种逻辑扩展,两者实际调用的函数都是一样。

信号量分为以下三种。

1、System V信号量,在内核中维护,可用于进程或线程间的同步,常用于进程的同步。

2、Posix有名信号量,一种来源于POSIX技术规范的实时扩展方案(POSIX Realtime Extension),可用于进程或线程间的同步,常用于线程。

3、Posix基于内存的信号量,存放在共享内存区中,可用于进程或线程间的同步。

为了获得共享资源进程需要执行下列操作:

(1)测试控制该资源的信号量。

(2)若信号量的值为正,则进程可以使用该资源。进程信号量值减1,表示它使用了一个资源单位。此进程使用完共享资源后对应的信号量会加1。以便其他进程使用。

(3)若信号量的值为0,则进程进入休息状态,直至信号量值大于0。进程被唤醒,返回第(1)步。

为了正确地实现信号量,信号量值的测试值的测试及减1操作应当是原子操作(原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断)。为此信号量通常是在内核中实现的。

·System V IPC机制:信号量。

函数原型:

#include <sys/sem.h>

#include <sys/ipc.h>

#include <sys/types.h>

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

函数semget创建一个信号量集或访问一个已存在的信号量集。返回值:成功时,返回一个称为信号量标识符的整数,semop和semctl会使用它;出错时,返回-1.

参数key是唯一标识一个信号量的关键字,如果为IPC_PRIVATE(值为0,创建一个只有创建者进程才可以访问的信号量,通常用于父子进程之间;非0值的key(可以通过ftok函数获得)表示创建一个可以被多个进程共享的信号量;

参数nsems指定需要使用的信号量数目。如果是创建新集合,则必须制定nsems。如果引用一个现存的集合,则将nsems指定为0.

参数flag是一组标志,其作用与open函数的各种标志很相似。它低端的九个位是该信号量的权限,其作用相当于文件的访问权限。此外,它们还可以与键值IPC_CREAT按位或操作,以创建一个新的信号量。即使在设置了IPC_CREAT标志后给出的是一个现有的信号量的键字,也并不是一个错误。我们也可以通过IPC_CREAT和IPC_EXCL标志的联合使用确保自己将创建出一个新的独一无二的信号量来,如果该信号量已经存在,就会返回一个错误。

int semop(int semid,struct sembuf *sops,size_t nops);

函数semop用于改变信号量对象中各个信号量的状态。返回值:成功时,返回0;失败时,返回-1.

参数semid是由semget返回的信号量标识符。

参数sops是指向一个结构体数组的指针。每个数组元素至少包含以下几个成员:

 1 struct sembuf{
 2     short sem_num;  //操作信号在信号集中的编号,第一个信号的编号是0。
 3
 4     /*通常只会用到两个值,一个是-1,也就是p操作,它等待信号量变为可用;
 5     一个是+1,也就是v操作,它发送信号通知信号量现在可用。
 6      sem_op成员的值是信号量在一次操作中需要改变的数值*/
 7     short sem_op;
 8
 9     short sem_flg; //通常设为:SEM_UNDO,程序结束,信号量为semop调用前的值。
10 };

参数nops为sops指向的sembuf结构数组的大小。

int semctl(int semid, int semnum, int cmd, …);

函数semctl用来直接控制信号量信息。函数返回值:成功时,返回0;失败时,返回-1.

参数semid是由semget返回的信号量标识符。

参数semnum为集合中信号量的编号,当要用到成组的信号量时,从0开始。一般取值为0,表示这是第一个也是唯一的一个信号量。

参数cmd为执行的操作。通常为:IPC_RMID(立即删除信号集,唤醒所有被阻塞的进程)、GETVAL(根据semnun返回信号的值)、SETVAL(根据semun设定信号的值)、GETALL(获取所有信号量的值,第二个参数为0,将所有信号的值存入semun.array中)、SETALL(将所有semun.array的值设定到信号集中,第二个参数为0)等。

参数…是一个union semun(需要由程序员自己定义),它至少包含以下几个成员:

1 union semun{
2     int val; /* Value for SETVAL */
3
4     struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
5
6     unsigned short *array; /* Array for GETALL, SETALL */
7 };

示例:

sem1.c

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<unistd.h>
 5 #include<sys/types.h>
 6 #include<sys/ipc.h>
 7 #include<sys/sem.h>
 8
 9 //P操作,申请一个资源
10 void P(int semid)
11 {
12     struct sembuf my_buf;
13     memset(&my_buf,0,sizeof(my_buf));
14     my_buf.sem_num = 0;
15     my_buf.sem_op = -1;
16     my_buf.sem_flg = SEM_UNDO;
17     semop(semid,&my_buf,1);
18 }
19
20 void V(int semid)
21 {
22     struct sembuf my_buf;
23     memset(&my_buf,0,sizeof(my_buf));
24     my_buf.sem_num = 0;
25     my_buf.sem_op = 1;
26     my_buf.sem_flg = SEM_UNDO;
27     semop(semid,&my_buf,1);
28 }
29
30 int main(int *argc, char *argv[])
31 {
32     key_t my_key = 1234;
33     int my_sem;
34
35     //创建一个信号量集或访问一个已存在的信号量集
36     my_sem = semget(my_key,1,0666 | IPC_CREAT);
37     if(my_sem == -1)
38     {
39         printf("semget error!\n");
40         exit(1);
41     }
42     printf("before setval: %d\n",semctl(my_sem,0,GETVAL));
43     printf("setting val...\n");
44
45     semctl(my_sem,0,SETVAL,1);
46     printf("After setval:%d\n",semctl(my_sem,0,GETVAL));
47     P(my_sem);
48     while(getchar() != EOF);
49     V(my_sem);
50
51     sleep(5);
52     semctl(my_sem,0,IPC_RMID);
53
54     return 0;
55 }

sem2.c

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<unistd.h>
 5 #include<sys/types.h>
 6 #include<sys/ipc.h>
 7 #include<sys/sem.h>
 8
 9 void P(int semid)
10 {
11     struct sembuf my_buf;
12     memset(&my_buf,0,sizeof(my_buf));
13     my_buf.sem_num = 0;
14     my_buf.sem_op = -1;
15     my_buf.sem_flg = SEM_UNDO;
16     semop(semid,&my_buf,1);
17 }
18
19 void V(int semid)
20 {
21     struct sembuf my_buf;
22     memset(&my_buf,0,sizeof(my_buf));
23     my_buf.sem_num = 0;
24     my_buf.sem_op = 1;
25     my_buf.sem_flg = SEM_UNDO;
26     semop(semid,&my_buf,1);
27 }
28
29 int main(int *argc, char *argv[])
30 {
31     key_t my_key = 1234;
32     int my_sem;
33
34     my_sem = semget(my_key,1,0666);
35     if(my_sem == -1)
36     {
37         printf("semget error!\n");
38         exit(1);
39     }
40     printf("getval:%d\n",semctl(my_sem,0,GETVAL));
41
42     printf("p....\n");
43     P(my_sem);
44     printf("sem1 finished!\n");
45     V(my_sem);
46
47     return 0;
48 }
时间: 2024-07-29 15:10:30

Linux IPC 之信号量的相关文章

Linux进程间通信:信号量

上次书写了进程间通信的消息队列,这次是IPC中的另一个模块.信号量 信号量是什么? 荷兰计算机科学家Dijkstra把互斥的关键含义抽象称为信号量(semaphore)概念.信号量是一个被保护的量. 信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的 通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识.信号 量在此过程中负责数据操作的互斥.同步等功能. 当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可 用.大于0,资源可

Linux IPC实践(1) -- 概述

进程的同步与互斥 进程同步: 多个进程需要相互配合共同完成一项任务. 进程互斥: 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥;系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源, 而在进程中涉及到互斥资源的程序段叫临界区. Linux IPC发展 Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克

Linux IPC 共享内存用法

Linux IPC 常见的方式 写 Linux Server 端程序,必然会涉及到进程间通信 IPC. 通信必然伴随着同步机制,下面是一些常见的通信与同步机制: 进程间通信:匿名管道,命名管道,消息队列,共享内存,Domain Socket, 本机 TCP Socket,文件 进程间同步:信号,信号量 线程间同步:条件变量,互斥量,读写锁,自旋锁,Barrier. 对于大部分的业务场景,本机 TCP Socket 足以,现在Linux 也对本机 TCP Socket做了很好的优化.而且如果以后需

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

原文:Linux的进程间通信-信号量 Linux的进程间通信-信号量 版权声明: 本文章内容在非商业使用前提下可无需授权任意转载.发布. 转载.发布请务必注明作者和其微博.微信公众号地址,以便读者询问问题和甄误反馈,共同进步. 微博ID:orroz 微信公众号:Linux系统技术 前言 信号量又叫信号灯,也有人把它叫做信号集,本文遵循<UNIX环境高级编程>的叫法,仍称其为信号量.它的英文是semaphores,本意是“旗语”“信号”的意思.由于其叫法中包含“信号”这个关键字,所以容易跟另一个

细说linux IPC(五):system V共享内存

system V共享内存和posix共享内存类似,system V共享内存是调用shmget函数和shamat函数.           shmget函数创建共享内存区,或者访问一个存在的内存区,类似系统调用共享内存的open和posix共享内存shm_open函数.shmget函数原型为: #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); key: 函

细说linux IPC(三):mmap系统调用共享内存

前面讲到socket的进程间通信方式,这种方式在进程间传递数据时首先需要从进程1地址空间中把数据拷贝到内核,内核再将数据拷贝到进程2的地址空间 中,也就是数据传递需要经过内核传递.这样在处理较多数据时效率不是很高,而让多个进程共享一片内存区则解决了之前socket进程通信的问题.共享内存 是最快的进程间通信 ,将一片内存映射到多个进程地址空间中,那么进程间的数据传递将不在涉及内核.        共享内存并不是从某一进程拥有的内存中划分出来的:进程的内存总是私有的.共享内存是从系统的空闲内存池中

Linux IPC简单总结

###Linux IPC### --------------- ##消息传递## - 信号: 非亲缘,传递信息少,只能是控制信息而不是数据信息 - 管道: 要亲缘,无边界字节流,先进先出,消耗掉就没了,没有窥探功能MSG_PEEK - FIFO: 非亲缘,无边界字节流,先进先出,消耗掉就没了,没有窥探功能MSG_PEEK - Socket: 非亲缘,边界可有可无,效率较低 - Unix Socket: 非亲缘,边界可有可无,比Socket的效率高,比共享内存的效率低 - 消息队列:无亲缘,有边界

Linux IPC之共享内存C 事例

Linux IPC之共享内存 标签: linuxrandomnull工作 2011-08-25 11:52 4123人阅读 评论(0) 收藏 举报  分类: Linux(3)  读书札记(3)  版权声明:本文为博主原创文章,未经博主允许不得转载. 简介 共享内存(shared memory)是最简单的Linux进程间通信方式之一.使用共享内存,不同进程可以对同一块内存进行读写.由于所有进程对共享内存的访问就和访问自己的内存空间一样,而不需要进行额外系统调用或内核操作,同时还避免了多余的内存拷贝

Linux多线程编程-信号量

在Linux中,信号量API有两组,一组是多进程编程中的System V IPC信号量:另外一组是我们要讨论的POSIX信号量.这两组接口类似,但不保证互换.POSIX信号量函数都已sem_开头,并不像大多数线程函数那样以pthread_开头,常用的有以下5个: #include <semaphore.h> int sem_init(sem_t* sem, int pshared, unsigned int value); int sem_destroy(sem_t *sem); int se