|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
2 C8 l1 s# d# p8 p9 }, ?6 ?& W实验环境:Linux2.61 L" T0 A+ F* n( D5 g6 G( a% L
& i7 h+ h% M# [% G5 Q% N4 E
最近在操作设备文件的时候,要求使用独占模式使用串口设备,即一个进程用完之后释放该串口,供其他进程使用。该如何实现该需求呢?自然想到了用信号量来实现。信号量是什么呢?2 @4 R: \. w8 _8 @' B
/ X& W7 |3 d( a5 N, X8 c. ?
首先了解一下,信号量机概念是由荷兰科学家Dijkstr引入,值得一提的是,它提出的Dijksrtr算法解决了最短路径问题。
+ X+ o8 z5 x. B Y 信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况,信号量是一个特殊的变量,并且只有两个操作可以改变其值:等待(wait)与信号(signal)。
! @0 f9 d) x- x. s: s# K
2 M5 {, o" [% N9 |0 q" e: Y因为在Linux与UNIX编程中,"wait"与"signal"已经具有特殊的意义了(暂不知这特殊意义是啥),所以原始概念: 1 ^6 v$ P Y- m. I) H8 |
用于等待(wait)的P(信号量变量) ; $ n0 a9 S4 Q+ O6 z
用于信号(signal)的V(信号量变量) ; 3 W: N( P% v2 z' f4 E3 O* X
6 U9 {1 u) x1 {# @$ m6 e4 M
这两字母来自等待(passeren:通过,如同临界区前的检测点)与信号(vrjgeven:指定或释放,如同释放临界区的控制权)的荷兰语。% G: k3 V( j$ K3 x! W0 ]) H% |
3 W0 E- i: N( q- bP操作 负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。
* }, U" K+ N2 Z* e
w- E2 o+ X( ^5 e5 W; J+ U( L9 c" s: l
操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;; }6 \5 z9 m1 \
, C6 }/ I/ i2 J0 h! G
/ t1 B2 b9 V5 N; x: v0 n
V操作 负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。
& Z. n }4 G7 \3 x+ i6 |
& d; j7 l9 e* E S* o( c操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
! ~: Q' s5 o5 I$ P. e. ?, X3 A5 a. k7 E+ M5 |8 Q5 R
补充:查看共享信息的内存的命令是ipcs [-m|-s|-q] (全部的话是ipcs -a) ;查看共享信息的内存的命令是ipcs [-m|-s|-q]。
, `' d$ A9 J( C0 [
; g& L! n9 X% r7 w+ g( H头文件pv.h
% X7 x) H. f# T; H" ?" d
+ S4 H+ h: s9 z' f- ?# g- //pv.h头文件
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #include <errno.h>
- #include <stdlib.h>
- #define SEMPERM 0600
- typedef union _semun {
- int val;
- struct semid_ds *buf;
- ushort *array;
- } semun;
- int init_sem(key_t semkey);
- int p(int semid);
- int v(int semid);
- int destroysem();
8 Z/ O3 e1 I* T: m5 ]
$ _+ B5 j- a- o( D! _1 H//实现pv.c
( Q; j( j$ x; [
7 i* `/ b g) h; `' s- //pv.c 对信号量赋初值,初值固定为1
- //查看信号量
- //ipcs -s
- /*
- 创建信号量
- int semget(key_t key, int nsems, int semflg);
- key:自定义一个整数。这个参数类似open函数的第一个参数,由调用者指定一个“文件名”。
- nsems:要初始化多少个信号量,通常设为1。
- semflg:这个参数也和open函数类似。支持权限和IPC_CREAT以及IPC_EXCL
- IPC_CREAT表示要创建一个信号量,但是如果信号量已经存在了也不会报错。
- IPC_EXCL和IPC_CREAT一起使用时,如果信号量已经存在就会报EEXIST。
- 通常可以将semflg设为IPC_CREAT|IPC_EXCL|0666等
- */
- #include "pv.h"
- int init_sem(key_t semkey)
- {
- int status=0,semid; //信号量标识符semid
- if ((semid=semget(semkey,1,SEMPERM|IPC_CREAT|IPC_EXCL))==-1)
- {
- if (errno==EEXIST) //EEXIST:信号量集已经存在,无法创建
- semid=semget(semkey,1,0); //创建一个信号量
- }
- else
- {
- semun arg;
- arg.val=1; //信号量的初值
- status=sEMCtl(semid,0,SETVAL,arg); //设置信号量集中的一个单独的信号量的值。
- }
- if (semid==-1||status==-1)
- {
- perror("initsem failed");
- return(-1);
- }
- /*all ok*/
- return(semid);
- }
- int p(int semid)
- {
- struct sembuf p_buf;
- p_buf.sem_num=0;
- p_buf.sem_op=-1; //信号量减1,注意这一行的1前面有个负号
- p_buf.sem_flg=SEM_UNDO;
- //p_buf = {0,-1,SEM_UNDO};
- if (semop(semid, &p_buf, 1)==-1)
- {
- perror("p(semid)failed");
- exit(1);
- }
- return(0);
- }
- int v(int semid)
- {
- struct sembuf v_buf;
- v_buf.sem_num=0;
- v_buf.sem_op=1; //信号量加1
- v_buf.sem_flg=SEM_UNDO;
- if (semop(semid, &v_buf, 1)==-1)
- {
- perror("v(semid)failed");
- exit(1);
- }
- return(0);
- }
- int destroy_sem(int semid){
- // fprintf(stderr, "Failed to delete semaphore\n");
- return semctl(semid,0,IPC_RMID); //删除进程信号量值,IPC_RMID是删除命令
- }
9 h* j# g9 I9 v" T; R+ z 1 M, P7 S& S- S; A
测试程序如下:
; g" m; v* z# Q) b+ ?+ B2 H- O" h
7 {. S5 V) k$ |! P2 w- //testsem.c 主程序,使用PV操作实现三个进程的互斥
- #include "pv.h"
- void handlesem(key_t skey);
- int semid;
- main()
- {
- key_t semkey=0x200;
- int i;
- for (i=0;i<3;i++)
- {
- if (fork()==0) //父进程负责产生3个子进程
- handlesem(semkey); //子进程中才执行handlesem,做完后就exit。
- }
- if (destroy_sem(semid)<0)
- {
- perror("semctl error");
- exit(1);
- }
- }
- void handlesem(key_t skey)
- {
- int sleep_s=5;
- pid_t pid=getpid();
- if ((semid=init_sem(skey))<0)
- exit(1);
- printf("进程 %d 在临界资源区之前 \n",pid);
- p(semid); //进程进入临界资源区,信号量减少1
- printf("进程 %d 在使用临界资源时,停止%ds \n",pid,sleep_s);
- /*in real life do something interesting */
- sleep(sleep_s);
- printf("进程 %d 退出临界区后 \n",pid);
- v(semid); //进程退出临界资源区,信号量加1
- printf("进程 %d 完全退出\n",pid);
- exit(0);
- }2 i: h* h& {$ r1 `3 K2 O
* I! [- `0 P: N5 s编译命令gcc pv.c testsem.c -o testsem
3 b# b7 o1 Y6 H- M, H
5 e' I( \8 o0 W; J5 p执行之后即可看到效果,有个地方不太明白,就是信号量应该在什么时候释放掉?请各位网友多指教,感激不尽。- k) f5 F+ B( t3 ~' d! U
( y2 W6 O; O/ G7 ?* G* @' t9 n- g1 r$ H" H( b; z8 T' |
; S" z* w: @2 x
|
|