找回密码
 立即注册
首页 业界区 安全 信号量

信号量

呼延冰枫 2025-6-10 11:46:15
信号量

一、信号量的概念

Linux系统提供了一种名称叫做信号量(Semaphore)的IPC资源,信号量的英文也可以翻译为信号灯,信号量本质上其实是一个数字(非负整数),用来表示一种资源的数量,当多个进程或者线程争夺这些稀缺资源的时候,可以使用信号量来保证他们合理地、秩序地使用这些资源。
Linux系统中一般把这些可能被进程或者线程共同访问资源称为临界资源(Critical Resources),而程序中访问这些临界资源的代码段被称为临界区(Critical Zone)。
把申请信号量的操作称为P(Passeren)操作,把归还信号量的操作称为V(vrijgeven)操作。
(1) P操作
P操作指的是请求分配资源,也就是说每完成一次P操作,则信号量资源会减一,当信号量资源为0时,如果有进程或者线程请求分配资源,则会导致阻塞,直到有进程释放信号量。
(2) V操作
V操作指的是请求释放资源,也就是说每完成一次V操作,则信号量资源会加一,使用完资源之后应该及时释放,否则其他进程申请资源时如果资源不够,则会导致阻塞。
注意:P操作和V操作属于原子操作,也就是进程在进行P操作或者V操作的过程中不会被打断,也就是不管资源申请成功还是申请失败都是一次性完成
二、创建信号量集

对信号量进行申请和释放的前提是必须创建信号量,Linux系统中提供了一个名称叫做semget()的函数,利用该函数可以创建或者打开信号量。
(1)函数参数
第一个参数指的是IPC对象的键值key,用户可以调用ftok()函数由系统生成一个key并把生成的key作为参数。
第二个参数指的是信号量集中的信号量元素数量,因为系统中的公共资源可能有多种,为了防止进程间对这些资源进行抢占,就需要为每一种资源设置一个信号量,所以信号量元素的数量可以理解为公共资源的种类。
第二个参数指的是创建或者打开信号量集的标志,用户可以选择使用标志IPC_CREAT或者IPC_EXCL,另外还可以指定信号量集的权限,权限采用八进制,比如0666,和open函数类似。
(2)返回值
函数调用成功,则会返回信号量集的标识符(一个非负整数),函数调用失败,则会返回-1。
三、操作信号量集

创建好一个信号量集之后,用户就可以对信号量集中的信号量资源进行PV操作,Linux系统提供了一个名字叫做semop()的函数,用户通过该函数可以实现对信号量进行申请或者释放。
(1) 函数参数
第一个参数指的是要操作的信号量集的标识符ID,该标识符可以通过semget()函数的返回值得到。
第二个参数指的是一个指向struct sembuf的结构体指针,结构体中有三个成员,如下所示:
  1. struct sembuf
  2. {
  3.         unsigned  short  sem_num;
  4.         short  sem_op;  
  5.         short  sem_flg;
  6. };
复制代码
sem_num成员指的是信号量集中的信号量元素的下标,通过可以访问信号量集中的某个信号量,就相当于可以访问某种公共资源。
sem_op成员指的是对选中的信号量的操作,常见的操作就是P/V操作,
​        ①如果该成员是正整数,则表示释放信号量,相当于完成V操作。
​        ②如果该成员是负整数则表示申请信号量,相当于完成P操作。
​        ③如果该成员的值为0时,就称为等零操作,即阻塞等待直到对应的信号量元素的值为零。
sem_flg成员指的是对选中的信号量的操作标志,常见的标志有IPC_NOWAIT和SEM_UNDO。
第三个参数指的是要完成P/V操作的结构体数组的元素个数,利用该参数可以指定要操作多少个信号量。
注意:进行P操作时,如果信号量的值小于要申请的数量,则会导致进程阻塞,进行V操作时永远不会阻塞。
(2) 返回结果
函数调用成功,则返回0,函数调用失败,则返回-1,并会设置对应的错误码,用户可以根据错误码检查错误。
四、控制信号量集

Linux系统内核需要对创建成功的信号量集进行维护,所以会记录每个信号量集的属性和信息,Linux系统提供了一个名称叫做semctl()的函数接口,用户可以调用该函数接口来实现设置信号量集的属性、获取信号量集的属性、删除信号量集等操作。
五、练习

