1,实验环境:Linux2.6
2,参考文献:https://www.cnblogs.com/LZYY/p/3453582.html
最近在操作设备文件的时候,要求使用独占模式使用串口设备,即一个进程用完之后释放该串口,供其他进程使用。该如何实现该需求呢?自然想到了用信号量来实现。信号量是什么呢?
首先了解一下,信号量机概念是由荷兰科学家Dijkstr引入,值得一提的是,它提出的Dijksrtr算法解决了最短路径问题。
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况,信号量是一个特殊的变量,并且只有两个操作可以改变其值:等待(wait)与信号(signal)。
因为在Linux与UNIX编程中,"wait"与"signal"已经具有特殊的意义了(暂不知这特殊意义是啥),所以原始概念:
用于等待(wait)的P(信号量变量) ;
用于信号(signal)的V(信号量变量) ;
这两字母来自等待(passeren:通过,如同临界区前的检测点)与信号(vrjgeven:指定或释放,如同释放临界区的控制权)的荷兰语。
P操作 负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。
操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
V操作 负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。
操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
补充:查看共享信息的内存的命令是ipcs [-m|-s|-q] (全部的话是ipcs -a) ;查看共享信息的内存的命令是ipcs [-m|-s|-q]。
头文件pv.h
//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();
//实现pv.c
//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是删除命令
}
测试程序如下:
//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);
}
编译命令gcc pv.c testsem.c -o testsem
执行之后即可看到效果,有个地方不太明白,就是信号量应该在什么时候释放掉?请各位网友多指教,感激不尽。