信号量
当我们编写的程序使用了线程时,不管它是运行在多用户系统上、多线程系统上,还是运行在多用户多进程系统上,我们通常会发现程序中存在着一部分临界代码,我们需要只有一个进程(或一个执行线程)可以进入这个临界代码并拥有对资源独占式的访问权。为了防止出现因多个程序同时访问一个共享资源而引发的问题,我们需要有一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。信号量的一个更正式的定义式是:它是一个特殊变量,只允许对它进行等待(wait)和发送信号(signal)这两种操作。
1. 临界资源
同一时刻只能被一个进程访问的资源
2. 临界区
访问临界资源代码区域
3. 原子操作
任何情况下都不能被打断的操作
4. 内核对象
用于对进程间通讯时,多进程能够访问同一资源的记录
信号量的作用:进程间同步控制,信号量相当于记录资源能够同时被多少个进程访问
信号量的操作:
创建或获取:如果是创建,必须初始化;如果获取,则不能初始化。
减一操作:P 操作
加一操作:V 操作
删除:
信号量操作的相关函数:
int semget((key_t)key, int nsems, int flag); //创建或者获取
第一个参数 key 是整数值,不相关的进程可以通过它访问同一个信号量。
nsems 参数指定需要的信号量数目,它几乎总是取值为1。
flag 参数是一组标志,它与 open 的函数标志非常相似。
int semop(int semid, struct sembuf *buf, int lenyh); //P V 操作,用于改变信号量的值
第一个参数 semid 是有semget返回的信号量标识符。
第二个参数 buf 是指向一个结构体数组的指针,每个数组元素至少包括以下几个成员:
struct sembuf
{
short sem_num; //信号量编号
short sem_op; //信号量在一次操作中需要改变的值,-1为 P 操作,+1为 V 操作
short sem_flg;
}
int semctl(int semid, int pos, int cmd, /*union semun un*/);
cmd 参数为将要采取的动作。
练习:A 进程输入“OK”,B 进程输出数字0~9。
代码如下:
#ifndef _SEM_H #define _SEM_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semid; union semun { int val; }; void sem_get(); void sem_p(); void sem_v(); void sem_del(); #endif
#include "sem.h" void sem_get() { semid = semget((key_t)1234, 1, 0664); if (semid == -1) { semid = semget((key_t)1234, 1, 0664 | IPC_CREAT); assert(semid != -1); union semun v; v.val = 0; if (semctl(semid, 0, SETVAL, v) == -1) { perror("error"); exit(0); } } } void sem_p() { struct sembuf buffer; buffer.sem_num = 0; buffer.sem_op = -1; buffer.sem_flg = SEM_UNDO; if (semop(semid, &buffer, 1) == -1) { perror("p error"); exit(0); } } void sem_v() { struct sembuf buffer; buffer.sem_num = 0; buffer.sem_op = 1; buffer.sem_flg = SEM_UNDO; if (semop(semid, &buffer, 1) == -1) { perror("v error"); exit(0); } } void sem_del() { if (semctl(semid, 0, IPC_RMID) == -1) { perror("del error"); exit(0); } }
进程 A 代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <fcntl.h> //#include <sys/ipc.h> //#include <sys/sem.h> #include "sem.h" #include "sem.c" int main() { sem_get(); while (1) { printf("Please input: "); fflush(stdout); char buffer[128] = {0}; fgets(buffer, 127, stdin); buffer[strlen(buffer)-1] = 0; if (strncmp(buffer, "OK", 2) == 0) { sem_v(); } if (strncmp(buffer, "end", 3) == 0) { break; } } sem_del(); return 0; }
B 进程代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <fcntl.h> //#include <sys/ipc.h> //#include <sys/sem.h> #include "sem.h" #include "sem.c" int main() { sem_get(); sem_p(); for (int i=0; i<10; i++) { printf("%d \n", i); } sem_del(); return 0; }