System V 信号量不是用来在进程间传输数据的。相反,它们用来同步进程的动做。信号量的一个常见用途是同步一块共享内存的访问以防止一个进程在访问共享内存的同时另外一个进程更新这块内存的状况。linux
一个信号量是一个由内核维护的整数,其值被限制为大于或等于0。在一个信号量上能够执行各类操做(即系统调用):程序员
上面后两个操做可能致使调用阻塞。由于内核会将全部试图将信号量下降到0之下的操做阻塞。相似的,若是信号量的当前值不为0,那么等待信号量的值等于0的调用进程将会发生阻塞。数组
#include<sys/types.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int semflg);
//return semaphore set identifier on success,or -1 on error
复制代码
key: 使用值IPC_PRIVATE或由ftok()返回的键bash
nsems: 指定集合中信号量的数量,而且其值必须大于0.若是使用semget()来获取一个既有集的标识符,那么nsems必需要小于或等于集合的大小(不然发生EINVAL错误)。没法修改一个既有集中的信号量数量。数据结构
semflg:参数是一个位掩码,它指定了施加于新信号量集之上的权限或需检查的一个既有集合的权限。ide
建立一个System V 信号量oop
int semid=semget(IPC_PRIVATE,1,S_IRUSR | S_IWUSR);//建立一个信号量集,数量为1,所属用户可读可写 使用IPC_PRIVATE时,能够不显示指出IPC_CREAT
if (semid == -1)
errExit("semid");
复制代码
#include<sys/types.h>
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,.../*union semun arg*/);
复制代码
/* The user should define a union like the following to use it for arguments
for `semctl`.
union semun
{
int val; <= value for SETVAL
struct semid_ds *buf; <= buffer for IPC_STAT & IPC_SET
unsigned short int *array; <= array for GETALL & SETALL
struct seminfo *__buf; <= buffer for IPC_INFO
};
Previous versions of this file used to define this union but this is
incorrect. One can test the macro _SEM_SEMUN_UNDEFINED to see whether
one must define the union or not. */
复制代码
cmd: 参数指定了需执行的操做ui
常规控制操做:this
IPC_RMID:当即删除信号量集及其关联的semid_ds数据结构。全部因semop()调用操做堵塞的进程都会当即唤醒,并返回EIDRM错误。这操做不须要arg参数。spa
IPC_STAT: 在arg.buf指向的缓冲区中放置一份与这个信号量集相关联的semid_ds数据结构的副本。
IPC_SET: 使用arg.buf指向的缓冲区中的值来更新与这个信号量集相关联的semid_ds数据结构中选中的字段。
获取和初始化信号量值
GETVAL:semctl()返回由semid指定的信号量集中第semnum个信号量的值。这个操做无需arg参数。
SETVAL:将semid指定的信号量集中第semnum个信号量的值修改成arg.val。
GETALL: 获取由semid指向的信号量集中全部信号量的值并将它们放在arg.array指向的数组中。程序员必需要确保该数组具有足够的空间。
SETALL:使用arg.array指向的数组中的值修改semid指向的集合中的全部信号量。
获取单个信号量的信息
下面操做返回semid引用的集合中第semnum个信号量的信息。全部这些操做都须要在信号量集合中具有读权限,而且无需arg参数。
GETPID:返回上一个在该信号量上执行semop()的进程的进程ID; 这个值被称为sempid值。若是尚未进程在该信号量上执行semop(),那么就返回0。
GETNCNT: 返回当前等待该信号量的值增加的进程数; 这个值被称为semncnt值。
GETZCNT: 返回当前等待该信号量的值变成0的进程数; 这个值被称为semzcnt值。
/* Data structure describing a set of semaphores. */
struct semid_ds
{
struct ipc_perm sem_perm; /* operation permission struct */
__time_t sem_otime; /* last semop() time */
__syscall_ulong_t __glibc_reserved1;
__time_t sem_ctime; /* last time changed by semctl() */
__syscall_ulong_t __glibc_reserved2;
__syscall_ulong_t sem_nsems; /* number of semaphores in set */
__syscall_ulong_t __glibc_reserved3;
__syscall_ulong_t __glibc_reserved4;
};
/* Data structure used to pass permission information to IPC operations. */
struct ipc_perm
{
__key_t __key; /* Key. */
__uid_t uid; /* Owner's user ID. */ __gid_t gid; /* Owner's group ID. */
__uid_t cuid; /* Creator's user ID. */ __gid_t cgid; /* Creator's group ID. */
unsigned short int mode; /* Read/write permission. */
unsigned short int __pad1;
unsigned short int __seq; /* Sequence number. */
unsigned short int __pad2;
__syscall_ulong_t __glibc_reserved1;
__syscall_ulong_t __glibc_reserved2;
};
复制代码
程序员必需要使用semctl()系统调用显式地初始化信号量。(在linux上,semget()返回的信号量实际上会被初始化为0,但为了取得移植性就不能依赖于此。)
因建立和初始化信号量是分开进行的,因此当多个进程要对同一个信号量进行建立和初始化信号量时,就会出现竞争,那么信号量的初始值将由最后调用初始化的进程所决定。
解决办法:与信号量集相关联的semid_ds数据结构中的sem_otime字段的初始化。在一个信号量集首次被建立时,sem_otime字段会被初始化为0,而且只有后续的semop()调用才会修改这个字段的值。所以能够利用这个特性消除竞争条件。即只须要插入额外的代码来强制第二个进程(即没有建立信号量的那个进程)等待知道第一个进程即初始化了信号量又执行了一个更新sem_otime字段但不修改信号量的值的semop()调用为止。
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms);
if (semid != -1) { /* Successfully created the semaphore */
union semun arg;
struct sembuf sop;
sleep(5);
printf("%ld: created semaphore\n", (long) getpid());
arg.val = 0; /* So initialize it to 0 */
if (semctl(semid, 0, SETVAL, arg) == -1)
errExit("semctl 1");
printf("%ld: initialized semaphore\n", (long) getpid());
/* Perform a "no-op" semaphore operation - changes sem_otime
so other processes can see we`ve initialized the set. */
sop.sem_num = 0; /* Operate on semaphore 0 */
sop.sem_op = 0; /* Wait for value to equal 0 */
sop.sem_flg = 0;
if (semop(semid, &sop, 1) == -1)
errExit("semop");
printf("%ld: completed dummy semop()\n", (long) getpid());
} else { /* We didn`t create the semaphore set */
if (errno != EEXIST) { /* Unexpected error from semget() */
errExit("semget 1");
} else { /* Someone else already created it */
const int MAX_TRIES = 10;
int j;
union semun arg;
struct semid_ds ds;
semid = semget(key, 1, perms); /* So just get ID */
if (semid == -1)
errExit("semget 2");
printf("%ld: got semaphore key\n", (long) getpid());
/* Wait until another process has called semop() */
arg.buf = &ds;
for (j = 0; j < MAX_TRIES; j++) {
printf("Try %d\n", j);
if (semctl(semid, 0, IPC_STAT, arg) == -1)
errExit("semctl 2");
if (ds.sem_otime != 0) /* Semop() performed? */
break; /* Yes, quit loop */
sleep(1); /* If not, wait and retry */
}
if (ds.sem_otime == 0) /* Loop ran to completion! */
fatal("Existing semaphore not initialized");
}
}
复制代码
#include<sys/types.h>
#include<sys/sem.h>
int semop(int semid,struct sembuf *sops,unsigned int nsops);
//return 0 on succes,or -1 on error
struct sembuf
{
unsigned short int sem_num; /* semaphore number */
short int sem_op; /* semaphore operation */
short int sem_flg; /* operation flag */
};
复制代码
sops 参数是一个指向数组的指针,数组中包含了须要执行的操做。
nsops参数给出了数组的大小(数组至少需包含一个元素)。操做将会按照在数组中的顺序以原子的方式被执行了。
sem_num 字段标识出了在集合中的哪一个信号量上执行操做。
sem_op 字段指定了需执行的操做。
当semop()调用阻塞事,进程会保持阻塞直到发生下列某种状况为止。
sem_flg:参数是一个位掩码。
需特别指出: semop()是原子操做,要么当即执行全部操做,要么堵塞直到可以同时执行全部操做。
semtimedop()系统调用与semop()执行的任务同样,但多了一个timeout参数,这个参数能够指定调用所阻塞的时间上限。
#define _GNU_SOURCE
#include<sys/types.h>
#include<sys/sem.h>
int semtimedop(int semid,struct sembuf *sops,unsigned int nsops,struct timespec *timeout);
//return 0 on success, or -1 on error
复制代码
Boolean bsUseSemUndo = FALSE;
Boolean bsRetryOnEintr = TRUE;
int /* Initialize semaphore to 1 (i.e., "available") */
initSemAvailable(int semId, int semNum)
{
union semun arg;
arg.val = 1;
return semctl(semId, semNum, SETVAL, arg);
}
int /* Initialize semaphore to 0 (i.e., "in use") */
initSemInUse(int semId, int semNum)
{
union semun arg;
arg.val = 0;
return semctl(semId, semNum, SETVAL, arg);
}
/* Reserve semaphore (blocking), return 0 on success, or -1 with 'errno'
set to EINTR if operation was interrupted by a signal handler */
int /* Reserve semaphore - decrement it by 1 */
reserveSem(int semId, int semNum)
{
struct sembuf sops;
sops.sem_num = semNum;
sops.sem_op = -1;
sops.sem_flg = bsUseSemUndo ? SEM_UNDO : 0;
while (semop(semId, &sops, 1) == -1)
if (errno != EINTR || !bsRetryOnEintr)
return -1;
return 0;
}
int /* Release semaphore - increment it by 1 */
releaseSem(int semId, int semNum)
{
struct sembuf sops;
sops.sem_num = semNum;
sops.sem_op = 1;
sops.sem_flg = bsUseSemUndo ? SEM_UNDO : 0;
return semop(semId, &sops, 1);
}
复制代码
union semun arg;
struct seminfo buf;
arg.__buf=&buf;
semctl(0,0,IPC_INFO,arg);
struct seminfo
{
int semmap;
int semmni; //系统级别的限制,限制了所能建立的信号量标识符的数量
int semmns;//系统级别限制,限制了全部信号量集中的信号量数量。
int semmnu;//系统级别限制,限制了信号量撤销结构的总数量。
int semmsl; //一个信号量集中能分配的信号量的最大数量
int semopm; //每一个semop()调用可以执行的操做的最大数量。(semop(),E2BIG)
int semume; //每一个信号量撤销结构中撤销条目的最大数量。
int semusz;
int semvmx;//一个信号量能取的最大值。(semop(),ERANGE)
int semaem; //在semadj总和中可以记录的最大值。(semop(),ERANGE)
};
复制代码