目录
一 信号量概念:
① 信号量又称为信号灯,不以传输数据为主要目的;
② 用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。
核心操作(semop):
• P(-1):如果有一个任务想要获得已经被占用的信号量时,信号量会将其放入一个等待队列然后让其睡眠。
• V(+1):当持有信号量的进程将信号释放后,处于等待队列中的一个任务讲被唤醒(因为队列中可能不止一个任务),并让其获得信号量。
举例:如果一个公共厕所有3个坑位,p1进来后就剩下2个坑位,当p2和p3进来后,此时剩下0个坑位。当p4在进来,坑位就为-1个,p5在进来,坑位就为-2个。那么坑位(信号量)就会执行P操作,把p4和p5放在等待队列中。当p1完事离开后,坑位(信号量)就执行V操作,将p4放入原p1的坑位,此时的坑位数就位-1,只有p5还在等待队列。以次内推,当p1p2p3p4p5全部完事后,坑位数量又重新为3。(新增一个进程,信号量+1;释放一个进程,信号量-1),这样就可以通过信号量有效的管理和分配资源。
二 相关函数
semget函数
功能:获得信号量的ID,由semid来接收
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
int key:信号量的键值;
int nsems:信号量的数量;
int semflg:标识/权限
返回值:成功返回信号量的ID;失败返回-1;
代码示例:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> int main() { key_t key = ftok("./semget.c",2);//获取key值 int semid = semget(key,1,0666|IPC_CREAT);//获取信号量ID if(semid == -1){ printf("semget error!\n"); } printf("semget success! semid=%d\n",semid); return 0; } 编译结果: dhw@dhw-virtual-machine:~/sem$ ./a.out semget success! semid=0
semctl函数
作用:信号量控制,由该函数中cmd参数所决定
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, union semun arg);
int semid:信号量ID
int semnum:信号量编号(要操作的那个信号)
int cmd: ①IPC_STAT(获取信号量的属性);②IPC_SET(设置信号量的属性);③IPC_RMID(删除信号量);④SETVAL(设置信号量的值)
arg: union semun {
int val; /* SETVAL的值 */④
struct semid_ds *buf; /* IPC_STAT, IPC_SET的缓冲区 */①②
unsigned short *array; /* 数组为GETALL, SETALL */
struct seminfo *__buf; /* IPC_INFO缓冲区(linux专用) */
};特别说明:要使用cmd中那个编号,就使用arg中对应编号的参数,不一定要全部使用。
返回值:成功返回0;失败返回-1;
semop函数
作用:对信号量进行P&V操作
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops);
int semid:信号量ID
struct sembuf *sops:信号量结构体数组
size_t nsops:要操作信号量的数量
struct sembuf{ unsigned short sem_num; /* 要操作信号量的编号*/ short sem_op; /* P/V操作,+1为V操作,释放资源;-1为P操作,分配资源;0为等 待,直到信号量的值变为0*/ short sem_flg; /* 0表示阻塞;IPC_NOWAIT表示非阻塞 */ };
返回值:成功返回0,失败返回-1
代码流程:semget() semctl() semop();
代码示例:父进程执行的是p操作-1,等待有空缺的资源;子进程执行的是v操作+1,执行子进程。所以下图代码执行结果是:先执行子进程,在执行父进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
union semun{
int val;
};
int main()
{
key_t key = ftok("./semget.c",2);//获取key值
int semid = semget(key,1,0666|IPC_CREAT);//获取信号量ID
union semun semun_union;
semun_union.val = 0;
semctl(semid,0,SETVAL,semun_union);
struct sembuf sem;
int pid = fork();
if(pid > 0){
sem.sem_num = 0;
sem.sem_op = -1; //p操作-1,等待资源,
sem.sem_flg = 0;
semop(semid,&sem,1);
printf("this is father!\n");
sem.sem_num = 0;
sem.sem_op = 1;//还原操作
sem.sem_flg = 0;
semop(semid,&sem,1);
}
if(pid == 0){
sem.sem_num = 0;
sem.sem_op = 1;//v操作+1,进程被唤醒
sem.sem_flg = 0;
semop(semid,&sem,1);
printf("this is child!\n");
}
return 0;
}