信号量
一、信号量的概念
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的结构体指针,结构体中有三个成员,如下所示:- struct sembuf
- {
- unsigned short sem_num;
- short sem_op;
- short sem_flg;
- };
复制代码 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- /***************************************************
- *
- * file name: semA.c
- * author : zzlyx1239.126.com
- * date : 2025/04/3
- * brief : 进程A,用于对共享内存中的数据进行修改
- * note : None
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***************************************************/
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/wait.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <time.h>
- #include <sys/ipc.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- struct sembuf
- {
- unsigned short sem_num;//信号量集中的信号量元素的下标
- short sem_op; //对选中的信号量的操作,常见的操作就是P/V操作
- short sem_flg; //成员指的是对选中的信号量的操作标志
- };
- // 定义信号量联合体
- union semun {
- int val; /* Value for SETVAL */
- struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
- unsigned short *array; /* Array for GETALL, SETALL */
- struct seminfo *__buf;
- };
- int main()
- {
- //0.打开信号量集,如果不存在则创建,如果存在则打开
- int sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == sem_id)
- {
- fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
- sem_id = semget(ftok(".",20),1,0644);
- }
- //1.打开共享内存段,如果不存在则创建,如果存在则打开
- int shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == shm_id)
- {
- fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
- shm_id = shmget(ftok(".",10),256,0644);
- }
- //2.把共享内存段连接到进程空间 shmat() 默认是可读可写 默认会初始化为0
- int *pshm = (int *)shmat(shm_id,NULL,0);
- if (pshm == (void *)-1)
- {
- fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
- return -1;
- }
- *pshm=0;
- union semun semn; //定义一个信号量联合体 变量
- semn.val = 1; //将信号量联合体中的val的值赋值为1
- semctl(sem_id,0,SETVAL,semn); //通过semctl函数接口将信号量的值设为1
- while(1);
- return 0;
- }
复制代码 进程B- /***************************************************
- *
- * file name: semB.c
- * author : zzlyx1239.126.com
- * date : 2025/04/3
- * brief : 进程B,用于对共享内存中的数据进行修改
- * note : None
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***************************************************/
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/wait.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <time.h>
- #include <sys/ipc.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- #include <stdbool.h>
- struct sembuf
- {
- unsigned short sem_num;//信号量集中的信号量元素的下标
- short sem_op; //对选中的信号量的操作,常见的操作就是P/V操作
- short sem_flg; //成员指的是对选中的信号量的操作标志
- };
- struct sembuf sem={0,1,0};
- int shm_id;
- int sem_id;
- char *pshm;
- /*********************************************************
- *
- * function name:p
- * brief :申请访问权限
- * author :zzlyx1239.126.com
- * parameter :
- * version :
- * note :
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***********************************************************/
- bool p(int sem_id)
- {
- sem.sem_op=-1;
- int a=semop(sem_id,&sem,0);
- if(a=-1)
- {
- fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
- return false;
- }
- return true;
- }
- /*********************************************************
- *
- * function name:v
- * brief :V操作
- * author :zzlyx1239.126.com
- * parameter :
- * version :
- * note :
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***********************************************************/
- bool v(int sem_id)
- {
- sem.sem_op=1;
- int a=semop(sem_id,&sem,0);
- if(a=-1)
- {
- fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
- return false;
- }
- return true;
- }
- int main()
- {
- //0.打开信号量集,如果不存在则创建,如果存在则打开
- sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == sem_id)
- {
- fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
- sem_id = semget(ftok(".",20),1,0644);
- }
- //1.打开共享内存段,如果不存在则创建,如果存在则打开
- shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == shm_id)
- {
- fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
- shm_id = shmget(ftok(".",10),256,0644);
- }
- //2.把共享内存段连接到进程空间 shmat() 默认是可读可写 默认会初始化为0
- pshm = (char *)shmat(shm_id,NULL,0);
- if (pshm == (void *)-1)
- {
- fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
- return -1;
- }
- //申请访问共享内存
- while(1)
- {
- //P操作,申请权限
- p(sem_id);
- //向共享内存中写入数据
- *pshm+=1;
- //V操作,释放
- v(sem_id);
- }
- shmdt(pshm);
- return 0;
- }
复制代码 进程C- /***************************************************
- *
- * file name: semCC.c
- * author : zzlyx1239.126.com
- * date : 2025/04/3
- * brief : 进程B,用于对共享内存中的数据进行修改
- * note : None
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***************************************************/
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/wait.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <time.h>
- #include <sys/ipc.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- #include <stdbool.h>
- struct sembuf
- {
- unsigned short sem_num;//信号量集中的信号量元素的下标
- short sem_op; //对选中的信号量的操作,常见的操作就是P/V操作
- short sem_flg; //成员指的是对选中的信号量的操作标志
- };
- struct sembuf sem={0,1,0};
- int shm_id;
- int sem_id;
- char *pshm;
- /*********************************************************
- *
- * function name:p
- * brief :申请访问权限
- * author :zzlyx1239.126.com
- * parameter :
- * version :
- * note :
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***********************************************************/
- bool p(int sem_id)
- {
- sem.sem_op=-1;
- int a=semop(sem_id,&sem,0);
- if(a=-1)
- {
- fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
- return false;
- }
- return true;
- }
- /*********************************************************
- *
- * function name:v
- * brief :V操作
- * author :zzlyx1239.126.com
- * parameter :
- * version :
- * note :
- *
- * CopyRight(c) 2025 zzlyx1239.126.com All Reserved
- ***********************************************************/
- bool v(int sem_id)
- {
- sem.sem_op=1;
- int a=semop(sem_id,&sem,0);
- if(a=-1)
- {
- fprintf(stderr, "semop error,errno:%d,%s\n",errno,strerror(errno));
- return false;
- }
- return true;
- }
- int main()
- {
- //0.打开信号量集,如果不存在则创建,如果存在则打开
- sem_id = semget(ftok(".",20),1,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == sem_id)
- {
- fprintf(stderr, "semget error,errno:%d,%s\n",errno,strerror(errno));
- sem_id = semget(ftok(".",20),1,0644);
- }
- //1.打开共享内存段,如果不存在则创建,如果存在则打开
- shm_id = shmget(ftok(".",10),256,IPC_CREAT|IPC_EXCL|0644);
- if (-1 == shm_id)
- {
- fprintf(stderr, "shmget error,errno:%d,%s\n",errno,strerror(errno));
- shm_id = shmget(ftok(".",10),256,0644);
- }
- //2.把共享内存段连接到进程空间 shmat() 默认是可读可写 默认会初始化为0
- pshm = (char *)shmat(shm_id,NULL,0);
- if (pshm == (void *)-1)
- {
- fprintf(stderr, "shmat error,errno:%d,%s\n",errno,strerror(errno));
- return -1;
- }
- //申请访问共享内存
- while(1)
- {
- //P操作,申请权限
- p(sem_id);
- //打印共享内存中的数据
- printf("share memory data=%d\n",*pshm);
- //V操作,释放
- v(sem_id);
- }
- shmdt(pshm);
- return 0;
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |