System V信号量

来源:互联网 发布:java模拟上传文件 编辑:程序博客网 时间:2024/06/11 06:32
System V 信号量在内核中维护,其中包括二值信号量 、计数信号量、计数信号量集。
二值信号量 : 其值只有0、1 两种选择,0表示资源被锁,1表示资源可用;
计数信号量:其值在0 和某个限定值之间,不限定资源数只在0 1 之间;
计数信号量集 :多个信号量的集合组成信号量集

创建一个信号量或访问一个已经存在的信号量集。
int semget(key_t key, int nsems, int semflg);
该函数执行成功返回信号量标示符,失败返回-1
参数key是通过调用ftok函数得到的键值,nsems代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0,我们一旦创建了该信号量,就不能更改其信号量个数,只要你不删除该信号量,你就是重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;
semflg 指定该信号量的读写权限,当创建信号量时不许加IPC_CREAT ,若指定IPC_CREAT |IPC_EXCL则创建是存在该信号量,创建失败。

打开一个信号量集后,对其中一个或多个信号量的操作。
int semop(int semid, struct sembuf *sops, unsigned nsops);
该函数执行成功返回0,失败返回-1;
第一个参数semid 为信号量标示符;nops为第二个参数的操作数组的个数,第二个参数sops为一个结构体数组指针,结构体定义在sys/sem.h中,结构体如下

struct sembuf {   unsigned short sem_num; /* semaphore index in array */   short sem_op; /* semaphore operation */   short sem_flg; /* operation flags */};
sem_num 操作信号的下标,其值可以为0 到nops
sem_flg为该信号操作的标志:其值可以为0、IPC_NOWAIT 、 SEM_UNDO
0 在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止;
IPC_NOWAIT 在对信号量的操作不能执行的情况下,该操作立即返回;
SEM_UNDO当操作的进程推出后,该进程对sem进行的操作将被取消;
sem_op取值 >0 则信号量加上它的值,等价于进程释放信号量控制的资源
sem_op取值 =0若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回
sem_op取值 <0则信号量加上它的值,等价于进程申请信号量控制的资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回。

对信号量执行各种控制操作。
int semctl(int semid, int semnum, int cmd, ...);
该函数执行成功返回非负值,失败返回-1
参数semid为信号集的标识符,参数 semnum标识一个特定信号,该参数仅用于 SETVAL、GETVAL、GETPID命令

看一个例子:这个范例使用信号灯来同步共享内存的操作, 程序创建一块共享内存, 然后父子进程共同修改共享内存. 父子进程采用信号灯来同步操作.

#include <stdio.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#define SHM_KEY 0x33#define SEM_KEY 0x44union semun {    int val;    struct semid_ds *buf;    unsigned short *array;};int P(int semid){    struct sembuf sb;    sb.sem_num = 0;    sb.sem_op = -1;    sb.sem_flg = SEM_UNDO;        if(semop(semid, &sb, 1) == -1) {        perror("semop");        return -1;    }    return 0;}int V(int semid){    struct sembuf sb;    sb.sem_num = 0;    sb.sem_op = 1;    sb.sem_flg = SEM_UNDO;        if(semop(semid, &sb, 1) == -1) {        perror("semop");        return -1;    }    return 0;}int main(int argc, char **argv){    pid_t pid;    int i, shmid, semid;    int *ptr;    union semun semopts;    /* 创建一块共享内存, 存一个int变量 */    if ((shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0600)) == -1) {        perror("msgget");    }    /* 将共享内存映射到进程, fork后子进程可以继承映射 */    if ((ptr = (int *)shmat(shmid, NULL, 0)) == (void *)-1) {        perror("shmat");    }    *ptr = 0;    /* 创建一个信号量用来同步共享内存的操作 */    if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) {        perror("semget");    }    /* 初始化信号量 */    semopts.val = 1;    if (semctl(semid, 0, SETVAL, semopts) < 0) {        perror("semctl");    }    if ((pid = fork()) < 0) {        perror("fork");    } else if (pid == 0) {      /* Child */        /* 子进程对共享内存加1 */        for (i = 0; i < 100000; i++) {            P(semid);            (*ptr)++;            V(semid);            printf("child: %d\n", *ptr);        }    } else {                    /* Parent */        /* 父进程对共享内存减1 */        for (i = 0; i < 100000; i++) {            P(semid);            (*ptr)--;            V(semid);            printf("parent: %d\n", *ptr);        }        waitpid(pid);        /* 如果同步成功, 共享内存的值为0 */        printf("finally: %d\n", *ptr);    }    return 0;}




0 0
原创粉丝点击