linux应用编程笔记(12)信号量详解及互斥编程
来源:互联网 发布:晚会暖场小游戏知乎 编辑:程序博客网 时间:2024/06/18 04:52
摘要: 总结了信号量的机制,以及各个信号量操作的函数,最后通过公示栏问题,将信号量机制引入加深了理解。
一、什么是信号量
信号量的主要用途是保护临界资源,进程根据信号量用于判断能否访问某些共享资源,除了用于访问控制以为,还可以用于进程间的同步。当信号量的值只能取0或者1的时候,叫二值信号量,当可以取任意非负值的时候叫做计数信号量。
二、公示栏问题
有一个公示栏,甲乙都想往里写东西,甲写了一段文字之后,有事离开了一下,这时候乙再往里写了一些东西,甲回来再接着自己之前的写,这样整个公示栏就混乱了。这其实抽象的是两个进程同时访问一个资源,但是这个资源在一个时间内只能被一个进程访问,这样就会造成数据混乱,为了避免这种情况,一种比较好的解决办法就是信号量的互斥编程。
三、将公示栏问题程序化
我们将公示栏用文件代替,甲乙同学用AB两个进程代替,这样相应的对公示栏的操作就可以转化为AB两个进程对文件的操作。
首先是A进程,往文件里写入“数学课取消”,在写完“数学课”的时候,休息一段时间,然后接着写“取消”
B进程往文件里写入“英语课考试”。
程序分别如下:
<span style="font-size:18px;">processA.c#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h> int main(void){ int fd; /*打开公示栏并判断是否打开成功*/ fd=open("./board.txt",O_RDWR|O_APPEND); if(fd==-1) { printf("openerror!\n"); exit(0); } /*写入一部分*/ write(fd,"math class ",12); /*休息*/ sleep(20); /*接着写*/ write(fd,"is cancle",10); /*关闭公告栏,写完了*/ close(fd); return 0; }</span>
<span style="font-size:18px;">bprocess.c的内容如下:#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h> int main(void){ int fd; /*打开公示栏*/ fd=open("./board.txt",O_RDWR|O_APPEND); if(fd==-1) { printf("openerror!\n"); exit(0); } /*往里写入内容*/ write(fd,"englishexam",15); /*关闭公示栏,写完了*/ close(fd); return 0; }</span>
编译运行,然后先运行a再运行b,在当前目录下使用ctrl+shit+T来创建一个新的窗口,运行完了之后,就可以看到board.txt里面写入了如下内容,和我们之前预计的情况是一样的,这种情况在操作系统里面是经常可以看到的。
四、信号量操作函数
1.创建/打开信号量
函数名:semget
函数原型:int semget(key_t key,int nsems,int semflg);
函数功能:获取信号量集合的标识符
当key键值指定的信号量集合不存在,并且semflg等于IPC_CREAT的时候,就创建信号量集合,并和键值关联,返回与之对应的标识符。
头文件:#include <sys/types.h> #include <sys/ipc.h>#include <sys/sem.h>
返回值:成功返回信号量集合的标识符,失败返回-1
参数说明:
key_t key:键值。那么什么是键值?我们在open一个文件的时候,是通过文件名来访问这个文件的,然后open返回的时候,就会返回这个文件的句柄,也就是我们的fd,这里也是一样,当我们使用semget的时候,成功了就会返回信号量集合的标识符,相当于文件的fd,但是之前我们怎么去访问这个信号量呢,那就是通过这个key,也就是相当于文件名,这个key就是键值,在系统中其实就是一个数字。
如何获得键值?有两种方法:第一种是任意指定一个数字,但是这个数字有可能被别的IPC对象,例如消息队列,共享内存使用了,这样与创建的新的信号量关联的时候就会失败。第二种方法是使用一个函数来构造,构造出来的键值是可用的,不会已经被别人用过的:
key_t ftok(char*fname,int id);
那么这个ftok是如何工作的呢?两个参数,一个是我们要使用的文件名,另一个是我们的项目id,ftok会根据这个两个数字组合成一个信号量,当ab进程使用的时候根据文件名和项目id就会访问到相同的信号量集合。
int semflg:标志,可以取IPC_CREAT,意思是当前没有key对应的信号量集合的时候,就创建它,这一点类似open,通过文件名打开,如果文件不存在就自己创建它,这个很好理解吧。
int nsems:创建信号量集合里面包含的信号量数目。
2.操作信号量函数
函数名:semop
函数原型:int semop(int semid,struct sembuf *sops,unsigned nsops);
函数功能:对信号量进行操作,包括获取和释放
头文件:#include <sys/ipc.h> #include <sys/types.h>#include <sys/sem.h>
返回值:成功返回0,失败返回-1
参数说明:
int semid:要操作的信号量集合的标识符
struct sembuf *sops:要操作信号量集合里哪一个信号量,以及对信号量是进行+还是-,这里+意味着获取信号量,-意味着释放信号量,其中的sem_flg是在无法获取信号量之后是等待还是什么都不做就退出。
unsigned nsops:要操作多少个信号量
3.信号量初始值检查和设置函数
函数名:semctl
函数原型:int semctl(int semid,int semnum,int cmd,…);
函数功能:检查信号量集合中指定的信号量当前的值为多少
头文件:#include <sys/ipc.h> #include <sys/types.h> #include<sys/sem.h>
返回值:失败返回-1,成功根据cmd给入的参数返回响应的值,这里我们是GETVAL
参数说明:同上,只是这里的cmd需要注意,可以man 2semctl查看
五、利用信号量互斥控制公示栏
进入aprocess程序,将信号量互斥机制加进去,a在写之前要获取信号量,无法获得就不能写,获得之后才能写,写完了释放,这样b就可以获得再去写,写完了也要释放,这当中还要进行信号量初始值的检查。
<span style="font-size:18px;">aprocess.c内容如下:#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <sys/ipc.h>#include <sys/sem.h> int main(void){ int fd; key_t key;//键值 int semid; int retval; struct sembuf sops; /*创建信号量*/ key= ftok("/home/passionbird",1);//这里可以利用一个目录创建多个键值,只要他们的项目编号不一样就可以 semid=semget(key,1,IPC_CREAT);//将键值传入,我们这里信号量集合里就只有一个信号量,因为还没有,所以需要创建,加上IPC_CREAT /*检查信号量的初始值*/ retval=semctl(semid,0,GETVAL); printf("theinit value is:%d\n",retval); retval=semctl(semid,0,SETVAL,1);//设置为1 printf("the init value is:%d\n",retval);//两处打印是为了确保初始值为1 /*打开公示栏并判断是否打开成功*/ fd=open("./board.txt",O_RDWR|O_APPEND); if(fd==-1) { printf("open error!\n"); exit(0); } /*获取信号量*/ sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0 sops.sem_op= -1;//-1即获取走了信号量 semop(semid,&sops,1); //传入返回的semid,值操作一个信号量,这里需要提前顶一个struct sembuf /*写入一部分*/ write(fd,"mathclass ",12); /*休息*/ sleep(20); /*接着写*/ write(fd,"iscancle",12); /*释放信号量*/ sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0 sops.sem_op= +1;//+1即释放了信号量,写成+1是为了便于理解 semop(semid,&sops,1); /*关闭公告栏,写完了*/ close(fd); return 0; } bprocess.c内容如下;#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <sys/ipc.h>#include <sys/sem.h> int main(void){ int fd; key_t key; int semid; int retval; struct sembuf sops; /*打开公示栏*/ fd=open("./board.txt",O_RDWR|O_APPEND); /*打开信号量*/ key= ftok("/home/passionbird",1);//利用相同的键值可以关联同一个信号量 semid=semget(key,1,IPC_CREAT);//将键值传入,使得a,b打开的信号量是同一个信号量,这里已经有了不会再创建 if(fd==-1) { printf("openerror!\n"); exit(0); } /*获取前想查看初始值*/ retval=semctl(semid,0,GETVAL); printf("theinit value is:%d\n",retval); /*获取信号量*/ sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0 sops.sem_op= -1;//-1即获取走了信号量 semop(semid,&sops,1); //传入返回的semid,值操作一个信号量,这里需要提前顶一个struct sembuf /*往里写入内容*/ write(fd,"englishexam",15); /*释放信号量*/ sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0 sops.sem_op= +1;//+1即释放了信号量,写成+1是为了便于理解 semop(semid,&sops,1); retval=semctl(semid,0,GETVAL); printf("theinit value is:%d\n",retval); /*关闭公示栏,写完了*/ close(fd); return0; } </span>
最终编译运行,打开两个窗口,想运行a,在运行b,b会在运行的时候等待a休息结束写完释放信号量,b获得信号量之后才会往里写东西,打印出来的效果如下:
这下数据就不混乱了,数学课取消,英语课考试。
这篇帖子就总结到这里,如有不正确的地方还请指出,大家共同进步!
- linux应用编程笔记(12)信号量详解及互斥编程
- linux应用开发-信号量互斥编程
- Linux 信号量互斥编程
- 信号量互斥编程
- 信号量互斥编程
- 信号量互斥编程
- 【Linux信号通讯编程】信号量互斥编程
- linux 多线程编程 之 信号量互斥同步
- linux 多线程编程 之 信号量互斥同步
- linux 多线程编程 之 信号量互斥同步
- linux 多线程编程 之 信号量互斥同步
- linux应用编程笔记(13)信号量同步编程
- Linux_4.6_信号量互斥编程
- 2-16 信号量互斥编程
- Linux信号量机制及编程
- 【Linux】进程间通信-信号量详解及编程实例
- 【Linux】进程间通信-信号量详解及编程实例
- 【Linux】进程间通信-信号量详解及编程实例
- 招聘一个靠谱的iOS 面试题参考
- 日经春秋 20151126
- 抽象类
- 面试题
- QCA4010上手使用
- linux应用编程笔记(12)信号量详解及互斥编程
- Git与Repo入门
- 文章标题
- iOS版PhoneGap原理分析
- android-Fragment
- Hybrid选型和PhoneGap开发环境搭建
- 安装PHP运行环境时候vcruntime140.dll问题的解决方法
- 约瑟夫报数出列题
- 复杂写法的宏