|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
9 Q/ A( B3 U% Y" w ~7 E
实验环境:Linux2.6. u/ O0 N2 g: ^3 q* t5 w8 V, F
1 ?9 T; ], w- h4 j$ s9 x最近在操作设备文件的时候,要求使用独占模式使用串口设备,即一个进程用完之后释放该串口,供其他进程使用。该如何实现该需求呢?自然想到了用信号量来实现。信号量是什么呢?/ I4 T9 b: Y. l, n- a
m5 F7 K) w( Y) k2 w, ^
首先了解一下,信号量机概念是由荷兰科学家Dijkstr引入,值得一提的是,它提出的Dijksrtr算法解决了最短路径问题。! q: ~: w8 g, _3 }8 e3 }0 h- i8 Y
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况,信号量是一个特殊的变量,并且只有两个操作可以改变其值:等待(wait)与信号(signal)。
9 Y* F$ i! K; S+ o0 E5 L
- G( f$ V. k5 w6 b4 b因为在Linux与UNIX编程中,"wait"与"signal"已经具有特殊的意义了(暂不知这特殊意义是啥),所以原始概念: , W' C" X' `# ~8 l7 x2 r3 H* E
用于等待(wait)的P(信号量变量) ; 1 O3 g+ ]+ N6 J& T) L. j1 q) }
用于信号(signal)的V(信号量变量) ;
9 {8 e( a- F; w7 B8 ?. x$ a3 V" o
这两字母来自等待(passeren:通过,如同临界区前的检测点)与信号(vrjgeven:指定或释放,如同释放临界区的控制权)的荷兰语。
1 X# u$ ^! ^8 g, a) a$ A3 s! A) \
0 \4 a7 u: {+ yP操作 负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。. E* h6 W! F# M7 n8 Y% C: C5 s) T
" C; Q- |5 g9 ^& o9 h5 L+ H( Y& x6 ^+ [" C
操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
" Q& _! J3 ?' b/ ]0 w* ]3 R5 D
- d$ a% Y: g0 x5 v, I1 ^3 i5 ~' F% p5 T0 I% S. R5 Q9 _0 z/ g
V操作 负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。
& t# f6 V' G: b N1 f8 A, X! d" G5 E K+ e
操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
q0 q* U M6 J2 c, j* O& M3 w# Y' _; _
补充:查看共享信息的内存的命令是ipcs [-m|-s|-q] (全部的话是ipcs -a) ;查看共享信息的内存的命令是ipcs [-m|-s|-q]。
# z5 L7 ~; b, L' ~6 ^2 ^" e3 o* {
5 N) k7 l- x3 y F) X; E头文件pv.h
. [# r+ V' e/ P. \& Z Y8 E6 p* x8 }* u! a
- //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();
( c ^6 b0 @) d3 Z2 G u % I7 Y% _5 s9 C% @
//实现pv.c
: P' ^; E. I3 S; {1 A- k/ f
& g$ I$ C# C4 O( |" h- //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是删除命令
- }
; G0 n6 j/ I; E% U5 i 7 f6 ]. z! [. K, @0 k
测试程序如下:; z) m! F! _1 D: W3 b: |
# v$ M1 F. s9 j4 o, ?, x, o* b
- //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);
- }: D% m6 N# L/ T( x2 d( M$ x
, y* r1 D4 p9 R8 a" @4 ^编译命令gcc pv.c testsem.c -o testsem
5 U) `+ g( J; O
' n; b$ g4 w; x执行之后即可看到效果,有个地方不太明白,就是信号量应该在什么时候释放掉?请各位网友多指教,感激不尽。0 B0 ~) b* ? P
( k. O" s$ T, r( t
2 c+ M! T R: i
y$ F+ ^3 K; t4 O% z8 R* I |
|