Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量

本章介绍有名信号相关函数的使用,在参考书的第10章。本文主要分为两部分,第一部分为有名信号量相关函数的介绍,第二部分为在生产-消费(生产、消费各一个线程)的案例中使用有名信号量进行同步操作;演示的程序源代码都是从G-F所提供免费源码工程中提取出来的,这样每个函数的执行操作就会更加清楚;

第一部分:

1、有名信号量的相关函数

#include 

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

打开或者创建一个有名信号量,oflag的的参数设置同open()函数相类似

#include 

int sem_close(sem_t *sem);

关闭信号量,但并不删除信号量。但信号量是随内核持续的,下面的demo演示就可看到进程执行结束,信号量依然随内核保持

#include 

int sem_unlink(sem_t *sem);

从文件系统删除有名消耗量的名字

#include 
int sem_wait(sem_t *sem); //信号量V操作,减1
int sem_post(sem_t *sem); //信号量P操作,加1
int sem_getvalue(sem_t *sem,int *valp); //返回 信号量sem的当前值

2、源码分析

semcreate源码:

include "pxsem.h"

int main(int argc, char **argv)
{
        int             c, flags;
        sem_t   *sem;
        unsigned int    value;

        flags = O_RDWR | O_CREAT;
        value = 1;
        while ( (c = Getopt(argc, argv, "ei:")) != -1) {
                switch (c) {
                case 'e':
                        flags |= O_EXCL;
                        break;

                case 'i':
                        value = atoi(optarg);
                        break;
                }
        }
        if (optind != argc - 1)
                err_quit("usage: semcreate [ -e ] [ -i initalvalue ] ");

        sem = Sem_open(argv[optind], flags, FILE_MODE, value);

        Sem_close(sem);
        exit(0);
}

semunlink源码:

#include        "pxsem.h"

int
main(int argc, char **argv)
{
        if (argc != 2)
                err_quit("usage: semunlink ");

        Sem_unlink(argv[1]);

        exit(0);
}

semgetvalue源码:

#include        "pxsem.h"

int
main(int argc, char **argv)
{
        sem_t   *sem;
        int             val;

        if (argc != 2)
                err_quit("usage: semgetvalue ");

        sem = Sem_open(argv[1], 0);
        Sem_getvalue(sem, &val);
        printf("value = %d\n", val);

        exit(0);
}

运行结果如下,

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第1张图片

在编译完成之后,当创建管道时,发现有sem_open error for /tmp/test: No such file or directory的提示错误,解决的方案是需要在

/dev/shm/目录下创建一个sem.tmp目录;详情可以看https://www.redhat.com/archives/phil-list/2003-January/msg00113.html

semcreate 创建一个有名信号量,随着semcreate进程的结束,可以看到信号量的值依然为1,说明fedora linux系统的有名信号量是随内核保持的。semwait程序把信号量进行减1操作;应用程序每次对信号量进行了关闭操作,但信号量还是存在,实际没有被删除。semunlink程序把信号量从系统进行删除。

sempost源码:

#include        "pxsem.h"

int
main(int argc, char **argv)
{
        sem_t   *sem;
        int             val;

        if (argc != 2)
                err_quit("usage: sempost ");

        sem = Sem_open(argv[1], 0);
        Sem_post(sem);
        Sem_getvalue(sem, &val);
        printf("value = %d\n", val);

        exit(0);
}

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第2张图片

先挂起的进程先被唤醒执行; fedora Linux由于系统缘故,演示不了信号量为负数的情况;

第二部分:

1、有名信号量在单个生产-单个消费者问题的应用,

/* include main */
#include        "pxsem1.h"

#define NBUFF    10 //缓冲区大小
#define SEM_MUTEX       "mutex"    //同一时刻只能一个线程(生产或者消费)访问数据缓冲区     /* these are args to px_ipc_name() */
#define SEM_NEMPTY      "nempty"   //(缓冲区能够提供生产的空位数量)
#define SEM_NSTORED     "nstored"  //(缓冲区已经生产好的数据个数)

