IPC之Posix信号量详解

原创 2016年08月28日 22:14:05


基本概念:

信号量(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之信号量详解

一、信号量介绍 作用:实现进程间的同步和互斥。 信号量,确切地说是信号量集,集合里有≥1个信号量成员。可以把信号量集比作数组,信号量当做数组用的元素。 但是,常用的信号量集都只有一个信号量成员。所以,...

Linux进程同步之POSIX信号量

POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分。在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V...

信号量——POSIX 与 System V的接口对比分析

一 POSIX 标准 #include   sem_t:信号量的数据结构   int sem_init (sem_t *sem, int pshared, unsigned int value) 无...

posix信号量sem_wait死锁解决方法

在开发过程中,有三个独立运行的程序模块,三个模块都对sqlite数据库进行读写操作。sqlite在linux共享性较差,所以需要加入互斥信号量解决三个模块访问数据库该问题。另外,在加入信号量后,信号量...

Linux系统编程——进程同步与互斥:POSIX有名信号量

在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量。无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。它们的区别和管道及命名管道的区别类似,无名信号量则直...

进程间通信笔记(6)—POSIX信号量

POSIX信号量简介。。。

linux——posix标准下的信号量

#include        sem_t *sem_open(const char *name, int oflag);        sem_t *sem_open(const char *...

POSIX信号量与互斥锁

3种用于线程同步的机制: POSIX信号量,互斥锁和条件变量 一.POSIX信号量 常用的POSIX信号量函数如下     int sem_init(sem_t *sem, int pshare...

Posix信号量

进程间通信方式: (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。   管道包括三种:  1)普通管道PIPE, 通常有种限制,一是半...

linux网络编程之POSIX信号量、自旋锁、读写锁

posix信号量 system v 信号量只能用于进程间同步,而posix信号量除了可以进程间同步,还可以线程间同步。systemv 信号量每次PV操作可以是N,但Posix信号量每次PV只能是1。除...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:IPC之Posix信号量详解
举报原因:
原因补充:

(最多只允许输入30个字)