IPC之Posix信号量详解

Posted 带鱼兄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC之Posix信号量详解相关的知识,希望对你有一定的参考价值。


基本概念:

信号量(semaphore)是一种用于提供不同进程间或一个给定进程的不用线程间同步手段的原语。

共有三种类型的信号量:

1)Posix有名信号量:使用Posix IPC名字标识,可用于进程或线程间的同步。

2)Posix基于内存的信号量:存放在共享内存区中,可用于进程或线程间的同步。

3)System V信号量:在内核中维护,可用于进程或者线程间同步。(本文不讨论System V信号量)


一个进程可以在某个信号量上执行三种操作:

(1)创建(create)一个信号量。这还要求调用者指定初始值。

(2)等待(wait)一个信号量。该操作会测试这个信号量的值,如果其值小于或等于0,那就等待(阻塞),一旦值变为大于0就将它减1,这两个步骤是一个原子操作。

(3)挂出(post)一个信号量。该操作将信号量的值加1,挂出操作同样是原子的。


Posix.1基本原理一文声称。有了互斥锁和条件变量还提供信号量的原因是:“本标准提供信号量的主要目的是提供一种进程间同步方式。这些进程可能共享也可能不共享内存区。互斥锁和条件变量是作为线程间的同步机制说明的,这些线程总是共享(某个)内存区。这两者都是已广泛使用了多年的同步范式。每组原语都特别适合于特定的问题”。


尽管信号量的意图在于进程间同步,互斥锁和条件变量的意图则在于线程间同步,但是信号量也可用于线程间,互斥锁和条件变量也可用于进程间。我们应该使用适合具体应用的那组原语。


上面提到Posix提供两类信号量:有名(named)信号量和基于内存的(memory-based)信号量,后者也称为无名(unnamed)信号量。图10-6比较了这两类信号量使用的函数。



图10-2是Posix有名信号量的图示:



图10-7展示了某个进程内由两个线程共享的一个Posix基于内存的信号量:



图10-8展示了某个共享内存区中由两个进程共享的一个Posix基于内存的信号量。图中画出该共享内存区属于这两个进程的地址空间。



多线程编程通常使用互斥锁、读写锁、条件变量、自旋锁,所以下面不再对基于内存的信号量展开。


下面讨论下Posix有名信号量编程:


linux下的Posix有名信号量编程的一些限制:

man 7 sem_overview

Named semaphores
              A named semaphore is identified by a name of the form /somename;
              that  is,  a  null-terminated  string of up to NAME_MAX-4 (i.e.,
              251) characters consisting of an initial slash, followed by  one
              or  more  characters,  none of which are slashes.  Two processes
              can operate on the same named semaphore by passing the same name
              to sem_open(3).

 Accessing named semaphores via the filesystem
       On  Linux,  named  semaphores are created in a virtual filesystem, nor‐
       mally mounted under /dev/shm, with  names  of  the  form  sem.somename.
       (This  is  the  reason  that  semaphore names are limited to NAME_MAX-4
       rather than NAME_MAX characters.)
就是说,信号量命名必须是/somename这种格式,查看信号量在/dev/shm中,被命名为sem.somename

创建:

SEM_OPEN(3)                Linux Programmer's Manual               SEM_OPEN(3)

NAME
       sem_open - initialize and open a named semaphore

SYNOPSIS
       #include <fcntl.h>           /* For O_* constants */
       #include <sys/stat.h>        /* For mode constants */
       #include <semaphore.h>

       sem_t *sem_open(const char *name, int oflag);
       sem_t *sem_open(const char *name, int oflag,
                       mode_t mode, unsigned int value);

       Link with -pthread.
返回:若成功则为指向信号量的指针,若出错则为SEM_FALLED。

oflag参数如果指定了O_CREAT,那么第三个和第四个参数是需要的,其中mode参数指定权限位,value参数指定信号量的初始值。该初始值不能超过SEM_VALUE_MAX(这个常值必须至少为32767)。

sem_open的返回值是指向某个sem_t数据类型的指针。

关闭:

SEM_CLOSE(3)               Linux Programmer's Manual              SEM_CLOSE(3)

NAME
       sem_close - close a named semaphore

SYNOPSIS
       #include <semaphore.h>

       int sem_close(sem_t *sem);

       Link with -pthread.
返回:若成功则为0.若出错则为-1

删除:

SEM_UNLINK(3)              Linux Programmer's Manual             SEM_UNLINK(3)

NAME
       sem_unlink - remove a named semaphore

SYNOPSIS
       #include <semaphore.h>

       int sem_unlink(const char *name);

       Link with -pthread.
返回:若成功则为0.若出错则为-1

等待:

SEM_WAIT(3)                Linux Programmer's Manual               SEM_WAIT(3)

NAME
       sem_wait, sem_timedwait, sem_trywait - lock a semaphore

SYNOPSIS
       #include <semaphore.h>

       int sem_wait(sem_t *sem);

       int sem_trywait(sem_t *sem);

       int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

       Link with -pthread.