设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。
进程A
  1. /***************************************************
  2. *
  3. *  file name:        semA.c
  4. *        author         :  zzlyx1239.126.com
  5. *        date         :  2025/04/3
  6. *        brief    :  进程A,用于对共享内存中的数据进行修改
  7. *         note         :  None
  8. *
  9. *  CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  10. ***************************************************/
  11. #include <unistd.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <fcntl.h>
  15. #include <sys/wait.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <time.h>
  21. #include <sys/ipc.h>
  22. #include <sys/stat.h>
  23. #include <unistd.h>
  24. #include <sys/ipc.h>
  25. #include <sys/shm.h>
  26. #include <sys/sem.h>
  27. struct sembuf
  28. {
  29.         unsigned  short  sem_num;//信号量集中的信号量元素的下标
  30.         short  sem_op;  //对选中的信号量的操作,常见的操作就是P/V操作
  31.         short  sem_flg; //成员指的是对选中的信号量的操作标志
  32. };
  33. // 定义信号量联合体
  34. union semun {
  35.     int              val;    /* Value for SETVAL */
  36.     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
  37.     unsigned short  *array;  /* Array for GETALL, SETALL */
  38.     struct seminfo  *__buf;  
  39. };
  40. int main()
  41. {
  42.         //0.打开信号量集,如果不存在则创建,如果存在则打开
  43.         int sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
  44.         if (-1 == sem_id)
  45.         {
  46.                 fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
  47.                 sem_id = semget(ftok(".",20),1,0644);
  48.         }
  49.     //1.打开共享内存段,如果不存在则创建,如果存在则打开
  50.         int shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
  51.         if (-1 == shm_id)
  52.         {
  53.                 fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
  54.                 shm_id = shmget(ftok(".",10),256,0644);
  55.         }
  56.     //2.把共享内存段连接到进程空间  shmat()  默认是可读可写  默认会初始化为0
  57.         int *pshm = (int *)shmat(shm_id,NULL,0);
  58.         if (pshm == (void *)-1)
  59.         {
  60.                 fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
  61.                 return -1;
  62.         }
  63.         *pshm=0;
  64.         union semun semn;    //定义一个信号量联合体 变量
  65.     semn.val = 1;       //将信号量联合体中的val的值赋值为1
  66.     semctl(sem_id,0,SETVAL,semn);        //通过semctl函数接口将信号量的值设为1
  67.         while(1);
  68.         return 0;
  69. }
复制代码
进程B
  1. /***************************************************
  2. *
  3. *  file name:        semB.c
  4. *        author         :  zzlyx1239.126.com
  5. *        date         :  2025/04/3
  6. *        brief    :  进程B,用于对共享内存中的数据进行修改
  7. *         note         :  None
  8. *
  9. *  CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  10. ***************************************************/
  11. #include <unistd.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <fcntl.h>
  15. #include <sys/wait.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <time.h>
  21. #include <sys/ipc.h>
  22. #include <sys/stat.h>
  23. #include <unistd.h>
  24. #include <sys/ipc.h>
  25. #include <sys/shm.h>
  26. #include <sys/sem.h>
  27. #include <stdbool.h>
  28. struct sembuf
  29. {
  30.         unsigned  short  sem_num;//信号量集中的信号量元素的下标
  31.         short  sem_op;  //对选中的信号量的操作,常见的操作就是P/V操作
  32.         short  sem_flg; //成员指的是对选中的信号量的操作标志
  33. };
  34. struct sembuf sem={0,1,0};
  35. int shm_id;
  36. int sem_id;
  37. char *pshm;
  38. /*********************************************************
  39. *
  40. *  function name:p
  41. *         brief                 :申请访问权限
  42. *         author                 :zzlyx1239.126.com
  43. *         parameter         :
  44. *         version                 :
  45. *         note                 :       
  46. *
  47. *          CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  48. ***********************************************************/
  49. bool p(int sem_id)
  50. {
  51.         sem.sem_op=-1;
  52.         int a=semop(sem_id,&sem,0);
  53.         if(a=-1)
  54.         {
  55.                 fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
  56.                 return false;
  57.         }
  58.         return true;
  59. }
  60. /*********************************************************
  61. *
  62. *  function name:v
  63. *         brief                 :V操作
  64. *         author                 :zzlyx1239.126.com
  65. *         parameter         :
  66. *         version                 :
  67. *         note                 :       
  68. *
  69. *          CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  70. ***********************************************************/
  71. bool v(int sem_id)
  72. {
  73.         sem.sem_op=1;
  74.         int a=semop(sem_id,&sem,0);
  75.         if(a=-1)
  76.         {
  77.                 fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
  78.                 return false;
  79.         }
  80.         return true;
  81. }
  82. int main()
  83. {
  84.         //0.打开信号量集,如果不存在则创建,如果存在则打开
  85.         sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
  86.         if (-1 == sem_id)
  87.         {
  88.                 fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
  89.                 sem_id = semget(ftok(".",20),1,0644);
  90.         }
  91.     //1.打开共享内存段,如果不存在则创建,如果存在则打开
  92.         shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
  93.         if (-1 == shm_id)
  94.         {
  95.                 fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
  96.                 shm_id = shmget(ftok(".",10),256,0644);
  97.         }
  98.     //2.把共享内存段连接到进程空间  shmat()  默认是可读可写  默认会初始化为0
  99.         pshm = (char *)shmat(shm_id,NULL,0);
  100.         if (pshm == (void *)-1)
  101.         {
  102.                 fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
  103.                 return -1;
  104.         }
  105.         //申请访问共享内存
  106.         while(1)
  107.         {
  108.                 //P操作,申请权限
  109.                 p(sem_id);
  110.                 //向共享内存中写入数据  
  111.                 *pshm+=1;
  112.                 //V操作,释放
  113.                 v(sem_id);
  114.         }
  115.         shmdt(pshm);
  116.     return 0;
  117. }
