进程间通信(IPC)之 信号量(semaphore)

信号量(semaphore)与前面的IPC结构不同,它是一个计数器。数据量用于实现进程间的互斥与同步,而不是用于存储进程间通信的数据。

特点

  1. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

  1. 信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作

  1. 每次对信号量的PV操作不仅限于对信号量值加1减1,而且可以加减任意正整数。

  1. 支持信号量组

原型

最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,也叫二值信号量。

而可以取多个正整数的信号量被称为通用信号量。

Linux下的信号量函数都是在通用信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

 #include <sys/sem.h>
 #include <sys/types.h>
 #include <sys/ipc.h>

// 创建或获取一个信号量;若成功返回信号量集ID,失败返回 -1
int semget(key_t key, int nsems, int semflg);
key: 整数值,不相关的进程可以通过它访问同一个信号量。
nsems: 创建信号量的数目,一般取值为1
semflg: 标志位,如果未创建时IPC_CREAT;如果为全新创建,不知道是否有人创建过,则为IPC_CREAT|IPC_EXCL。

// 对信号量组进行操作,改变信号量的值;成功返回0,失败返回-1
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid: 信号量标识符。
sops: 结构体
struct sembuf{
    short sem_num;
    short sem_op;   // 表明是p操作还是v操作,p:-1,v:+1。
    short sem_flg;  // 一般等于SEM_UNDO, 目的是:不论当前进程是否正常退出,
                    //  都将还原此操作的 sem_op 值。用于以防万一异常结束的进程。
}

// 控制信号量的相关信息;成功返回0,失败返回-1。
int semctl(int semid, int semnum, int cmd, ...);
semid: 信号量标识符。
semnum: 操作第几个信号量,因为是数组,所以从0开始
cmd: 将要采取的动作
   SETVAL:初始化信号量,该值通过下面union semun中的val成员设置,其作用是再信号量第一次使用之前对它进行设置。
   IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

如果有第四个参数,它会是一个union semun的联合体:

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short * array;
}
  • P:用于等待--获取资源 (-1)

  • V:用于操作--释放资源(1)

目的

保证同一时刻只能有一个进程对某个资源进行访问,下面是一个让子进程先执行去访问资源的例子

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

// int semget(key_t key, int nsems, int semflg);
// int semctl(int semid, int semnum, int cmd, ...);
// int semop(int semid, struct sembuf *sops, unsigned nsops);

union semun {
        int val;    /* Value for SETVAL */
        struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
        unsigned short  *array;  /* Array for GETALL, SETALL */
        struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};
void pCetKey(int id)
{
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = -1;  // 获取资源
        set.sem_flg = SEM_UNDO;
        semop(id, &set, 1);
        printf("getkey\n");
}
void vPutBackKey(int id)
{
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = 1;  // 释放资源
        set.sem_flg = SEM_UNDO;
        semop(id, &set, 1);
        printf("put back key\n");
}

int main()
{
        key_t key;
        int semid;
        key = ftok(".", 2);
        // 获取创建信号量,结果是数组, 1代表有一个信号量
        semid = semget(key, 1, IPC_CREAT|0666);
        union semun initsem;
        initsem.val = 0;
        // 0 代表操作的是数组中的第一个信号量
        // 初始化信号量
        semctl(semid, 0, SETVAL, initsem);

        int pid = fork();
        if(pid > 0){
                // 拿锁
                pCetKey(semid);
                printf("this is father\n");
                // 把锁返回去
                semctl(semid, 0, IPC_RMID);
        }
        else if(pid == 0){
                printf("this is child\n");
                vPutBackKey(semid);
        }else{
                printf("fork error\n");
        }

        return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值