目录
保证同一时刻只能有一个进程对某个资源进行访问,防止因多个程序同时访问一个共享资源而引发的问题,使得在任意时刻只有一个执行线程访问代码的临界区域。而一个进程要持续不断的运行以等待某个内存位置被改变。
头文件:#include
- 作用:创建一个新信号量或者获取一个已有的信号量的键:
int semget(key_t key,int num_sems,int sem_flags);- 第一个参数key是整数值,不相关的进程可以通过它访问同一个信号量。程序对所有信号量的访问都是间接的,它先提供一个键,再由系统生成一个相应的信号量标识符。只有semget函数才直接使用信号量键,所有其他的信号量函数都是由semget函数返回信号量的标识符。
- 第二个参数num_sems:创建信号量的数目,一般取值为1。
- 第三个参数sem_flags:标志位,如果未创建时IPC_CREAT;如果为全新创建,不知道是否有人创建过,则为IPC_CREAT|IPC_EXCL。
- 返回值:成功返回一个整数(非零)值,是其他信号量函数将用到的信号量标识符。如果失败,返回-1。
- 作用:对信号量进行改变,做p或者v操作
int semop(int sem_id,struct sembuf * sem_ops,sieze_t num_sem_ops);- 第一个参数是返回的信号量标识符。表明是p操作还是v操作,p:-1,v:+1。
- 第二个参数是结构体指针,指向sembuf,sembuf内有三个结构体成员:
struct sembuf{ short sem_num; short sem_op; short sem_flg; }- sem_flg一般等于SEM_UNDO。这样的目的是:不论当前进程是否正常退出,都将还原此操作的 sem_op 值。用于以防万一异常结束的进程。
- 第三个参数是数组长度
- 作用:用来直接控制信号量信息
int semctl(int sem_id,int sem_num,int command,...);- 第一个参数是信号量标识符
- 第二个参数是信号量编号
- 第三个信号是将要采取的动作
- SETVAL:初始化信号量,该值通过下面union semun中的val成员设置,其作用是再信号量第一次使用之前对它进行设置。
- IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
- 如果有第四个参数,它会是一个union semun的联合体:
该结构体一般需要我们程序员自己定义
union semun{ int val; struct semid_ds *buf; unsigned short * array; }- 返回值:成功返回0,失败返回-1。
ipcrm -s id
信号量的使用只需用一个最简单的二进制信号量即可,在下面例子讲述用完整编程接口为二进制信号量创建一个非常简单的pv类型接口。
例子:
用进程a和进程b模拟访问打印机,进程a输出第一个字符”A“表示开始使用打印机,输出第二个字符”A“表示结束使用,b进程操作与a进程相同。如下图示:
sleep----开始使用
printf-----使用结束

1.编写a和b的代码:
- n=rand()%3; sleep(n);//随机睡眠3秒钟
- 在后台可以实现ab进程同时运行(后台&实现同时运行多进程),执行结果如下:
- 如果不加控制,AB交替出现,说明AB是在交替使用,A在sleep时B会打印出B,就不符合信号量的要求——A在使用时B不能使用。
由于打印机在同一时刻只能被一个进程使用,所以输出结果不应该出现abab这样交替的结果,下面使用信号量对其进行控制,使AB成对出现:
2.实现A在sleep时B不能使用,A和B是成对出现的:

头文件:seem.h
- #include
- #include
- #include
-
- //程序员自己定义semun联合体
- union semun
- {
- int val;
- };
-
- void sem_init();
- void sem_p(); //P操作
- void sem_v(); //V操作
- void sem_destory();//销毁
- ~
seem.cpp文件:
- //初始化
- void sem_init()
- {
- //semget:创建一个新信号量或者获取一个已有的信号量的键
- semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//1234:房间号,0600->文件权限
- if(semid==-1) //全新创建失败,获取已经存在的信号量
- {
- semid=semget((key_t)1234,1,0600);
- if(semid==-1)
- {
- perror("semget error");
- }
- else
- {
- //初始化
- union semun a;//定义联合体
- a.val=1;//初始化设置为1,假设刚开始可以使用
- //semctl:用来直接控制信号量信息
- if(semctl(semid,0,SETVAL,a)==-1)//编号从0开始
- {
- perror("semctl init error");
- }
- }
- }
- }
-
- void sem_p()//p操作
- {
- struct sembuf buf;
- buf.sem_num=0;
- buf.sem_op=-1;
- buf.sem_flg=SEM_UNDO;//一般都这样设置,是让操作系统记住使用了P操作
- //semop:对信号量进行改变,做p或者v操作
- if(semop(semid,&buf,1)==-1)//1:长度,只有一个信号量
- perror("p perror");
- }
-
- void sem_v()//v操作
- {
- struct sembuf buf;
- buf.sem_num=0;
- buf.sem_op=1;
- buf.sem_flg=SEM_UNDO;//一般都这样设置,是让操作系统记住使用了P操作
- if(semop(semid,&buf,1)==-1)//1:长度,只有一个信号量
- perror("v perror");
- }
- //销毁
- void sem_destory()
- {
- if(semctl(semid,0,IPC_RMID)==-1)
- perror("destory sem error");
- }
a.c:
- #include
- #include
- #include
- #include
- #include
- #include"seem.h"
-
- int main()
- {
- int i=0;
- //信号量初始化
- sem_init();
- for(;i<5;i++)
- {
- sem_p();
- printf("A");
- fflush(stdout);
- int n=rand()%3;
- sleep(n);
- printf("A");
- fflush(stdout);
- sem_v();
- n=rand()%3;
- sleep(n);
- }
- sleep(10);
- sem_destory();
- }
b.c:
- #include
- #include
- #include
- #include
- #include
- #include"seem.h"
-
- int main()
- {
- int i=0;
- sem_init();
- for(;i<5;i++)
- {
- sem_p();
- printf("B");
- fflush(stdout);
- int n=rand()%3;
- sleep(n);
- printf("B");
- fflush(stdout);
- sem_v();
- n=rand()%3;
- sleep(n);
- }
- }

此时,AB是成对打印的。
