信号量
当我们编写的程序使用了线程时,不管它是运行在多用户系统上、多线程系统上,还是运行在多用户多进程系统上,我们通常会发现程序中存在着一部分临界代码,我们需要只有一个进程(或一个执行线程)可以进入这个临界代码并拥有对资源独占式的访问权。为了防止出现因多个程序同时访问一个共享资源而引发的问题,我们需要有一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。信号量的一个更正式的定义式是:它是一个特殊变量,只允许对它进行等待(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;
}