1 system V信号量
1.1 信号量基本定义
信号量:
- 互斥: P,V在同一个进程中
- 同步:P,V在不同进程中
信号量值的含义:
- S>0: S表示可用资源的个数
- S=0: 表示没有可用资源,无等待进程
- S<0: S绝对值表示等待队列种进程的个数
1.2 信号量PV操作
//linux系统信号量结构体的定义struct semaphore{int value;//s的个数pointer_PCB queue; //s等待队列,等待获取信号量}//P获取信号量的基本操作流程P(s){s.value -= 1;if (s.value < 0){//改进程状态设置为等待状态//讲改进程的PCB插入到相应的等待队列s.queue末尾}}//V归还信号量的基本操作流程V(s){s.value += 1;if (s.value >= 0){//唤醒等待队列中等待的一个进程//改变其状态为就绪态,将其插入就绪队列}}
1.3 信号量集函数
统一头文件:
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>
semget
函数定义: int semget(key_t key, int nsems, int semflg);
功能: 创建或打开一个信号量集
返回值: 成功返回信号量集的标识码,失败返回-1
参数:
- key: 信号量集名称
- nsems:信号量个数
-
semctl
函数定义:
int semctl(int semid, int semnum, int cmd,...);
功能: 修改或删除信号量集
返回值: 成功返回0,失败返回-1
参数: semid: 信号量集标识码
- semnum: 信号量的序号
- cmd: 要采取的操作

-
semop
函数定义:
int semop(int semid, struct sembuf* sops, unsigned nsops);
功能: 对信号量集进程PV操作
返回值: 成功返回0,失败返回-1
参数: sops:指向一个结构体的指针
struct sembuf{short sem_num; //信号量编号short sem_op;//信号量一次PV操作时加减的数值, +1,-1short sem_flg;//IPC_NOWAIT, SEM_UNDO};
nsops: 信号量的个数
1.4 代码示例
//创建信号量集int sem_create(key_t key){int semid;semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);if (semid == -1)ERR_EXIT("semget create failed");return semid;}//打开一个已经存在的信号量集int sem_open(key_t key){int semid;semid = semget(key, 0, 0);if (semid == -1)ERR_EXIT("get sem failed");return semid;}//设置当前计数值int sem_setval(int semid, int val){union semun s;s.val = val;int ret = semctl(semid, 0, SETVAL, s);if (ret == -1)ERR_EXIT("semctl setval failed");return 0;}//获取当前计数值int sem_getval(int semid){int ret = semctl(semid, 0, GETVAL, 0);if (ret == -1)ERR_EXIT("semctl getval failed");return ret;}//删除信号量集int sem_d(int semid){if (semctl(semid, 0, IPC_RMID, 0) == -1)ERR_EXIT("semctl delete failed");return 0;}//PV操作int sem_p(int semid){sembuf s = {0, -1, 0};//可以使用二维数组定义多个信号量的处理int ret = semop(semid, &s, 1);//这里1表示处理一个信号量if (ret == -1)ERR_EXIT("semop P failed");return ret;}int sem_v(int semid){sembuf s = {0, 1, 0};int ret = semop(semid, &s, 1);if (ret == -1)ERR_EXIT("semop V failed");return ret;}//PV操作,同时操作两个信号量int sem_p(int semid, int left, int right){sembuf s[2] = {{left, -1, 0},{right, -1, 0}};int ret = semop(semid, s, 2);if (ret == -1)ERR_EXIT("semop P failed");return ret;}int sem_v(int semid, int left, int right){sembuf s[2] = {{left, 1, 0},{right, 1, 0}};int ret = semop(semid, s, 2);if (ret == -1)ERR_EXIT("semop V failed");return ret;
2 POSIX信号量
头文件
#include <fcntl.h> /* For O_* constants */#include <sys/stat.h> /* For mode constants */#include <semaphore.h>
在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量。
无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。
它们的区别和管道及命名管道的区别类似,无名信号量则直接保存在内存中,而有名信号量要求创建一个文件。
2.1 有名信号量创建删除
- sem_open:创建信号量,会在/dev/shm中生成一个同名文件
- sem_close:关闭信号量
- sem_unlink:删除信号量
2.2 无名信号量创建删除
- sem_init:创建无名信号量
- sem_destroy:删除无名信号量
2.3 信号量PV操作
- sem_wait: P操作
- sem_post: V操作
2.4 代码示例
有名信号量示例
#include <stdio.h>#include <semaphore.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/wait.h>void printer(sem_t *sem, char *str){sem_wait(sem); //信号量减一while(*str!='\0'){putchar(*str);fflush(stdout);str++;sleep(1);}printf("\n");sem_post(sem); //信号量加一}int main(int argc, char *argv[]){pid_t pid;sem_t *sem = NULL;pid = fork(); //创建进程if (pid<0){ //出错perror("fork error");}else if(pid == 0){//子进程//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量sem = sem_open("name_sem", O_CREAT | O_RDWR, 0666, 1); //信号量值为 1if(sem == SEM_FAILED){//有名信号量创建失败perror("sem_open");return -1;}char str1[6] = "hello";printer(sem, str1); //打印sem_close(sem); //关闭有名信号量_exit(1);}else if(pid > 0){//父进程//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量sem = sem_open("name_sem", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1if(sem == SEM_FAILED){//有名信号量创建失败perror("sem_open");return -1;}char str2[6] = "world";printer(sem, str2); //打印sem_close(sem); //关闭有名信号量wait(NULL); //等待子进程结束}sem_unlink("name_sem");//删除有名信号量return 0;}
无名信号量示例
#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <semaphore.h>sem_t sem; //信号量void printer(char *str){sem_wait(&sem);//减一while(*str){putchar(*str);fflush(stdout);str++;sleep(1);}printf("\n");sem_post(&sem);//加一}void *thread_fun1(void *arg){char str1[6] = "hello";printer(str1);}void *thread_fun2(void *arg){char str2[6] = "world";printer(str2);}int main(void){pthread_t tid1, tid2;sem_init(&sem, 0, 1); //初始化无名信号量,初始值为 1//创建 2 个线程pthread_create(&tid1, NULL, thread_fun1, NULL);pthread_create(&tid2, NULL, thread_fun2, NULL);//等待线程结束,回收其资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);sem_destroy(&sem); //销毁信号量return 0;}
