linux ipc信号量
ipcs 命令,可以看到当前系统上的共享资源实例
ipcrm 命令,可以删除一个共享资源实例
linux 操作信号量的函数有三个:semget, semop, semctl
semget 声明为:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
key 是一个键值,用来标识一个全局唯一的信号量集。要通过信号量通信的进程需要使用相同的键值来获取该信号量。
nsems 表示要创建/获取的信号量集中信号量的数目。如果是创建信号量,这个值必须指定。如果是获取已经存在的信号量,可以把它设置成0.
semflg 指定一组标志。它的低端9个比特是该信号量的权限。相当于文件操作权限,类似open函数的mode参数。而且,它还可以和IPC_CREAT标志按位“或”运算来创建新的信号量集。
成功返回一个正整数,它是信号量集的标识符;失败返回-1,并设置errno
semop 系统调用改变信号量的值,即执行P、V操作:
int semop(int semid, struct sembuf *sops, size_t nsops);
semid 是由 semget 返回的信号量集标识符,用来指定被操作的目标信号量集。
sops 是 struct sembug 结构体,定义为:
/* Structure used for argument to `semop' to describe operations. */
struct sembuf
{
unsigned short int sem_num; /* semaphore number */
short int sem_op; /* semaphore operation */
short int sem_flg; /* operation flag */
};
sem_num 是信号量集的编号,0表示第一个信号量。
sem_op 指定操作类型,可选值为正整数、0、负整数。
sem_flg 可选值是 IPC_NOWAIT, SEM_UNDO。IPC_NOWAIT指,无论信号量集操作是否成功,semop调用都立刻返回。SEM_UNDO含义是,当进程退出时,取消正在进行的semop操作
nsops参数指定要执行的操作个数,即sops数组中元素的个数。semop对数组sops中的每个元素按照数组顺序依次执行操作,并且这个过程是原子操作。
semop成功返回0,失败返回-1并设置errno。失败的时候,sops数组中指定的所有操作都不执行。
semctl用来对信号量进行直接操作:
int semctl(int semid, int semnum, int cmd, ...);
semid 是semget调用返回的信号量集标识符。semnum指定被操作的信号量在信号量集中的编号。cmd指定要执行的命令。有的命令需要传入第4个参数,这个参数类型由用户定义,但是,内核给出了它的格式,而且,必须是这个格式:
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) */
};
cmd 的值比较多,不列举了,可以看manpage,其中,
IPC_STAT 将信号量集关联的内核数据结构复制到semun.buf中,
GETVAL 获取信号量的semval的值
SETVAL 将信号量的semval值设置为semun.val
IPC_RMID 立即移除信号量集,唤醒所有等待信号量集的进程
该函数返回值根据cmd不同而不同。失败返回-1,并设置errno
semget函数第一个参数key可以设置为 IPC_PRIVATE(值为0),这样,无论信号量是否存在,都会创建一个新的信号量
#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;
}