信号量
信号量一、信号量的概念
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
{
unsignedshortsem_num;
shortsem_op;
shortsem_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
{
unsignedshortsem_num;//信号量集中的信号量元素的下标
shortsem_op;//对选中的信号量的操作,常见的操作就是P/V操作
shortsem_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
{
unsignedshortsem_num;//信号量集中的信号量元素的下标
shortsem_op;//对选中的信号量的操作,常见的操作就是P/V操作
shortsem_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
{
unsignedshortsem_num;//信号量集中的信号量元素的下标
shortsem_op;//对选中的信号量的操作,常见的操作就是P/V操作
shortsem_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;
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]