均返回:若成功则为0,若出错则为-1

挂出:

SEM_POST(3)                Linux Programmer's Manual               SEM_POST(3)

NAME
       sem_post - unlock a semaphore

SYNOPSIS
       #include <semaphore.h>

       int sem_post(sem_t *sem);

       Link with -pthread.

均返回:若成功则为0,若出错则为-1


测试:

SEM_GETVALUE(3)            Linux Programmer's Manual           SEM_GETVALUE(3)

NAME
       sem_getvalue - get the value of a semaphore

SYNOPSIS
       #include <semaphore.h>

       int sem_getvalue(sem_t *sem, int *sval);

       Link with -pthread.
均返回:若成功则为0,若出错则为-1


例子1,创建关闭删除信号量。

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

/* man 7 sem_overview */

int main()
{
    /* 创建和打开信号量 */
    char *name = "/test"; /* 必须是这种格式/somename */
    unsigned int value = 1;
    sem_t *sem = sem_open(name, O_RDWR | O_CREAT | O_EXCL, 0777, value);
    if (sem == SEM_FAILED) {
        perror("sem_open create failed");
        sem = sem_open(name, O_RDWR);
        if (sem == SEM_FAILED) {
            perror("sem_open open failed");
            exit(EXIT_FAILURE);
        }
    }
    printf("sem_open %s success\\n", name);

    /* 获取当前信号量的值 */
    value = 0;
    if (sem_getvalue(sem, &value) != -1)
        printf("the sem value = %d\\n", value);

    /* 关闭信号量 */
    if (sem_close(sem) != -1)
        printf("sem_close %s success\\n", name);

    printf("wait for sem_unlink, 30s\\n");
    sleep(30);

    /* 删除信号量 */
    if (sem_unlink(name) != -1)
        printf("sem_unlink %s success\\n", name);

    return 0;
}

编译运行:



例子2,两个进程,一个挂出,一个等待。

挂出进程:

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

/* man 7 sem_overview */

int main()
{
    /* 创建和打开信号量 */
    char *name = "/test"; /* 必须是这种格式/somename */
    unsigned int value = 0;
    sem_t *sem = sem_open(name, O_RDWR | O_CREAT | O_EXCL, 0777, value);
    if (sem == SEM_FAILED) {
        perror("sem_open create failed");
        sem = sem_open(name, O_RDWR);
        if (sem == SEM_FAILED) {
            perror("sem_open open failed");
            exit(EXIT_FAILURE);
        }
    }
    printf("sem_open %s success\\n", name);

    int i = 0;
    int num = 5; /* 挂出5次 */
    for (i; i<num; i++) {
        if (sem_post(sem) != -1) {
            printf("sem_post success, ");
        }
        int value = 0;
        if (sem_getvalue(sem, &value) != -1) {
            printf("the semaphore value = %d\\n", value);
        }
    }

    sem_close(sem);

    return 0;
}

等待进程:

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

/* man 7 sem_overview */

int main()
{
    /* 创建和打开信号量 */
    char *name = "/test"; /* 必须是这种格式/somename */
    unsigned int value = 0;
    sem_t *sem = sem_open(name, O_RDWR | O_CREAT | O_EXCL, 0777, value);
    if (sem == SEM_FAILED) {
        perror("sem_open create failed");
        sem = sem_open(name, O_RDWR);
        if (sem == SEM_FAILED) {
            perror("sem_open open failed");
            exit(EXIT_FAILURE);
        }
    }
    printf("sem_open %s success\\n", name);

    while (1) {
        if (sem_wait(sem) != -1) {
            int value = 0;
            if (sem_getvalue(sem, &value) != -1) {
                printf("sem_wait success, the semaphore value = %d\\n", value);
            }
        }
    }

    return 0;
}


编译运行:



信号量限制:

Posix定义了两个信号量限制:

SEM_NSEMS_MAX 一个进程可同时打开着的最大信号量数(Posix要求至少为256)

SEM_VALUE_MAX 一个信号量的最大值(Posix要求至少为32767)

可以通过sysconf函数获取,例子semsysconf.c:

#include <unistd.h>
#include <stdio.h>

int main()
{
    long sem_nsems_max = sysconf(_SC_SEM_NSEMS_MAX);
    long sem_value_max = sysconf(_SC_SEM_VALUE_MAX);
    printf("sem_nsems_max:%ld\\n", sem_nsems_max);
    printf("sem_value_max:%ld\\n", sem_value_max);
}

编译运行:



原文出自:http://blog.csdn.net/daiyudong2020/article/details/52347537


参考:《unix网络编程》·卷2

End;

以上是关于IPC之Posix信号量详解的主要内容,如果未能解决你的问题,请参考以下文章

Posix信号量

用于IPC的命名信号的POSIX实现

RE:Posix 和 System V IPC

使用 posix 未命名信号量的 IPC [关闭]

IPC- Posix与system v

linux里的ipc是啥意思