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;
}

 

posted @ 2018-09-13 22:56  二狗啸地  阅读(572)  评论(0编辑  收藏  举报