1.1 信号量
SystemV的信号量使用方法
1.1.1 数据结构
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1.
struct semid_ds{ /* 通道结构 */
struct ipc_perm sem_perm;
struct sem *sem_base; /* 指向信号量数组,系统内部使用,用户不能直接访问*/
ushort sem_nsems; /* 信号量个数 */
time_t sem_otime; /* 最后semop时间 */
time_t sem_ctime; /* 最后修改时间 */
}
2.
struct sem { /* 信号量的成分 */
ushort semval; /*非负值*/
ushort sempid; /*最后semop的pid*/
ushort semncnt; /*等待semval增加的进程数*/
ushort semzcnt; /*等待semval=0的进程数*/
}
3.
semctl函数需要的联合体:
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
int val; /* 用于 SETVAL 命令*/
struct semid_ds *buf; /* 用于 IPC_STAT, IPC_SET */
unsigned short *array; /* 用于 GETALL, SETALL 命令,存放所获得的或要设置的信号量集合中所有信号量的值*/
/* Linux specific part: */
struct seminfo *__buf; /* 用于 IPC_INFO */
};
#endif
4.
struct sembuf { /* 操作数数组成员结构,用于semop函数*/
ushort sem_num; /* 信号量编号 */
short sem_op; /* 操作数 */
short sem_flag; /* 操作标志 */
}
1.1.2 API
4.1 #include <semaphore.h> |
信号量集合初始化,建立信号量集合标识及相连的数据结构 |
int semget(key_t key, int nsems, int semflg); |
返回:成功返回信号量集合id,错误返回-1并设置errno。 |
第一个参数key是IPC关键字。它有特殊值IPC_PRIVATE表示总是创建一个私有信号量集合。
第二个参数指明信号量集合中信号量的个数。nsems必须大于0小于系统在头文件<sys/sem.h>中定义的信号量最大值宏SEMMSL。当信号量集合已经存在时,此参数必须合法,即为大于0小于该信号量集合的个数,或简单设为0。信号量的编号,第一个是0最后一个是nsems-1。
当key值不等于IPC_PRIVATE时,semget动作取决于最后一个参数semflag标志:
IPC_CREAT 单独设置此标志,当系统中不存在相同key时,创建一个新的信号量集合,否则返回已存在的信号量集合标识。
IPC_EXCL 单独设置不起作用。与 IPC_CREAT同时设置时,当系统中存在相同key时,错误返回。保证不会打开一个已存在的信号量集合。
此函数只能创建一个信号量集合,集合中的每个信号量没有初始化,这要用4.2函数。
可重用包装函数:
//打开或创建信号量集合,权限参数必须是型如"066"的字符串
int open_semaphore_set(key_t key_val, int num_sems, char* mode_str) {
int sid;
int mode;
if (mode_str != NULL) {
sscanf(mode_str, "%o", &mode);
} else {
mode = 0660;
}
if(!num_sems) {
return (-1);
}
if ((sid = semget(key_val, num_sems, mode | IPC_CREAT)) == -1)
return (-1);
else
return sid;
}
4.2 #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
信号量控制函数 |
int semctl(int semid, int semnum, int cmd, ...); |
返回:失败-1, 成功对于命令GETVAL,GETPID,GETNCNT,GETZCNT返回相应值,其他命令返回0 |
第1个参数semid必须是一个合法的信号量集合标识。
第2个参数semnum选择集合中一个特定的信号量,
第3个参数给出操作命令,第4个 参数给出相应命令所需的参数,此参数可选:
CMD | 说明 | 参数 |
SETVAL | 设置semid信号量集合中的semnum信号量semval为arg.val。同时更新semid_ds中的sem_ctime成员。 | arg.val |
GETALL | 返回信号量集合中所有信号量的值 | arg.array |
SETALL | 设置信号量集合中所有信号量的值 | arg.array |
IPC_STAT | 放置与信号量集合相连的semid_ds结构当前值于arg.buf指定的缓冲区 | arg.buf |
IPC_SET | 用arg.buf指定的结构值代替与信号量集合相连的semid_ds结构值 | arg.buf |
GETVAL | 系统调用,返回信号量集合重的semnum信号量的值semval |
|
GETPID | 返回最后一个操作该信号量集合的进程ID |
|
GETNCNT | 返回semncnt的值 |
|
GETZCNT | 返回semzcnt的值 |
|
IPC_RMID | 删除制定的信号量集合 |
|
可重用包装函数:
//获得指定的信号量的值
int get_sem_val(int sid, int semnum) {
return semctl(sid, semnum, GETVAL, 0);
}
//给指定的信号量赋初值
void init_a_semaphore(int sid, int semnum, int initval) {
union semun semopts;
semopts.val= initval;
semctl(sid, semnum, SETVAL, semopts);
}
//给指定的信号量集合赋初值
void init_all_semaphore(int sid, int val_array[]) {
union semun semopts;
semopts.array= (short unsigned int *)val_array;
semctl(sid, 0, SETALL, semopts.array);
}
//改变信号量集合的访问权限,权限参数必须是型如"066"的字符串
int changemode(int sid, char *mode) {
int rc;
union semun semopts;
struct semid_ds mysemds;
semopts.buf= &mysemds;
if ((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
return -1;
sscanf(mode, "%o", &semopts.buf->sem_perm.mode);
return semctl(sid, 0, IPC_SET, semopts);
}
//删除指定的信号量集合
int rm_semaphore(int sid) {
return semctl(sid, 0, IPC_RMID, 0);
}
4.3 #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
信号量操作函数 |
int semop(int semid, struct sembuf *sops, unsigned nsops); |
返回:失败-1, |
第1个参数指明信号量集合标识,
第2个参数sops指向一个类型为sembuf的结构数组
第3个参数nsops给出该数组元素个数。取值范围0~SEMOP。
此函数对制定的信号量集合进行操作,操作既可以针对单个信号量,也可一对整个信号量集合进行操作。操作的信号量对象和操作动作由sops指向的结构体指出:
struct sembuf { /* 操作数数组成员结构,用于semop函数*/
ushort sem_num; /* 信号量编号 */
short sem_op; /* 操作数 */
short sem_flag; /* 操作标志 */
}
sem_num指出信号量编号。
sem_op给出操作的类型:
1.< 0 减少一个信号量的值,减少为abs(sem_op)。这相当与请求信号量所控制的资源。当为-1时, 相当于P操作。当信号量当前值>=abs(sem_op),操作成功,否则当没有设置标志IPC_NOWAIT时,进程将等待直到信号量的当前值>=abs(sem_op)
2.>0 增加一个信号量的值,增加为sem_op。相当于返回信号量控制的资源。当为1时,相当于V操作。
3.=0 等待信号量变为0。对应于等待信号量控制的所有资源均可以用。如果信号量已经为0,调用立即返回;否则没有设置IPC_NOWAIT时,调用被阻塞。
sem_flag对操作进行适当控制:
1. IPC_NOWAIT 如果设置,当指定的操作不能完成时,进程将不等待立即返回-1,且设置errno为EAGAIN。
2.SEM_UNDO 如果设置,当进程退出时,进行信号量解除(UNDO)操作。注意,设置此值后,对信号量进行相反操作的动作也要设置(UNDO)操作。
可重用包装函数:
int semaphore_P(int sem_id) {
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = SEM_UNDO;
if (semop(sem_id, &sb, 1) == -1) {
fprintf(stderr, "semaphore_p failed/n");
return (0);
}
return 1;
}
int semaphore_V(int sem_id) {
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
if (semop(sem_id, &sb, 1) == -1) {
fprintf(stderr, "semaphore_v failed/n");
return (0);
}
return 1;
}
1.1.3 Example
int main(int argc, char** argv) {
int sem_id;
int i, creat = 0;
int pause_time;
char* cp;
//以进程id作为随机数种子
srand((unsigned int)getpid());
//打开/创建仅一个信号量的初值为1
sem_id = open_semaphore_set((key_t)1234, 1, “ 0660” );
if (argc > 1 && strcmp(argv[1], "1")) {
init_a_semaphore(sem_id, 0, 1);
creat = 1;
sleep(2);
}
for(i = 0; i < argc; i++) {
cp = argv[i];
if (!semaphore_p(sem_id))
exit(EXIT_FAILURE);
printf("process %d", getpid());
fflush(stdout);
while(*cp) {
printf("%c", *cp);
fflush(stdout);
pause_time = rand() %3;
sleep(pause_time);
cp++;
}
}
printf("/n%d -finished/n", getpid());
if (creat == 1) {
sleep(10);
rm_semaphore(sem_id);
}
exit(EXIT_SUCCESS);
}