进程间通信—System V-信号量

进程间通信——信号量(信号灯)

  

  信号与信号量

  信号:是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常,是一种处理异步事件的方式。

  信号量:是一个特殊的变量,本质是计数器,记录了临界资源的数量。进程对其访问都是原子操作(PV操作),用于多线程、多进程之间同步临界资源。

  信号量分类

  按实现方式,信号量可以分为POSIX信号量与System V信号量。(POSIX与System V使用区别)

  System V信号量是基于内核维护的,,通常用于Linux系统中。Posix是由文件系统中的路径名对应的名字来标识的。

  在多线程中使用的基本是POSIX标准提供的接口函数,而多进程则是基于System V

多线程使用System V接口---不建议。线程相对于进程是轻量级的,例如调度的策略开销,如果使用System V这种每次调用都会陷入内核的接口,会丧失线程的轻量优势。所以,多线程之间的通信不使用System V的接口函数。


       多进程使用POSIX也是允许的

     以mutex为例 ,POSIX的mutex如果要用于多进程,需要实现如下两点要求:(对于SEM信号量相对简单,因为提供了有名SEM的能够用于多进程,它是内核持续的,详见http://blog.csdn.net/firstlai/article/details/50706243)
为何不建议System V信号量-多线程

  基于内存的信号量,同步多线程时,可放到该多线程所属进程空间里;如果是同步多进程,那就要把信号量放入到共享内存中(方便多个进程访问)。

     System V 信号量使用步骤

1 打开/创建信号量   semget
2 信号量初始化     semctl
3 P/V操作         semop
4 删除信号量       semctl

  1、信号量创建/打开---semget

1 #include <sys/ipc.h>
2 #include <sys/sem.h>
3 
4 int semget(key_t key, int nsems, int semflg);
5 //成功返回信号量id,失败返回-1
6 //参数
7 //key---和消息队列关联的key, IPC_PRIVATE或ftok
8 //nsems---集合中包含计数信号量个数
9 //semflg---标志位, IPC_CREAT|0666  IPC_EXCL

  2、信号量初始化---semctl

 1 #include <sys/ipc.h>
 2 #include <sys/sem.h>
 3 
 4 int semctl(int semid, int semnum, int cmd, ...);
 5 
 6 //成功返回0,失败返回EOF
 7 //semid---要操作的信号量集的id
 8 //semnum---要操作的集合中的信号量编号
 9 //cmd---执行的操作  SETVAL  IPC_RMID
10 //union semun---联合体,取决于cmd

  union semun

1 union semun {
2      int               val;   //SETVAL的值 
3      struct semid_ds  *buf;   //Buffer for IPC_STAT, IPC_SET      
4      unsigned short *array;   //Array for GETALL, SETALL
5      struct seminfo   *_buf;  //Buffer for IPC_INFO
6 }

  3、信号量P/V操作---semop

 1 #include <sys/ipc.h>
 2 #include <sys/sem.h>
 3 
 4 int semop(int semid, struct sembuf *sops, unsigned nsops);
 5 
 6 //成功返回0,失败EOF
 7 //参数
 8 //semid---要操作的信号量集的id
 9 //sops ---描述对信号量操作的结构体(数组)
10 //nsops---要操作的信号量的个数
11 
12 struct sembuf{
13      short sem_num;    //信号量编号
14      short  sem_op;    //-1:P操作,1:V操作
15      short  sem_flg;    //SEM_UNDO即0 ,IPC_NOWAIT
16 }
  通常为SEM_UNDO即0,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量

示例:信号量集/共享内存

要求:父子进程通过System V信号量同步对共享内存的读写

   父进程从键盘输入字符串

   子进程输出字符串

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <sys/types.h>
  4 #include <sys/ipc.h>
  5 #include <errno.h>
  6 #include <sys/sem.h>
  7 #include <string.h>
  8 
  9 union semun{
 10     int val;
 11 }
 12 
 13 #define SEM_READ 0
 14 #define SEM_WRITE 0
 15 
 16 //实现P操作
 17 poperation(int index, int semid) //传入要操作的信号量,信号量集
 18 {
 19     //初始化sembuf结构体
 20     struct sembuf sop;
 21     sop.sem_num = index; //信号量编号
 22     sop.sem_op  = -1;    //P操作:-1
 23     sop.sem_flg =  0;    //SEM_UNDO
 24 
 25     //信号量集ID,对信号量操作的结构体,操作的信号量个数
 26     semop(semid,&sop,1);
 27 }
 28 
 29 voperation(int index, int semid)
 30 {
 31     //初始化sembuf结构体
 32     struct sembuf sop;
 33     sop.sem_num = index; //信号量编号
 34     sop.sem_op  =  1;    //V操作:1
 35     sop.sem_flg =  0;    //SEM_UNDO
 36 
 37     //信号量集ID,对信号量操作的结构体,操作的信号量个数
 38     semop(semid,&sop,1);
 39 
 40 }
 41 
 42 int main()
 43 {
 44     key_t key;
 45     int semid; //信号量集ID
 46     int shmid; //共享内存ID
 47     char * shmaddr;
 48     pid_t pid;
 49 
 50     key = ftok(".",123); //创建key键值,进而生成不同的IPC对象的标识符ID
 51 
 52     //创建2个信号量
 53     semid = semget(key, 2, IPC_CREAT|0777);
 54     if(semid < 0)
 55     {
 56         perror("semid");
 57         return -1;
 58     }
 59 
 60     //创建共享内存
 61     shmid = shmget(key, 256, IPC_CREAT|0777);
 62     if(shmid < 0)
 63     {
 64         perror("shmget");
 65         return -1;
 66     }
 67 
 68     //初始化2个信号量
 69     union semun myun;
 70     myun.val = 0; //初始化读信号量的值为0,未读
 71     semctl(semid, SEM_READ, SETVAL, myun);
 72 
 73     myun.val = 1; //初始化写信号量的值为1,可写
 74     semctl(semid, SEM_WRITE,SETVAL, myun);
 75 
 76     //创建一个子进程
 77     pid = fork();
 78     if(pid < 0)
 79     {
 80         perror("fork");
 81         return -1;
 82     }
 83     else if(pid == 0) //子进程
 84     {
 85         //获取映射后的共享内存的地址
 86         shmaddr = (char *)shmat(shmid, NULL, 0);
 87         
 88         poperation(SEM_READ,semid); //读之前对读信号量进行P操作
 89 
 90         printf("getshm:%s",shmaddr);
 91 
 92         voperation(SEM_WRITE,semid); //V操作,读完了,告诉父进程可写
 93 
 94     }else{ //父进程从键盘输入字符
 95 
 96         //获取映射后的共享内存的地址
 97         shmaddr = (char *)shmat(shmid, NULL, 0);
 98         
 99         poperation(SEM_WRITE,semid); //写之前对写信号量进行P操作
100 
101         printf("Please input char to shm:");
102         fgets(shmaddr, 32,stdin);
103 
104         voperation(SEM_READ,semid); //V操作,写完,告诉子进程可以读
105     }
106 
107 
108 }

结果:

【信号量的意图在于进程间同步,互斥锁和条件变量的意图则在于线程间同步。但是信号量也可用于线程间,互斥锁和条件变量也可用于进程间。我们应该使用适合具体应用的那组原语。】--------https://www.cnblogs.com/fangshenghui/p/4039946.html

原文地址:https://www.cnblogs.com/y4247464/p/12115743.html