1.信号量:同步进程
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。
临界区:访问临界资源的代码端
临界资源:同一时刻只允许一个进程访问的资源
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。
p 减一,获取资源
v 加一,释放资源
最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。
控制对某个资源的访问
2.信号链函数
2.1semget函数
semget函数的作用是创建一个新信号量或取得一个已有信号量的键:
int semget(key_t key,int num_sems,int sem_flags);
key参数是整数值,不相关的进程可以通过它访问同一个信号量。只有semget函数才直接使用信号量键,所有其他的信号量函数都是使用由semget函数返回的信号量标识符。
IPC_PRIVATE是一个特殊的信号量键值,它的作用是创建一个只有创建者进程才可以访问的信号量,但这个键值很少有实际的用途。
num_sems参数指定需要的信号量数目。它几乎总是取值为1.
sem_flags参数是一组标志,它与open函数的标志非常相似。
semget函数在成功时返回一个正数(非零)值,它就是其他信号量函数将用到的信号量标识符。如果失败,则返回-1.
2.2semop函数
semop函数用于改变信号量改变信号链的值:
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);
sem_id参数是由semget返回的信号量标识。
sem_ops参数是指向一个结构的指针,每个数组元素至少包括以下几个成员:
struct sembuf{
short sem_num;
short sem_op;
short sem_flg;
}
sem_num成员是信号量编号,除非使用一组信号量,一般取值为0;sem_op成员的值是信号量在一次操作中需要改变的数值;sem_flg成员通常呗设置为SEM_UNDO,它将使得OS跟踪当前进程对这个信号量的修改情况。
semop调用的一切动作都是一次性完成的,这是为了避免出现因使用多个信号量而可能发生的竞争。
2.3semctl函数
semctl函数用来直接控制信号量信息:
int semctl(int sem_id,int sem_num,int command,…);
sem_id参数是由semget返回的信号量标识符;sem_num参数是信号量编号,当需要用到成组的信号量时,就要用到这个参数,它一般取值为0,表示这是第一个也是唯一一个信号量;command参数时将要采取的动作;如果还有第四个参数,它将会是一个union semun:
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}
3.进程 a 和进程b 模拟访问打印机,进程 a 输出第一个字符‘a’表示开始使用打印机,输出第二个字符‘a’表示结束使用,b 进程操作与a 进程相同。(由于打印机同一时刻只能被一个进程使用,所以输出结果不应该出现abab)
封装信号量的接口
sem.h:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/sem.h>
union semun
{
int val;
};
void sem_init();//创建/或者已存在的信号量
void sem_p();//p 减一
void sem_v();//v 加一
void sem_destroy();//销毁
sem.c:
#include "sem.h"
static int semid = -1;
void sem_init()//创建/或者已存在的信号量
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
if ( semid == -1 )//已存在
{
semid = semget((key_t)1234,1,0600);
}
else
{
union semun a;
a.val = 1;//信号量的初始值
if ( semctl(semid,0,SETVAL,a) == -1 )
{
perror("semctl error");
}
}
}
void sem_p()//p 减一
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;//p
buf.sem_flg = SEM_UNDO;
if ( semop(semid,&buf,1) == -1 )
{
perror("semop p error");
}
}
void sem_v()//v 加一
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//v
buf.sem_flg = SEM_UNDO;
if ( semop(semid,&buf,1) == -1 )
{
perror("semop v error");
}
}
void sem_destroy()//销毁
{
if ( semctl(semid,0,IPC_RMID) == -1 )
{
perror("semctl del error");
}
}
a.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
sem_init();
int i = 0;
for( ;i < 10; i++ )
{
sem_p();
printf("a");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("a");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}
b.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
sem_init();
int i = 0;
for( ;i < 10; i++ )
{
sem_p();
printf("b");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("b");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}