Linux进程信号量使用备忘

来源:互联网 发布:数据分析职业发展 知乎 编辑:程序博客网 时间:2024/06/03 06:00

正文之前照例胡扯一下。
六月中旬女朋友跟我分手了,由于时间是考试前一周,结果考试也很差,挺难过的就没什么心情更新CSDN。

不久前接了个外包,需要跟nodejs后端配合控制外设。
Ubuntu下访问设备需要sudo,然而后端程序员发现无法sudo调用我写的上位机程序,所以方案就改成了共享内存+信号量。
由于信号量这个我以前写过,以为不会有什么问题,但由于当时掌握不透彻,还是出现了很多莫名其妙的问题。百度了很多,发现网上错误的太多,以讹传讹的也数不胜数,所以下面写一下正确的方法。
首先介绍下用到的函数与结构。

函数:

int semget(key_t key,int nsems,int semflg);
int semctl(int semid,int semnum,int cmd, union semun arg);
int semop(int semid,struct sembuf *sops,size_t nsops);

  1. key_t key : 为信号量的Key,自己随意设定。int nsems :需要的信号量的数量。 int semflg : 创建信号量的方式等。
  2. int semid :为调用semget返回的值,标示信号量。int semnum : 信号量的下标,0号,1号等等,如果后面是SETALL的话此处会被忽略,填什么都不影响。如果后面填的是SETVAL,则会将对应下标的信号量设置为对应的值。 union semun arg : 初始化的参数,见下文。
  3. int semid : 同上。 struct sembuf *sops:对信号量所做的操作,见下文。size_t nsops : 操作信号量的个数,一般一次操作一个所以写1。

结构:

struct sembuf{  unsigned short int sem_num; /* semaphore number *//*想要操作的信号量下标*/  short int sem_op;  /* semaphore operation *//*想要对信号量的操作,1为加1,-1为减1*/  short int sem_flg;  /* operation flag *//*这里参数有两个,SEM_UNDO与一个IPC_NOWAIT*/};union semun {    int val;/*设置单个信号量的值*/    struct semid_ds *buf;    unsigned short *array;/*设置多个信号量*/};

我重点说下short int sem_flg这个属性。
SEM_UNDO,程序如果退出了,不管怎么退出,对信号量所有的操作都取消,但其他程序对信号量的操作不受影响。
IPC_NOWAIT,不会堵塞,信号量为0返回错误代码。
如果既不想用IPC_NOWAIT,也不想用SEM_UNDO,一定要将其设置为0,否则会有奇怪问题。

初始化代码:

void Process_Sem_FirstConfig(){    unsigned short array[2]={1,0};    union semun sem_union;    int ShareMem_Flag = semget(650, 2, 0666 |IPC_CREAT);//获取Key为650的两个信号量组,没有则创建一个,权限为0666    sem_union.array = array;//初始化信号量组的初值,0号为1,1号为0    semctl(ShareMem_Flag, 0, SETALL, sem_union);//设置信号量}//信号量第一次创建,若非第一次创建只需要调用semget(650, 2, 0666)获得信号量标示即可。//千万不要再次调用semctl设置信号量初值

PV操作:

void Process_Sem_Wait(int Sem_ID, int Sem_Index)//(信号量标示,信号量组中信号量下标){    struct sembuf sem_b;    sem_b.sem_num = Sem_Index; /*id*/    sem_b.sem_op = -1;         /* P operation*/    sem_b.sem_flg = 0;    semop(Sem_ID, &sem_b, 1);}void Process_Sem_Post(int Sem_ID, int Sem_Index){    struct sembuf sem_b;    sem_b.sem_num = Sem_Index; /* id */    sem_b.sem_op = 1;  /* V operation */    sem_b.sem_flg = 0;    semop(Sem_ID, &sem_b, 1);}

销毁信号量

void Process_Sem_Delete(int ShareMem_Flag)//信号量标示{    union semun sem_union;    semctl(ShareMem_Flag, 0, IPC_RMID, sem_union);    semctl(ShareMem_Flag, 1, IPC_RMID, sem_union);}

倒是很简单,但如果研读不细的确很容易出错。