复制代码
进程C
  1. /***************************************************
  2. *
  3. *  file name:        semCC.c
  4. *        author         :  zzlyx1239.126.com
  5. *        date         :  2025/04/3
  6. *        brief    :  进程B,用于对共享内存中的数据进行修改
  7. *         note         :  None
  8. *
  9. *  CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  10. ***************************************************/
  11. #include <unistd.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <fcntl.h>
  15. #include <sys/wait.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <time.h>
  21. #include <sys/ipc.h>
  22. #include <sys/stat.h>
  23. #include <unistd.h>
  24. #include <sys/ipc.h>
  25. #include <sys/shm.h>
  26. #include <sys/sem.h>
  27. #include <stdbool.h>
  28. struct sembuf
  29. {
  30.         unsigned  short  sem_num;//信号量集中的信号量元素的下标
  31.         short  sem_op;  //对选中的信号量的操作,常见的操作就是P/V操作
  32.         short  sem_flg; //成员指的是对选中的信号量的操作标志
  33. };
  34. struct sembuf sem={0,1,0};
  35. int shm_id;
  36. int sem_id;
  37. char *pshm;
  38. /*********************************************************
  39. *
  40. *  function name:p
  41. *         brief                 :申请访问权限
  42. *         author                 :zzlyx1239.126.com
  43. *         parameter         :
  44. *         version                 :
  45. *         note                 :       
  46. *
  47. *          CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  48. ***********************************************************/
  49. bool p(int sem_id)
  50. {
  51.    sem.sem_op=-1;
  52.    int a=semop(sem_id,&sem,0);
  53.    if(a=-1)
  54.    {
  55.            fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
  56.            return false;
  57.    }
  58.    return true;
  59. }
  60. /*********************************************************
  61. *
  62. *  function name:v
  63. *         brief                 :V操作
  64. *         author                 :zzlyx1239.126.com
  65. *         parameter         :
  66. *         version                 :
  67. *         note                 :       
  68. *
  69. *          CopyRight(c) 2025 zzlyx1239.126.com All Reserved
  70. ***********************************************************/
  71. bool v(int sem_id)
  72. {
  73.    sem.sem_op=1;
  74.    int a=semop(sem_id,&sem,0);
  75.    if(a=-1)
  76.    {
  77.            fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
  78.            return false;
  79.    }
  80.    return true;
  81. }
  82. int main()
  83. {
  84.         //0.打开信号量集,如果不存在则创建,如果存在则打开
  85.         sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
  86.         if (-1 == sem_id)
  87.         {
  88.                 fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
  89.                 sem_id = semget(ftok(".",20),1,0644);
  90.         }
  91.     //1.打开共享内存段,如果不存在则创建,如果存在则打开
  92.         shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
  93.         if (-1 == shm_id)
  94.         {
  95.                 fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
  96.                 shm_id = shmget(ftok(".",10),256,0644);
  97.         }
  98.     //2.把共享内存段连接到进程空间  shmat()  默认是可读可写  默认会初始化为0
  99.         pshm = (char *)shmat(shm_id,NULL,0);
  100.         if (pshm == (void *)-1)
  101.         {
  102.                 fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
  103.                 return -1;
  104.         }
  105.         //申请访问共享内存
  106.         while(1)
  107.         {
  108.                 //P操作,申请权限
  109.                 p(sem_id);
  110.                 //打印共享内存中的数据  
  111.                 printf("share memory data=%d\n",*pshm);
  112.                 //V操作,释放
  113.                 v(sem_id);
  114.         }
  115.         shmdt(pshm);
  116.     return 0;
  117. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册