EDA365电子论坛网
标题:
对于Linux信号量的一些理解和探讨(一定要看完哦,肯定有收获)
[打印本页]
作者:
thinkfunny
时间:
2020-3-4 09:17
标题:
对于Linux信号量的一些理解和探讨(一定要看完哦,肯定有收获)
; Y+ ?$ M3 u4 g2 t/ o O
实验环境:Linux2.6
* U% q8 ^! h5 I$ K; d6 Q% w- E) O: P
7 {. u3 y' S9 Z* k
最近在操作设备文件的时候,要求使用独占模式使用串口设备,即一个进程用完之后释放该串口,供其他进程使用。该如何实现该需求呢?自然想到了用信号量来实现。信号量是什么呢?
- Q0 p3 ?- ~8 l0 e: \( W7 m0 G
+ O) {0 i y! _) _( `6 t* D! t
首先了解一下,信号量机概念是由荷兰科学家Dijkstr引入,值得一提的是,它提出的Dijksrtr算法解决了最短路径问题。
7 D0 E* Z" I4 \0 [
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况,信号量是一个特殊的变量,并且只有两个操作可以改变其值:等待(wait)与信号(signal)。
( q p% h, @8 P. O, w9 ~/ V2 g
& z8 r$ Y b9 Y: h
因为在Linux与UNIX编程中,"wait"与"signal"已经具有特殊的意义了(暂不知这特殊意义是啥),所以原始概念:
# @9 v: y) u v' L q+ p2 N Z
用于等待(wait)的P(信号量变量) ;
* v9 Z( e/ I2 `, s' b; ^2 ^
用于信号(signal)的V(信号量变量) ;
8 X2 C& a0 T$ ~7 I
" ?! a# t" T% Q
这两字母来自等待(passeren:通过,如同临界区前的检测点)与信号(vrjgeven:指定或释放,如同释放临界区的控制权)的荷兰语。
* s4 I/ a( J; n, W) v
c- G( \) c- s6 O1 L
P操作 负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。
1 I. v2 D4 L, L4 k% h
2 V) e4 H7 ^ k* j7 B3 b' u
* d& g+ H9 p* V z
操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
! L9 Y! F6 R2 f6 T$ }
9 k3 D7 ]( A9 U
" @# c, h6 U; Z5 i8 j7 m
V操作 负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。
1 X9 K$ S5 t- i
* b1 ]8 b' w5 Y, M
操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
+ }. c0 R9 X3 p/ Q- W$ l, ^& j% ~
: l9 A8 l; ]; Q3 h# k
补充:查看共享信息的内存的命令是ipcs [-m|-s|-q] (全部的话是ipcs -a) ;查看共享信息的内存的命令是ipcs [-m|-s|-q]。
1 l0 b. c3 ] u/ @. N
! \. b+ J7 e9 u1 d8 {
头文件pv.h
! O' q- g( t7 t1 |& T8 `
% Q& H7 G# I3 x! a+ W) D
//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();
( o: Q& \0 d; s7 V6 B
2 u3 z# A8 ~! V/ E( J1 X6 m
//实现pv.c
* h; Y Z9 l4 T+ v6 [5 r
. o* }& a! p8 q7 A8 \& L/ A
//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是删除命令
}
% S, u" l) r8 \( X9 P j5 A# v
% [5 A$ d3 i9 B. V
测试程序如下:
, a" a# K" p0 K
0 Y* A8 x5 K2 G6 K! r6 p% i
//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);
}
" s" I3 D5 d9 F2 u. q1 c
9 I( o0 @- p/ x% O$ r6 ~1 n. I
编译命令gcc pv.c testsem.c -o testsem
' T3 ^3 _- P& m
, i8 b3 [! m! A& c& ~* T& c
执行之后即可看到效果,有个地方不太明白,就是信号量应该在什么时候释放掉?请各位网友多指教,感激不尽。
0 N( a% B0 q+ N3 V2 l
, ^% w" P+ T+ A1 ~, W
! b( _+ O9 |* U g- l/ o7 O
- j" P! J3 N \. r# Z$ G# H% R. C
作者:
ExxNEN
时间:
2020-3-4 17:37
对于Linux信号量的一些理解和探讨
欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/)
Powered by Discuz! X3.2