【Linux】进程间通信——信号量
2018-03-26 23:12
316 查看
1. 信号量的基本概念
信号量主要用于同步与互斥的,先简单的说几个概念吧原子性:表示一个事件的两种状态,要么做了,要么没有做,没有第三种状态;
同步:在多道程序环境下,进程是并发执行的,不同进程之间存在着不同相互制约的关系;
互斥:在一段时间内,资源只允许被一个进程访问;
临界资源:像打印机这类一次只允许一个进程使用的资源;
临界区:多个进程访问临界资源的那一段代码。
那么什么是信号量呢,我们可以简单的理解成计数器(当然实际上并没有那么简单)描述资源的多少,它本身不具备数据交换的功能,而是通过控制临界资源来实现进程间通信。
简单说下信号量的工作机制,我么说信号量可以理解为一个计数器,它有一个初值(>0),每当有进程申请使用信号量时,通过P操作来对信号量进行-1;当计数器减到0的时候,其他进程想要访问资源,就需要挂起等待,直到该进程执行完操作,通过V操作对信号量+1释放资源。所以,我们说信号量也是进程间通信的一种方式,比如互斥锁的简单实现就是二元信号量,一个进程在使用互斥锁时,通知其他进程,阻止他们的访问挂起等。举一个例子:假如我们一趟火车只有一张火车票了,但是有两个人都要买,其中一个人先申请到了资源,买到了票,那么另一人只能等,等到如果有人退票或者改签,他就可以买到票了。
2.信号量的操作函数
信号量的操作函数与消息队列也是十分类似的:semget
功能:用来创建和访问一个信号
原型:
int semget(key_t key, int nsems, int semflg);
参数:
key:信号集的名字,与消息队列类似,不在详细的说了
nsems:信号集中信号量的个数,我们使用semget这个函数创建的是一个信号集,包括了好多信号量的, 当然可以选择只创建一个
semflg: 两个参数IPC_CREAT和IPC_EXCL,与之前的消息队列也类似-
返回值:成功返回⼀一个⾮非负整数,即该信号集的标识码;失败返回-1
semctl
功能:⽤用于控制信号量集
原型:
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号,序号从0开始
cmd:将要采取的动作(有三个可取值)
最后⼀一个参数根据命令不同⽽而不同
返回值:成功返回0;失败返回-1
这个函数,我们初始化和删除都会用到它,当第三个参数为IPC_RMID时,用于删除信号集;当第三个参数设置为SETVAL时,可以用于信号量的初始化,但此时就需要第四个参数了;第四个参数是这样的,它需要加入一个联合体:
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; /* Buffer for IPC_INFO */ }
semop
功能:⽤来实现PV操作的,这个函数还用得到了一个结构体
原型:
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数:
semid:是该信号量的标识码,semget函数的返回值
sops:指向一个结构体的指针
nsops:信号量的个数
返回值:成功返回0;失败返回-1
这个结构体是这样的:
struct sembuf { short sem_num;//sem_num是信号量的编号 short sem_op;//sem_op是信号量⼀次PV操作时加减的数值,一般只会⽤用到两个值:一个是“-1”,也就是P操作,等待信号量变得可⽤;另⼀个是“+1”,也就是我们的V操作,发出信号量已经变得可⽤ short sem_flg;//sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO };
同消息队列类似,这里也可以使用ipcs -s查看IPC资源,使用ipcrm -s删除IPC资源。
这里需要注意的是:信号量与信号并不一样,有的同学可能会把这两个概念搞混了,信号量是用来操作系统进程间同步访问共享资源。信号是用来通知进程发生了异步事件,虽然二者名字上很相似,但本质上相差很大。
3.实例
下面我们就用一段很经典的代码使用这些函数:先写一个Makefile
sem_test:sem_test.c comm.c gcc -o $@ $^ .PHONY:clean clean: rm -f sem_test
comm.h
#ifndef _COMM_H_ #define _COMM_H_ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> 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; /* Buffer for IPC_INFO*/ }; int createSemSet(int nums); int initSem(int semid, int nums, int initval); int getSemSet(int nums); int P(int semid, int who); int V(int semid, int who); int destorySemSet(int semid); #endif
comm.c
#include "comm.h" static int commSemSet(int nums, int flags) { key_t key = ftok("/tmp", 0x6666); if(key < 0) { perror("ftok"); return -1; } int semid = semget(key, nums, flags); if(semid < 0) { perror("semget"); return -2; } return semid; } int createSemSet(int nums) { return commSemSet(nums, IPC_CREAT|IPC_EXCL|0666); } int getSemSet(int nums) { return commSemSet(nums, IPC_CREAT); } int initSemS c22b et(int semid, int nums, int initval) { union semun _un; _un.val = initval; if(semctl(semid, nums, SETVAL, _un) < 0) { perror("semctl"); return -1; } return 0; } static int commPV(int semid, int who, int op) { struct sembuf _sf; _sf.sem_num = who; _sf.sem_op = op; _sf.sem_flg = 0; if(semop(semid, &_sf, 1) < 0) { perror("semop"); return -1; } return 0; } int P(int semid, int who) { return commPV(semid, who, -1); } int V(int semid, int who) { return commPV(semid, who, 1); } int destorySemSet(int semid) { if(semctl(semid, 0, IPC_RMID) < 0) { perror("semctl"); return -1; } return 0; }
test.c
#include "comm.h" int main() { int semid = createSemSet(1); initSemSet(semid, 0, 1); pid_t pid = fork(); if(pid == 0) { //child int _semid = getSemSet(0); while(1) { P(_semid, 0); printf("A"); fflush(stdout); usleep(123456); printf("A "); fflush(stdout); usleep(123456); V(_semid, 0); } } else { //parent while(1) { P(semid, 0); printf("B"); fflush(stdout); usleep(123456); printf("B "); fflush(stdout); usleep(123456); V(semid, 0); } wait(NULL); } destorySemSet(semid); return 0; }
看上面的测试程序,使用fork()创建了两个进程,但是两个进程交替执行,所以如果没有使用PV操作的话,打印出来的是酱紫的:
但是如果我们加了PV操作,这样就可以保证A和B是成都存在的了
相关文章推荐
- Linux进程间通信--共享内存与信号量
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信——使用信号量
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信之信号量
- linux进程间通信------使用信号量
- Linux进程间通信——使用信号量【转】
- Linux进程间通信 共享内存+信号量+简单例子
- Linux进程间通信——使用信号量
- Linux进程间通信——使用信号量
- Linux进程间通信——使用信号量
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- Linux进程间通信(五):信号量 semget()、semop()、semctl()
- Linux进程间通信—信号量
- Linux进程间通信之信号量
- 初学Linux--进程间通信(共享内存、信号量)
- Linux进程间通信(IPC)之三——信号量(Semaphore)
- Linux进程间通信--信号,管道,消息队列,信号量,共享内存,socket
- Linux--进程间通信(信号量,共享内存)(转)