int             nitems;                                 /* read-only by producer and consumer */
struct {        /* data shared by producer and consumer */
  int   buff[NBUFF];
  sem_t *mutex, *nempty, *nstored;
} shared;

void    *produce(void *), *consume(void *);

int
main(int argc, char **argv)
{
        pthread_t       tid_produce, tid_consume;

        if (argc != 2)
                err_quit("usage: prodcons1 <#items>");
        nitems = atoi(argv[1]);

                /* 4create three semaphores */
        shared.mutex = Sem_open(Px_ipc_name(SEM_MUTEX), O_CREAT | O_EXCL,
                                                        FILE_MODE, 1);//信号量mutex初始值为1
        shared.nempty = Sem_open(Px_ipc_name(SEM_NEMPTY), O_CREAT | O_EXCL,
                                                         FILE_MODE, NBUFF);//信号量mutex初始值为10
        shared.nstored = Sem_open(Px_ipc_name(SEM_NSTORED), O_CREAT | O_EXCL,
                                                          FILE_MODE, 0);//信号量mutex初始值为0

        /* 4create one producer thread and one consumer thread */
        Set_concurrency(2);//第7章已经介绍过相关线程函数的作用
        Pthread_create(&tid_produce, NULL, produce, NULL);
        Pthread_create(&tid_consume, NULL, consume, NULL);

        /* 4wait for the two threads */
        Pthread_join(tid_produce, NULL);
        Pthread_join(tid_consume, NULL);

        /* 4remove the semaphores */
        Sem_unlink(Px_ipc_name(SEM_MUTEX));//从系统中删除有名信号量
        Sem_unlink(Px_ipc_name(SEM_NEMPTY));
        Sem_unlink(Px_ipc_name(SEM_NSTORED));
        exit(0);
}
/* end main */

/* include prodcons */
void *
produce(void *arg)
{
        int             i;

        for (i = 0; i < nitems; i++) {
                Sem_wait(shared.nempty);//缓冲区的空位大于0,那么就把nempty减1然后继续执行(初始值为10)        /* wait for at least 1 empty slot */
                Sem_wait(shared.mutex);
                shared.buff[i % NBUFF] = i;     /* store i into circular buffer */
                printf("producter buff[%d] = %d\n", (i%NBUFF), shared.buff[i % NBUFF]);
                Sem_post(shared.mutex);
                Sem_post(shared.nstored);//生产完成,缓冲区已经生产的数据nstored累加1       /* 1 more stored item */
        }
        return(NULL);
}

void *
consume(void *arg)
{
        int             i;
        //逻辑与生产者线程相反
        for (i = 0; i < nitems; i++) {
                Sem_wait(shared.mutex);
                Sem_wait(shared.nstored);//缓冲区已生产的数据大于零,那么就把nstored减1然后继续执行               /* wait for at least 1 stored item */
                if (shared.buff[i % NBUFF] != i)
                        printf("buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
                        printf("consumer buff[%d] = %d\n", (i%NBUFF), shared.buff[i % NBUFF]);
                Sem_post(shared.mutex);
                Sem_post(shared.nempty); //缓冲区取出一个数据,那么就把nempty累加1               /* 1 more empty slot */
        }
        return(NULL);
}
/* end prodcons */
                                                                                                                                                                                               

以下为生产50个数据程序执行的情况,

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第3张图片

如果调换消费者线程中的以下两条语句的执行顺序,

Sem_wait(shared.mutex);
Sem_wait(shared.nstored);  

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第4张图片

在执行程序的过程中发生了死锁:

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第5张图片

Unix网络编程源码解析_进程间通信IPC之(Posix)有名信号量_第6张图片

执行顺序的不恰当,会让程序死锁。

你可能感兴趣的:(linux)