Linux中POSIX信号量代码解析(P/V操作)

文章目录

    • POSIX信号量
  • 1. 基本概念
  • 2. POSIX匿名信号量
    • 2. 定义
    • 2.2 初始化
    • 2.3 P/V 操作
  • 3. POSIX具名信号量
    • 3.1 创建和打开
    • 3.2 P/V 操作
    • 3.3 关闭、删除和其他注意事项

POSIX信号量

1. 基本概念

POSIX信号量与IPC信号量组中的信号量元素的逻辑完全一样,但POSIX信号量操作更加简便,接口更加易用。在多进程多线程中运用广泛。
POSIX信号量分成两种:

POSIX匿名信号量
通常用在线程间
只存在于内存,在文件系统中不可见
POSIX具名信号量
通常用在进程间
存在于文件系统/dev/shm中,可被不同进程操作

2. POSIX匿名信号量

2. 定义

#include 
sem_t s;

2.2 初始化

#include 
int sem_init(sem_t *sem, int pshared, unsigned int value);

接口说明:
sem:待初始化信号量指针
pshared:指定信号量的作用范围
0:作用于进程内的线程间
非0:作用于进程间
value:信号量的初始值
示例

sem_t s;
int main()
{
  // 初始化POSIX匿名信号量:
  // 将其设定为在本进程内的个线程间使用
  // 并将其初始值设定为1
  sem_init(&s, 0, 1);
}

2.3 P/V 操作

POSIX信号量(不管是匿名的还是具名的)的 P/V 操作相对于systemV的信号量组而言,接口非常简单:
#include
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作
接口说明:
P操作即申请资源,因此 sem_wait()在资源不足时会阻塞,V操作是释放资源,且V操作永远不会阻塞
示例

#include 
sem_t s;
char buf[100];
void *routine(void *arg)
{
    while(1)
    {
        // 申请信号量-1,输出字符串的长度  p操作
        sem_wait(&s);
        printf("%d\n", strlen(buf));
    }
}
int main()
{
    // 初始化,信号量初始值为0
    sem_init(&s, 0, 0);
    pthread_t t;
    pthread_create(&t, NULL, routine, NULL);
    while(1)
    {
        fgets(buf, 100, stdin);
        // 输入字符串后,释放信号量+1   v操作
        sem_post(&s);
    }
}

3. POSIX具名信号量

POSIX 具名信号量主要用在多进程间同步互斥,其 P/V 操作与匿名版本无异,其最大的特点是存在于文件系统 /dev/shm 中,可以被系统中任意有权限的进程打开。

3.1 创建和打开

POSIX 具名信号量使用如下接口创建:

#include            /* For O_* constants */
#include         /* For mode constants */
#include 

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

说明
与文件IO函数 open() 类似,sem_open() 也提供了两个版本,初建时需要指定文件权限 mode 和初始值 value,后续再打开使用时则无需指定。
示例
// 在进程 a.c 中创建具名信号量

int main()
{
    // 创建有名信号量,参数1代表初始值为1
    sem_t *s = sem_open("mysem", O_CREAT, 0666, 1);
}

// 在进程 b.c 中打开具名信号量

int main()
{
    // 打开具名信号量
    sem_t *s = sem_open("mysem", O_RDWR);
}

编译程序 a.c(注意要加线程库 -lpthread)并运行该程序,便会在系统 /dev/shm/ 产生对应的文件:
gec@ubuntu:~$ gcc a.c -o a -lpthread
gec@ubuntu:~$ ./a
gec@ubuntu:~$ ls -l /dev/shm/
总用量 4
-rw-rw-r-- 1 gec gec 32 Nov 26 18:27 sem.mysem
gec@ubuntu:~$

3.2 P/V 操作

与匿名信号量完全一致:

#include 
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作

示例
// a.c

int main()
{
    sem_t *s = sem_open("mysem", O_CREAT, 0666, 0);
    printf("A\n");
    sem_post(s); // V操作
}
// b.c
int main()
{
    sem_t *s = sem_open("mysem", O_RDWR);
    // 等待A执行完毕,才执行B
    sem_wait(s); // P操作
    printf("B\n");
}

信号量的V操作就像一个远程开关,控制另一个进程的进度:在进程A尚未执行 sem_post(s) 之前,进程B会一直在 sem_wait(s) 静静等待,这就是多进程进度控制。
注意,上述程序 a.c 和程序 b.c 是两个独立的程序,彼此之间没有任何共享的变量和数据,他们通过基于文件系统的具名信号量达成协同。

3.3 关闭、删除和其他注意事项

POSIX 具名信号量跟文件操作非常类似,打开之后会在内核需要对其维护,因此在不再需要的时候应该予以关闭:

sem_close(s);

另外,即使所有进程都关闭了信号量并且退出,具名信号量对应的文件是不会消失的,并且会保留所有 P/V 操作后的值,如果不再需要这个文件本身,则除了可以直接在文件系统中删除外,也可以使用如下接口删除:

sem_unlink("mysem");

上文提到,具名信号量会将所有的 P/V 操作后的值,因此向上述程序 a.c 如果连续执行三遍,那么信号量的值将会被 +3 ,因此程序 b.c 可以连续进行三次 P 操作。

你可能感兴趣的:(嵌入式Linux开发工程师课程,linux,bash,运维,信号量,线程池)