1.信号量
信号:是在软件层次上对中断机制的一种模拟,是一种异步通信方式。可以直接进行用户空间进程和内核进程之间的交互。它可以在任何时候发给某一进程,而无需知道该进程的状态。如果该进程未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它。
信号量:它是一个计数器,用于为多个进程提供对共享数据对象的访问。
为了获取共享资源,进程需要执行下列操作
1.测试控制该资源的信号量。
2.若信号量的值大于0,则进程可以使用该资源。在这种情况下,进程会将信号量的值减1,表示它使用了一个资源。
3.若信号量的值为0,则进程进入休眠,直至信号量的值大于0。进程被唤醒后,它返回至步骤1。
内核为每个信号量集合维护着一个semid_ds结构
struct semid_ds{
struct ipc_perm sem_perm;
unsigned short sem_nsems;
time_t sem_otime;
time_t sem_ctime;
...
}
2.semget
#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
第一个参数key:key是一个整数值,不相关的进程可以通过它访问同一个信号量。
第二个参数num_sems:指定需要的信号量数目。
第三个参数sem_flags:访问权限。
返回值: 成功:正数(非零)值; 失败:-1
作用:创建一个新的信号量或取一个已有信号量的键。当创建一个新的信号量时,要对semid_ds结构的下列成员赋初值。
初始化ipc_perm结构,该结构中的mode成员被设置为flag中的相应权限位。
sem_otime设置为0.
sem_ctime设置为当前时间。
sem_nsems设置为num_sems。
注意:如果是创建新的信号量(一般在服务器进程中),则必须指定num_sems。如果是引用现有信号量,则将num_sems指定为0。
3.semop
#include <sys/sem.h>
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
第一个参数sem_id:信号量标识符。
第二个参数sem_ops是指向一个结构数组的指针,每个数组元素至少包含以下几个成员:
struct sembuf{
short sem_num; //信号量编号,除非使用一组信号量,否则它的取值为0
short sem_op; //信号量在一次操作中需要改变的数值。通常用到两个值,-1,也就是p操作,它等待信号量变为可用;+1,也就是V操作,它发送信号表示信号量现在已可用。
short sem_flg; //通过被设置为SEM_UNDO
};
第三个参数num_sem_ops规定该数组中操作得数量。
4.semctl
#include <sys/shm.h>
int semctl(int sem_id, int sem_num, int command,...);
第一个参数sem_id:信号量标识符
第二个参数sem_num:信号量编号,当用到成组的信号量,就要用到这个参数。它一般取值为0,表示这是第一个也是唯一的一个信号量
第三个参数command:要采取的动作
有很多不同的值,有两个常用的SETVAL:用来把信号量初始化成一个已知的值,这个值通过union semun中的val成员设置。
IPC_RMID:用于删除一个已经无需使用的信号量标识符。
第四个参数:可选。是否使用取决于所请求的命令。如果使用该参数,则其类型是semun。
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
5.sembuf中sem_flg的设置问题
通常设置为SEM_UNDO,使操作系统跟踪信号量, 并在进程没有释放该信号量而终止时,操作系统释放信号量 ,例如在二元信号量中,你不释放该信号量 而异常退出,就会导致别的进程一直申请不到信号量,而一直处于挂起状态。
是否设置sem_flg为SEM_UNDO的区别
6.实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/sem.h>
//这个联合体需要手动定义
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 ErrExit(const char* reason)
{
fprintf(stderr, "%s: %d, %s\n", reason, errno, strerror(errno));
exit(1);
}
int initsem(int key = 0)
{
int semid = -1;
if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT)))
{
ErrExit("semget");
}
// 信号量初始值为1
union semun sem_un;
sem_un.val = 1;
if (-1 == semctl(semid, 0, SETVAL, sem_un))
{
ErrExit("semctl");
}
return semid;
}
void destroysem(int semid)
{
if (-1 == semctl(semid, 0, IPC_RMID))
{
ErrExit("semctl del");
}
}
// -1 为 p 操作
void P(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1))
{
ErrExit("semop p");
}
}
// 1 为 v 操作
void V(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = 1;
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1))
{
ErrExit("semop v");
}
}
int main(int argc, char const *argv[])
{
int semid = initsem();
pid_t pid = fork();
if (pid > 0)
{
P(semid);
printf("in parent process...\n");
sleep(1);
V(semid);
waitpid(pid, NULL, 0);
// 删除信号量集
destroysem(semid);
}
else if (0 == pid)
{
P(semid);
printf("in child process...\n");
sleep(1);
V(semid);
}
else
{
ErrExit("fork");
}
return 0;
}