多进程间通信学习之信号灯集(信号量数组)
  • 信号灯集(信号量数组):
  • 1、实现进程同步的机制
  • 2、在一个信号灯集中,可以有很多信号灯;
  • 3、这些信号灯集中的信号灯相互独立,每个灯的值的改变都不会影响到其他的信号灯;
  • 4、信号灯的值一般设置为二值量,即0或者1,其中0代表没有资源,1代表有资源
  • 常用的接口函数:
  • 一、创建一个信号灯集(semget函数):
	#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);/*参数:key:键值IPC_PRIVATE  keynsems:信号灯集合中信号灯的个数semflag:创建的标志位IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666返回值:成功 semid失败 -1  重置错误码*/
  • 二、控制信号灯集(semctl函数):
	#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);/*参数:semid信号灯集的IDsenum:信号灯的编号 从0开始cmd:命令码SETVAL:设置信号灯的值 --->第四个参数val选项GETVAL:获取信号灯的值 --->不需要第四个参数IPC_STAT:获取信号灯集的属性--->第四个参数buf选项IPC_SET :设置信号灯集的属性--->第四个参数buf选项IPC_RMID:第二参数被忽略,第4个参数不用填写 @...:可变参返回值:成功:GETVAL:成功返回信号灯的值其余的命令码成功返回0失败 -1  重置错误码*/union semun{int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */};
  • 三、操作信号灯集中信号灯(semop函数):
	#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);/*参数:semid:信号灯集的编号sops:操作方式struct sembuf{unsigned short sem_num; //信号灯的编号short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作}nsops:本次操作信号灯的个数返回值:成功 0失败 -1  重置错误码*/
  • 自定义封装信号灯集函数:
  • 自定义头文件:
	#ifndef __SEM_H__#define __SEM_H__int sem_init(int nsems);    //信号灯集中信号灯的个数int sem_wait_P(int semid,int sem_num);//信号灯集的编号,信号灯的编号int sem_post_V(int semid,int sem_num);int sem_exit(int semid);#endif
  • 源码文件:
	#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <sys/shm.h>#include <unistd.h>#include "sem.h"#include <errno.h>union semun {int val;    //设置信号灯的值struct semid_ds *buf;   //信号灯集的属性};/*struct sembuf{unsigned short sem_num; //信号灯的编号short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作}*/int sem_init(int nsems)   //信号灯集中信号灯的个数{int semid = 0;key_t key = ftok("/home/linux/work", 'K');if(-1 == key){perror("ftok error");exit(-1);}//创建信号灯集/*当使用IPC_EXCL选项时,如果共享资源已经存在,则创建操作将失败并返回-1。这个选项主要用于确保在创建共享资源时不会发生冲突。*/if(-1 == (semid = semget(key,nsems,IPC_CREAT|IPC_EXCL|0666))) {if(EEXIST == errno){if(-1 == (semid = semget(key,nsems,IPC_CREAT|0666))){perror("semget error");exit(-1);}return semid;}else{perror("semget error");exit(-1);}}//初始化信号灯集union semun myval = {.val = 1};if(-1 == semctl(semid,0,SETVAL,myval)){perror("semctl error");exit(-1);}myval.val = 0;for(int i = 1; i < nsems; i++){if(-1 == semctl(semid,i,SETVAL,myval)){perror("semctl error");exit(-1);}}return semid;}//获取资源int sem_wait_P(int semid,int sem_num)//信号灯集的编号,信号灯的编号{struct sembuf mysem;mysem.sem_num = sem_num;mysem.sem_op = -1;mysem.sem_flg = 0;if(-1 == semop(semid,&mysem,1)){perror("semop error");exit(-1);}}//释放资源int sem_post_V(int semid,int sem_num){struct sembuf mysem;mysem.sem_num = sem_num;mysem.sem_op = 1;mysem.sem_flg = 0;if(-1 == semop(semid,&mysem,1)){perror("semop error");exit(-1);}}//销毁信号灯集int sem_exit(int semid){if(-1 == semctl(semid,0,IPC_RMID)){perror("semctl error");exit(-1);}}
  • 使用信号灯集实现共享内存的同步:
  • 写端:
	#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <sys/shm.h>#include <unistd.h>#include "sem.h"#include <errno.h>#define PIGE_SIZE 4*1024int main(int argc, char const *argv[]){key_t key = ftok("/home/linux/work", 'K');if(-1 == key){perror("ftok error");exit(-1);}//创建共享内存int shmid = shmget(key,2*PIGE_SIZE,IPC_CREAT|0666);if(-1 == shmid){perror("shmget error");exit(-1);}//映射共享内存char *k_addr = shmat(shmid,NULL,0);if((void *)-1 == k_addr){perror("shmat error");exit(-1);}//创建信号灯集int semid = sem_init(2);//向共享内存中写入数据while(1){sem_wait_P(semid,0); //申请0号灯资源fgets(k_addr,128,stdin);k_addr[strlen(k_addr)-1] = '\0';sem_post_V(semid,1);  //释放1号灯资源if(!strncmp(k_addr,"exit",4)){break;}}//取消映射if(-1 == shmdt(k_addr)){perror("shmdt error");exit(-1);}//删除共享内存if(-1 == shmctl(shmid,IPC_RMID,NULL)){perror("shmctl error");exit(-1);}//销毁信号灯集sem_exit(semid);return 0;}
  • 读端:
	#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <sys/shm.h>#include <unistd.h>#include "sem.h"#include <errno.h>#define PIGE_SIZE 4*1024int main(int argc, char const *argv[]){key_t key = ftok("/home/linux/work", 'K');if(-1 == key){perror("ftok error");exit(-1);}//创建共享内存int shmid = shmget(key,2*PIGE_SIZE,IPC_CREAT|0666);if(-1 == shmid){perror("shmget error");exit(-1);}//映射共享内存char *k_addr = shmat(shmid,NULL,0);if((void *)-1 == k_addr){perror("shmat error");exit(-1);}//创建信号灯集int semid = sem_init(2);while(1){sem_wait_P(semid,1);printf("[%s]\n",k_addr);if(!strcmp(k_addr,"exit")){break;}sem_post_V(semid,0);}//取消映射if(-1 == shmdt(k_addr)){perror("shmdt error");exit(-1);}//删除共享内存if(-1 == shmctl(shmid,IPC_RMID,NULL)){perror("shmctl error");exit(-1);}//销毁信号灯集sem_exit(semid);return 0;}
  • 运行结果:
	linux@ubuntu:~/work/MSG/SME$ ./whellohiI Love Chinalinux@ubuntu:~/work/MSG/SME$ ./r[hello][hi][I Love China]