linux进程间通信:system V 信号量

概念描述
  • 英文:semaphore 简称SEM,主要用来进行进程间同步
  • 本质:内核维护的一个正整数,可对其进行各种+/-操作
  • 分类:systemV 信号量、POSIX 有名信号量、POSIX 无名信号量
  • 用途:用来标示系统中可用资源的的个数,协调各个进程有序的访问资源,防止发生冲突
  • P操作:程序在进入临界区之前要对资源进行申请
  • V操作:程序离开临界区后要释放相应的资源
通信原理
  • 类似于房卡,不是单个值,而是一组(实际上是数组)信号量元素构成
  • 将信号量初始设置成一个绝对值
  • 在信号量当前值的基础上加一个数量
  • 在信号量当前值的基础上减去一个数量,降到0以下再去访问会引起阻塞
  • 阻塞进程一直等待其他进程修改该信号量的值,直到恢复正常运行
  • 信号量本身无意义,通常会与一块临界资源(共享内存)关联使用
编程接口
  • 获取信号量ID
    a. 头文件 <sys/ipc.h> <sys/sem.h>
    b. int semget(key_t key,int nsem, int semflg)
    c. 函数参数:

    • key: 用来表示信号量的键,通常使用值IPC_PRIVATE或者由ftok创建
    • nsem: 信号的数量,所有的信号量放在一个数组里
    • semflg:位掩码,用来设置信号量的权限或者检查一个已有信号量的权限
      IPC_CREAT: 如果找不到指定的key相关联的信号量,创建一个信号量集合
      IPC_EXCL: 若指定了IPC_CREAT且指定key相关联的信号量存在,报EXIST错误

    d. 返回值

    • 成功:返回操作信号量描述符
    • 失败:返回-1
  • 设置信号量
    a. 头文件 <sys/ipc.h> <sys/sem.h>
    b. int semctl(int semid, int semnum, int cmd ..)
    c. 函数参数:

    • semid: 信号量的描述符,标识符;用于操作信号量
    • semnum: 信号量的数量,所有的信号量放在一个数组内
    • cmd :
      IPC_RMID :删除信号量及相关联的内核smid_ds数据结构
      IPC_STAT: 获取semid_ds的副本
      IPC_SET: 设置semid_ds的数据结构
      GETVAL: 获取信号集中地 semnum个信号量的值
      GETALL: 获取所有信号量的值
      SETVAL: 设置信号集中的第semnum个信号量的值

    d. 函数返回值

    • 成功:根据cmd命令,返回不同的值
    • 失败:-1
  • 信号量P/V操作
    a. 头文件<sys/ipc.h> <sys/sem.h>
    b. int semop(int semid,struct sembuf *sops, size_t nsops);
    c. 函数参数:

    • semid :信号的IPC标识符
    • sops: 指向数组的指针,数组中包含了需要执行的操作
      struct sembuf {
      	unsigned short sem_num; // 标识要操作的信号集中的信号量
      	
      	/*
      	sem_op如下信息:
      	a.若大于0 将sem_op的值加到信号量值上
      	b. 若等于0 ,则对信号量进行检查,确定其当前值是否为0,若为0则操作结束;若不为0,则一直阻塞,直到为0结束
      	c. 若小于0,则将信号量值减去sem_op,最后结果大于或等于0,操作立即结束;若最后结果小于0,则当前进程会阻塞
      	*/
      	short sem_op;
      	/*信号操作对标记,SEM_UNDO,IPC_NOWAIT*/
      	short sem_flag;
      }
      
    • nsops:数组的大小

    d. 返回值

    • 成功:根据cmd命令,返回不同的值
    • 失败:-1,并设置errno全局变量
使用流程
  • 使用semget 创建或打开一个信号量集
  • 使用semctl SETVAL 或 SETALL 操作初始化集合中的信号量(其中一个进程操作即可,内核中维护,对其它进程是全局可见的)
  • 使用semop操作信号量的值,多个进程通过多信号量值的操作来表示一些临界资源的获取和释放
  • 当所有进程不再需要信号量集时,使用semctl IPC_RMID 操作删除这个信号量集(其中一个进程操作即可)
编程案例

sem_create.c

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

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

union semnum {
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *__buf;
};


int main ()
{
    int sem_id;
    key_t key;

    if ((key =ftok(".",111)) == -1) {
        printf("create key failed \n");
        _exit(-1);
    }
	
	//使用semget 创建或打开一个信号量集
    if ((sem_id = semget(key,3,IPC_CREAT | 0770)) == -1) {
        printf("create shm_id failed \n");
        _exit(-1);
    } 
    printf("sem_id is :%d\n",sem_id);

	//获取信号量中的值
    int sem_value;
    sem_value = semctl(sem_id , 0, GETVAL);
    printf("sem value is %d\n",sem_value);

	//使用semctl SETVAL 或 SETALL 操作初始化集合中的信号量。通过sem_union设置信号量的值
    union semnum sem_union;
    sem_union.val = 2;
    semctl(sem_id, 0, SETVAL, sem_union);
    sem_value = semctl(sem_id ,0, GETVAL);
    printf("sem value after reset is %d\n",sem_value);

	//使用semop操作信号量,对信号量进行pv操作,这里是-1
    struct sembuf sops,sops2,sops3;
    sops.sem_num = 0;
    sops.sem_op = -1;
    sops.sem_flg = SEM_UNDO;

    /*sops.sem_num = 1;
    sops.sem_op = -1;
    sops.sem_flg = SEM_UNDO;
    
    sops.sem_num = 2;
    sops.sem_op = -1;
    sops.sem_flg = SEM_UNDO;*/

    if( -1 == (semop(sem_id,&sops, 1)) ) {
        printf("semop set failed \n");
        _exit(-1);
    }
    sem_value = semctl(sem_id, 0, GETVAL);
    printf("sem value after semop is %d\n",sem_value);

    sleep(30);
    semctl(sem_id, 0 ,IPC_RMID);
    return 0;
}

最终的输出如下:

sem_id is :524314
sem value is 0
sem value after reset is 2
sem value after semop is 1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值