本质上是一个计数器,用于协调多个进程对共享数据对象的读/写。保证共享资源在一个时刻只有一个进程独享。可以用来保证两个或多个关键代码段不被并发调用。
临界资源:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。
用ipcs -s 可以查看系统的信号量。
用ipcrm sem + 信号量编号,可以手动删除信号量。
信号量实际上是一个计数器,最简单的信号量是取值0和1的二元信号量,这是信号量最常见的形式,1表示可以访问,0表示加锁。
//创建或获取一个信号量组,成功会返回信号量集 ID ,失败返回 -1
int semget(key_t key, int nsems, int semflg);
//对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);
//控制信号量的相关信息 (用于给信号量初始化)
int semctl(int semid, int semnum, int cmd, ...);
semget函数用来获取或创建信号量。
int semget(key_t key, int nsems, int semflg);
(1)该函数用来控制信号量(用于设置信号量的初始值和销毁信号量)
int semctl(int semid, int semnum, int cmd, ...);
IPC_RMID:销毁信号量,不需要第四个参数;
SETVAL:初始化信号量的值(信号量成功创建后,需要设置初始值),这个值由第四个参数决定。第四参数是一个自定义的共同体,如下:
union semun { int val; struct semid_ds *buf; unsigned short *arry; };
(2)初始化信号量的值为1,信号量可用。
union semun sem_union; sem_union.val = 1; semctl(semid,0,SETVAL,sem_union);
(3)销毁信号量。
semctl(semid,0,IPC_RMID);
该函数有两个功能:
int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf { short sem_num; // 信号量集的个数,单个信号量设置为0。 short sem_op; // 信号量在本次操作中需要改变的数据:-1-等待操作;1-发送操作。 short sem_flg; // 把此标志设置为SEM_UNDO,操作系统将跟踪这个信号量。 // 如果当前进程退出时没有释放信号量,操作系统将释放信号量,避免资源被死锁。 };
示例:
1)等待信号量的值变为1,如果等待成功,立即把信号量的值置为0;
struct sembuf p_sem;
p_sem.sem_num = 0;
p_sem.sem_op = -1;
p_sem.sem_flg = SEM_UNDO;
semop(sem_id, &p_sem, 1);
2)把信号量的值置为1。
struct sembuf v_sem;
v_sem.sem_num = 0;
v_sem.sem_op = 1;
v_sem.sem_flg = SEM_UNDO;
semop(sem_id, &v_sem, 1);
#include
#include
#include
#include
#include
#include
#include
#include
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
(Linux-specific) */
};
void p_sem(int semid)
{
struct sembuf p_sembuf;
p_sembuf.sem_num = 0;
p_sembuf.sem_op = -1;
p_sembuf.sem_flg = SEM_UNDO;
if(semop(semid,&p_sembuf,1) == -1){
perror("semop p error");
}
}
void v_sem(int semid)
{
struct sembuf v_sembuf;
v_sembuf.sem_num = 0;
v_sembuf.sem_op = 1;
v_sembuf.sem_flg = SEM_UNDO;
if(semop(semid,&v_sembuf,1) == -1){
perror("semop v error");
}
}
int main()
{
int semid;
key_t key;
pid_t pid;
int i;
key = ftok("./",1);
semid = semget(key,1,IPC_CREAT|0666);//获取创建信号量
union semun sem_union;
sem_union.val = 1;
if(semctl(semid,0,SETVAL,sem_union) == -1) perror("semctl error!");
if((pid = fork()) > 0){
sleep(1);
p_sem(semid);//等待锁并加锁
printf("in father process\n");
for(i=0;i<3;i++){
printf("father i=%d\n",i);
sleep(1);
}
v_sem(semid);//释放锁
semctl(semid,0,IPC_RMID);//销毁锁
}
if(pid == 0){
p_sem(semid);//等待锁并加锁
printf("in child process\n");
for(i=0;i<3;i++){
printf("child i=%d\n",i);
sleep(1);
}
v_sem(semid);//释放锁
}
if(pid < 0){
perror("fork error");
exit(-1);
}
return 0;
}