Linux进程通信---信号量

信号量是一个内核变量,它可以被系统中的任何进程所访问。进程间可以使用这个变量来协调对于共享内存和其他资源的访问。

如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

简单的例子
父进程与子进程共享打印机,必须其中一者打印完后,另一者再打印。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>

int P(int semid, int who)
{
    struct sembuf sf;
    sf.sem_num = who;// 在信号集中的编码0,1,2...
    sf.sem_op = -1;  // 信号量在一次操作中需要改变的数据,通常是两个数,  
                     // 一个是-1,即P(等待)操作,一个是+1,即V(发送信号)操作 
    sf.sem_flg = 0;  // 信号操作标志,可能的选择有两种:
    // IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息;
    // SEM_UNDO:程序结束时(无论正不正常结束),保证信号值会被重设为semop()调用前的值。
    //  这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

    // 操作信号量
    // int semop(int semid, struct sembuf *actions, size_t numacionts);
    // semid: 指定信号量集
    // actions: 一组活动的数组
    // numactions: 数组的大小
    return semop(semid, &sf, 1);
}

int V(int semid, int who)
{
    struct sembuf sf;
    sf.sem_num = who;
    sf.sem_op = 1;
    sf.sem_flg = 0;
    return semop(semid, &sf, 1);
}

int main()
{
    key_t key = ftok(".", 0x6666);
    if(key < 0)
    {
        perror("ftok\n");
        return -1;
    }

    // 创建信号量集
    // semset_id = semget(key_t key, int numsems, int flags);
    // key:所创建或打开信号量集的键值,也可以有函数 ftok 生成
    // numsems:表示创建的信号量集中信号量的个数,该参数只在创建信号量集时有效
    // flags: 
    //    IPC_CREAT: 如果IPC存在,则打开它,若不存在,则创建IPC资源。
    //    IPC_EXCL: 不能单独使用,没有意义;当与IPC_CREAT搭配使用时表示:
    //      若IPC不存在则创建IPC资源,若存在,则产生错误。
    int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
    if(semid < 0)
    {
        perror("semget\n");
        return -1;
    }

    // 初始化信号量
    // 执行在信号量集上的控制操作
    // semctl(int semset_id, int semnum, int cmd, union semun arg)
    // semset_id:信号量集IPC标识符,因为信号量一般是作为一个信号量集使用的,而不是一个单独的信号量
    // semnum:集合中某特定信号量的编码,第一个信号为0
    // cmd:控制命令:
    //   IPC_STAT   读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    //   IPC_SET    设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    //   IPC_RMID   将信号量集从内存中删除。
    //   GETALL     用于读取信号量集中的所有信号量的值。
    //   GETNCNT    返回正在等待资源的进程数目。
    //   GETPID     返回最后一个执行semop操作的进程的PID。
    //   GETVAL     返回信号量集中的一个单个的信号量的值。
    //   GETZCNT    返回这在等待完全空闲的资源的进程数目。
    //   SETALL     设置信号量集中的所有的信号量的值。
    //   SETVAL     设置信号量集中的一个单独的信号量的值。
    // arg:控制命令的参数
    if(semctl(semid, 0, SETVAL, 1) < 0)
    {
        perror("semctl\n");
        return -1;
    }

    pid_t id = fork();

    if(id == 0)
    {
        int semid = semget(key, 0, IPC_CREAT);
        if(semid < 0)
        {
            perror("child semget\n");
            return -1;
        }

        while(1)
        {
            P(semid, 0);
            printf("A\n");
            fflush(stdout);
            usleep(100000);
            printf("A\n");
            fflush(stdout);
            usleep(100000);
            V(semid, 0);
        }
    }
    else
    {
        while(1)
        {
            P(semid, 0);
            printf("B\n");
            fflush(stdout);
            usleep(100000);
            printf("B\n");
            fflush(stdout);
            usleep(100000);
            V(semid, 0);
        }
        wait(NULL);
    }

    // 删除信号量
    if(semctl(semid, 0, IPC_RMID, NULL) < 0)
    {
        perror("semctl\n");
        return -1;
    }

    return 0;
}

这里写图片描述

一个读者、一个写者

写者

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>

void wait_and_lock(int semset_id)
{
    struct sembuf actions[2];
    actions[0].sem_num = 0;   // 读信号量
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = 0;    // 等待读者为0

    actions[1].sem_num = 1;   // 写信号量
    actions[1].sem_flg = SEM_UNDO;
    actions[1].sem_op = 1;    // 增加写者计数

    if(semop(semset_id, actions, 2) == -1)
    {
        perror("semop: locking\n");
    }
}

void release_lock(int semset_id)
{
    struct sembuf actions[1];
    actions[0].sem_num = 1;  // 写信号量
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = -1;  // 减少写者计数

    if(semop(semset_id, actions, 1) == -1)
    {
        perror("semop: unlocking\n");
    }
}

int main()
{
    // 创建信号量集
    int semset_id = semget((key_t)1234, 2, IPC_CREAT|IPC_EXCL|0666);
    if(semset_id < 0)
    {
        perror("semget\n");
        return -1;
    }

    // 初始化读者信号量
    if(semctl(semset_id, 0, SETVAL, 0) < 0)
    {
        perror("semctl\n");
        return -1;
    }

    // 初始化写者信号量
    if(semctl(semset_id, 1, SETVAL, 0) < 0)
    {
        perror("semctl\n");
        return -1;
    }

    wait_and_lock(semset_id);
    for(int i=0; i<3; ++i)
    {
        printf("AA\n");
        sleep(3);
    }
    release_lock(semset_id);

    // 删除信号量集
    if(semctl(semset_id, 0, IPC_RMID, NULL) == -1)
    {
        perror("semctl\n");
        return -1;
    }

    return 0;
}

读者

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void wait_and_lock(int semset_id)
{
    struct sembuf actions[2];
    actions[0].sem_num = 1;   // 写信号量
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = 0;    // 等待写者数为0

    actions[1].sem_num = 0;   // 读信号量
    actions[1].sem_flg = SEM_UNDO;
    actions[1].sem_op = 1;    // 增加读者计数

    if(semop(semset_id, actions, 2) == -1)
    {
        perror("semop: locking\n");
    }
}

void release_lock(int semset_id)
{
    struct sembuf actions[1];
    actions[0].sem_num = 0;   // 读信号量
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = -1;   // 减少读者计数

    if(semop(semset_id, actions, 1) == -1)
    {
        perror("semop: unlocking\n");
    }
}

int main()
{
    // 得到已有的信号量集
    int semset_id= semget((key_t)1234, 2, 0);
    if(semset_id < 0)
    {
        perror("semget\n");
        return -1;
    }

    wait_and_lock(semset_id);
    printf("read\n");
    release_lock(semset_id);

    // 不需要删除信号量集

    return 0;
}

这里写图片描述

这里写图片描述
写者先运行完后,读者才开始读。但读者出现semop: unlocking : Invalid argument,不知道是什么原因。

出现“semget : File exists”的解决方式

这里写图片描述

参考
http://www.jb51.net/article/103886.htm
http://www.1024do.com/?p=3581
https://www.cnblogs.com/hanxiaoyu/p/5859986.html
Unix/Linux编程实践教程 Bruce Molay

猜你喜欢

转载自blog.csdn.net/u012319493/article/details/79788651