linux 信号量_SystemV IPC通信信号量

562ea3bace63e7ce988feaef9f910169.gif

1信号量的认知

Linux 中用到的信号量有 3 种:ststem-V 信号量、POSIX 有名信号量和 POSIX 无名信号量)。他们虽然有很多显著不同的地方,但是最基本的功能室一致的:用来表征一种资源的数量,当多个进程或者线程争夺这些稀缺资源的时候,信号量用来保证他们合理地、秩序地使用这些资源,而不会陷入逻辑谬误之中。 

用一个例子来说明什么是“旗语”——红绿灯, 一个繁忙的十字路口,稀缺资源就是通过十字路口的权限,为了避免撞车规定每次只能是对开方向的车通过路口不能转弯),此时另外两个方向的车必须停下来等待,直到红绿灯变换为止

44c3e7c2abc540c0dbfd1ba12f247ab1.png

一些基本概念如下:       

1,多个进程或线程有可能同时访问的资源(变量、链表、文件等等)称为共享资源,也叫临界资源(critical resources)。       

2,访问这些资源的代码称为临界代码,这些代码区域称为临界区(critical zone)。       

3,程序进入临界区之前必须要对资源进行申请,这个动作被称为 P 操作,这就像你要把车开进停车场之前,先要向保安申请一张停车卡一样,P 操作就是申请资源,如果申请成功,资源数将会减少。如果申请失败,要不在门口等,要不走人。       

4,程序离开临界区之后必须要释放相应的资源,这个动作被称为 V 操作,这就像你把车开出停车场之后,要将停车卡归还给保安一样,V 操作就是释放资源,释放资源就是让资源数增加。所有一起访问共同临界资源的进程都必须遵循以上游戏规则,否则大家就都乱套了,但是值得注意的是:这些规则是自愿的,如果有进程就是胡来——在访问资源之前不申请,那么将会可能导致逻辑谬误,就像开车压死保安直接撞进停车场一样,虽然于情于理都不可以,物理上阻止不了这种行为。

2相关API

bcf6f71be85c8dc73e34e8ced201d8bb.png

创建信号量时,还受到以下系统信息的影响:
        1,SEMMNI:系统中信号量的总数最大值。
        2,SEMMSL:每个信号量中信号量元素的个数最大值。
        3,SEMMNS:系统中所有信号量中的信号量元素的总数最大值。
        Linux 中,以上信息在/proc/sys/kernel/sem 中可查看。

44c3e7c2abc540c0dbfd1ba12f247ab1.png

0c4fa256166d877f86d2fa8cbb215f37.png

使用以上函数接口需要注意以下几点:
        1,信号量操作结构体的定义如下:

struct sembuf{unsigned short sem_num; /* 信号量元素序号(数组下标) */short sem_op; /* 操作参数 */short sem_flg; /* 操作选项 */};

请注意:信号量元素的序号从 0 开始,实际上就是数组下标。
        2,根据 sem_op 的数值,信号量操作分成 3 种情况:
        A) 当 sem_op 大于 0 时:进行 V 操作,即信号量元素的值(semval)将会被加上 sem_op 的值。如果 SEM_UNDO 被设置了,那么该 V 操作将会被系统记录。V 操作永远不会导致进程阻塞。
        B) 当sem_op等于0时:进行等零操作,如果此时semval恰好为0,则semop( )立即成功返回,否则如果 IPC_NOWAIT 被设置,则立即出错返回并将 errno 设置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:

        B1) semval 变为 0。
                B2) 信号量被删除。(将导致 semop( )出错退出,错误码为 EIDRM)
                B3) 收到信号。(将导致 semop( )出错退出,错误码为 EINTR)
        C) 当 sem_op 小于 0 时:进行 P 操作,即信号量元素的值(semval)将会被减去 sem_op 的绝对值。如果 semval 大于或等于 sem_op 的绝对值,则 semop( )立即成功返回,semval 的值将减去 sem_op 的绝对值,并且如果 SEM_UNDO 被设置了,那么该 P 操作将会被系统记录。如果 semval 小于 sem_op 的绝对值并且设置了IPC_NOWAIT,那么 semop( )将会出错返回且将错误码置为 EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:
                C1) semval 的值变得大于或者等于 sem_op 的绝对值。

        C2) 信号量被删除。(将导致 semop( )出错退出,错误码为 EIDRM

        C3) 收到信号。(将导致 semop( )出错退出,错误码为 EINTR)

44c3e7c2abc540c0dbfd1ba12f247ab1.png

38841cf4b28f25e768cbb804638fbf58.png

3代码展示

sem_p.c(进行减操作) 

#include #include #include #include #include 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) */           };int main(void){  key_t key;  int sem_id;  int retval;  //获取一个IPC对象  key = ftok(".", 1);  if(key == -1)  {    perror("获取IPC对象的key值出错");    goto get_ipc_key_err;  }  //获得信号量ID,其中如果信号量没有则创建出来,1代表创建1个信号量  sem_id = semget( key, 1, IPC_CREAT|0644);  if(sem_id == -1)  {    perror("获取信号量出错");    goto get_semid_err;  }  union semun semarg;//这个是semctl函数说明的,如果第四个参数需要定义的一个共用体  semarg.val = 0;//设置信号量初值为0  /*    设置信号量初值    sem_id:设置的信号量ID    0:设置这个信号量当中的第几个信号量    SETVAL:命令,设置单个信号量的初值    semarg:设置的共用体  */  semctl(sem_id, 0, SETVAL, semarg);  struct sembuf op_sem;  op_sem.sem_num = 0;//第几个信号量  op_sem.sem_op = -1;//-1操作  op_sem.sem_flg = 0;//按照默认的信号量操作(当减到即将成为一个负数的时候陷入睡眠)  //进行减操作  /*    sem_id:操作的信号量    op_sem:做何种操作    1:一次性操作1个信号量  */  semop( sem_id, &op_sem, 1);    printf("减操作成功\n");    //删除整个信号量集合  //semctl(sem_id, 0, IPC_RMID);  return 0;get_semid_err:get_ipc_key_err:  return -1;}

sem_v.c(进行加操作) 

#include #include #include #include #include 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) */           };int main(void){  key_t key;  int sem_id;  int retval;  //获取一个IPC对象  key = ftok(".", 1);  if(key == -1)  {    perror("获取IPC对象的key值出错");    goto get_ipc_key_err;  }  //获得信号量ID,其中如果信号量没有则创建出来,1代表创建1个信号量  sem_id = semget( key, 1, IPC_CREAT|0644);  if(sem_id == -1)  {    perror("获取信号量出错");    goto get_semid_err;  }  struct sembuf op_sem;  op_sem.sem_num = 0;//第几个信号量  op_sem.sem_op = 1;//+1操作  op_sem.sem_flg = 0;//按照默认的信号量操作(当减到即将成为一个负数的时候陷入睡眠)  //进行加操作  /*    sem_id:操作的信号量    op_sem:做何种操作    1:一次性操作1个信号量  */  semop( sem_id, &op_sem, 1);    //删除整个信号量集合  //semctl(sem_id, 0, IPC_RMID);  return 0;get_semid_err:get_ipc_key_err:  return -1;}

c8e8a12e89c7fc1238af7b891d0d79a9.png

记录 点点滴滴的笔记 欢迎关注,共同学习

小浩笔记

20bbbcc8124fd3cf845135a5b14fe258.png c74a55e7c73e607e17fe4087d4d53844.gif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值