0. 前言
进程是一个独立的资源管理单元,不同进程间的资源是独立的,不能在一个进程中访问另一个进程的用户空间和内存空间。但是,进程不是孤立的,不同进程之间需要信息的交互和状态的传递,因此需要进程间数据的传递、同步和异步的机制。
当然,这些机制不能由哪一个进程进行直接管理,只能由操作系统来完成其管理和维护,Linux提供了大量的进程间通信机制,包括同一个主机下的不同进程和网络主机间的进程通信,如下图所示:
同主机间的信息交互:
- 无名管道:特点:多用于亲缘关系进程间通信,方向为单向;为阻塞读写;通信进程双方退出后自动消失问题:多进程用同一管道通信容易造成交叉读写的问题
- 有名管道: FIFO(First In First Out),方向为单向(双向需两个FIFO),以磁盘文件的方式存在;通信双方一方不存在则阻塞
- 消息队列: 可用于同主机任意多进程的通信,但其可存放的数据有限,应用于少量的数据传递
- 共享内存: 可实现同主机任意进程间大量数据的通信,但多进程对共享内存的访问存在着竞争
- 同主机进程间同步机制:信号量(Semaphore)
- 同主机进程间异步机制:信号(Signal)
网络主机间数据交互:Socket(套接字)
1. 信号量的原理
所谓信号量,主要用来实现进程间同步,避免并发访问共享资源;同时,信号量也可以标记资源的个数。
1). 信号量集合 - semid_ds
/* come from /usr/include/linux/sem.h */struct semid_ds { struct ipc_perm sem_perm; /* permissions .. see ipc.h */ __kernel_time_t sem_otime; /* last semop time */ __kernel_time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ struct sem_queue *sem_pending; /* pending operations to be processed */ struct sem_queue **sem_pending_last; /* last pending operation */ struct sem_undo *undo; /* undo requests on this array */ unsigned short sem_nsems; /* no. of semaphores in array */};struct sem{ int semval; /*信号量的值*/ int sempid; /*最近一次操作的进程PID*/}
2). 信号量系统信息 - struct seminfo
/* come from /usr/include/linux/sem.h */struct seminfo { int semmap; /* Number of entries in semaphore map; unused within kernel */ int semmni; /* Maximum number of semaphore sets */ int semmns; /* Maximum number of semaphores in all semaphore sets */ int semmnu; /* System-wide maximum number of undo structures; unused within kernel */ int semmsl; /* Maximum number of semaphores in a set */ int semopm; /* Maximum number of operations for semop(2) */ int semume; /* Maximum number of undo entries per process; unused within kernel */ int semusz; /* Size of struct sem_undo */ int semvmx; /* Maximum semaphore value */ int semaem; /* Max. value that can be recorded for semaphore adjustment (SEM_UNDO) */};
3). 信号量设置的联合体 - union semun
针对信号量的不同操作,semctl
的第四个参数有所不同,为了方便起见,一般我们使用一个联合体对所有的可能性进行封装(自行封装,系统无此联合体)
union semun{ int value; struct semid_ds *buf; unsigned short *array;};
4). 函数semop
参数结构体 - sembuf
可通过该结构体实现信号量的 P(get)、V(release) 操作,具体看sem_op的值为正(v)还是负(P)
/* come from /usr/include/linux/sem.h *//* semop system calls takes an array of these. */struct sembuf { unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ 信号量操作:正数,增加信号量的值;负数,减少信号量的值 short sem_flg; /* operation flags */ 包括:1.IPC_NOWAIT: 阻塞立刻返回;2.SEM_UNDO: 进程退出后,该进程对sem的操作将会撤销};
2. 信号量的操作
1). 创建信号量集合 - semget
- 作用: 创建一个信号量的集合
头文件:
#include函数原型:
int semget(key_t key, int nsems, int semflg)参数:
- key: 有函数
ftok
产生的返回值- nsems: 创建的信号量的个数,以数组的形式存储
- semflg: 信号量集合的权限,最终值为 perm & ~umask
Macro No. Description IPC_CREAT 00001000 若key不存在,这创建 IPC_EXCL 00002000 若key存在,返回失败 IPC_NOWAIT 00004000 若需要等待,直接返回错误 返回值:
成功: 返回sem id 失败: -1
2). 控制信号量(集合) - semctl
- 作用: 对信号量集合或者对信号量集合中的某个或者某几个信号进程操作
头文件:
#include函数原型:
int semctl(int semid, int semnum, int cmd, ...)参数:
- semid : 由
semget
函数返回的semaphore id 号- semnum : 集合中,信号量处于信号量集合数组的下标
- cmd: 要执行的操作(以宏的形式出现)
Macro No. Description Return Argument 4 IPC_RMID 0 删除 以下皆针对整个信号量集合,/usr/include/linux/ipc.h IPC_SET 1 设置ipc_perm参数 struct semid_ds IPC_STAT 2 获取ipc_perm参数 struct semid_ds IPC_INFO 3 获取系统信息 struct seminfo GETPID 11 获取信号量拥有者的PID 以下皆针对某个信号量 /usr/include/linux/sem.h GETVAL 12 获取信号量的值,函数返回信号的值 当前信号量的值:进程数; 失败:-1 GETALL 13 获取所有信号量的值 成功:0; 失败:-1 数组地址 GETNCNT 14 获取等待信号量递增的进程数 成功:进程数; 失败:-1 GETZCNT 15 获取等待信号量值递减的进程数 成功:进程数; 失败:-1 int value (信号量的值) SETVAL 16 设置信号量的值,设置的值在第四个参数中 成功:0; 失败:-1 SETALL 17 设置所有信号量的值 成功:0; 失败:-1 数组地址 返回值:
成功: 0 失败:-1
3). 信号量操作 - semop
- 作用: 操作信号量的集合
头文件:
#include函数原型:
int semop(int semid, struct sembuf *sops, unsigned nsops)参数:
- semid: 信号量集合ID(由
semget
返回)- sops: struct sembuf结构体变量,见上面
- nsops: 进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
返回值:
成功: 0 失败:-1
3. 示例代码
生产者/消费者问题,经典PV问题,具体过程如下图所示:
本例代码分为两个文件:
- customer.c :
- producer.c :
- producer.c
/** Filename: producer.c*/#include#include #include #include #include #include #include #include int sem_id;int init(void){ key_t key; unsigned short int sem_array[2]; //for semaphore set, [0] for produce, [1] for space union semun //used for function semop() { int val; struct semid_ds *buf; unsigned short *array; }arg; key = ftok(".", 0xFF); if(-1 == key) { perror("ftok"); return -1; } sem_id = semget(key, 2, IPC_CREAT|0644); if(-1 == sem_id) { perror("semget"); return -1; } sem_array[0] = 0; sem_array[1] = 100; arg.array = sem_array; if(-1 == (semctl(sem_id, 0, SETALL, arg))) { perror("semctl"); return -1; } printf("produce init is %d\n", semctl(sem_id, 0, GETVAL)); printf("space init is %d\n", semctl(sem_id, 1, GETVAL)); return 0;}void del(){ semctl(sem_id, 0, IPC_RMID);}int main(){ struct sembuf sops[2]; //for function semop() sops[0].sem_num = 0; sops[0].sem_op = 1; //increase 1 sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = -1; //decrease 1 sops[1].sem_flg = 0; signal(SIGALRM, NULL); if(-1 == init()) { fprintf(stderr,"init error!\n"); exit(EXIT_FAILURE); } printf("This is producer:\n"); while(1) { alarm(0); //cancel the previous alarm alarm(10); //set the 10s , if timeout, the process printf("Before produce:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); printf("\n\nNow, producing...\n\n"); semop(sem_id, &sops[0], 1); semop(sem_id, &sops[1], 1); printf("After produce:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); sleep(2); } del(); return 0;}
- customer.c
/** Filename: producer.c*/#include#include #include #include #include #include #include int sem_id;int init(void){ key_t key; key = ftok(".", 0xFF); sem_id = semget(key, 2, IPC_CREAT|0644); if(-1 == sem_id) { perror("semget"); return -1; } return 0;}void del(){ semctl(sem_id, 0, IPC_RMID);}int main(){ struct sembuf sops[2]; //for function semop() sops[0].sem_num = 0; sops[0].sem_op = -1; //increase 1 sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = 1; //decrease 1 sops[1].sem_flg = 0; signal(SIGALRM, NULL); if(-1 == init()) { fprintf(stderr,"init error!\n"); exit(EXIT_FAILURE); } printf("This is customer:\n"); while(1) { alarm(0); //cancel the previous alarm alarm(10); //set the 10s , if timeout, the process printf("Before consume:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); printf("\n\nNow, consuming...\n\n"); semop(sem_id, sops, 2); printf("After consume:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); sleep(3); } del(); return 0;}