进程间通信(三):信号量 semget()、semop()、semctl()-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 12830
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2022-11-22 23:41
个人简介

将分享技术博文作为一种快乐,提升自己帮助他人

文章分类

(14)

  • (1)
  • (2)
  • (6)
  • (5)
  • (0)
文章存档

(9)

(5)

我的朋友
最近访客
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: linux

2023-04-07 22:46:02

一、什么是信号量

多个线程同一时刻对共享资源的读写会引起一些问题,需要通过一种机制,使得在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。信号量就是这种机制中的一种,其可用来调协进程对共享资源的访问的。信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即p(信号变量))和发送(即v(信号变量))信息操作。一种常见的信号量是二进制信号量,其值只能为0和1。而取多个正整数值的信号量,称为通用信号量。

二、信号量的工作原理

信号量只能进行两种操作等待和发送信号,即p(sv)和v(sv),其行为分别如下:
p(sv): 如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
,进入等待信号量状态;
v(sv): 如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给信号量加1。
例如:两个进程共享一个二进制信号量sv,一旦其中一个进程执行了p(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行p(sv)时,sv为0,它会被挂起以等待{banned}中国第一个进程离开临界区域并执行v(sv)释放信号量,这时第二个进程就可以恢复执行。

三、linux的信号量机制

linux提供了一套信号量操作接口,其支持通用信号量,二进制信号量是其特殊情况。下面分别认识下这些接口。
1、semget()函数
该函数作用为根据key值创建一个信号量或获得一个已有的信号量,其原型如下:

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/ipc.h>
  3. #include <sys/sem.h>

  4. int semget(key_t key, int nsems, int semflg);
{banned}中国第一个参数key:唯一非0,通过该key值创建一个信号量标识符。不同进程使用相同的key值,通过semget()得到信号量标识符,其他操作函数均通过该标识符操作该信号量。
第二个参数num_sems:指定需要的信号量数目,它的值几乎总是1。
第三个参数sem_flags:是一组标志,其低9bit指定了键值对象操作的用户权限,其指定方式和含义与open()函数类似。此外,还可指定ipc_creat和ipc_excl,来控制信号量对象已存在时,调用该接口的行为方式。
返回值:成功返回正整数,失败返回-1.

2、semop()函数

该函数的作用是改变信号量的值,其定义如下:

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/ipc.h>
  3. #include <sys/sem.h>

  4. int semop(int semid, struct sembuf *sops, size_t nsops);
semid: 为通过semget()创建的信号量标识符。
sembuf结构定义如下:

点击(此处)折叠或打开

  1. struct sembuf {
  2.     unsigned short sem_num; /* semaphore number */
  3.     short sem_op; /* semaphore operation */
  4.     short sem_flg; /* operation flags */
  5. }
sem_num:若为二进制信号量,它为0。
sem_op: 信号量在依次操作需要改变的数据,通常为两个数。-1:即p等待操作 1,即发送信号操作。
sem_flg可为ipc_nowait和sem_undo。设置操作为sem_undo,使操作系统跟踪信号,并在进程没有释放信号量而终止时,操作系统释放信号量。,

3、semctl()函数
函数原型如下:

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/ipc.h>
  3. #include <sys/sem.h>

  4. int semctl(int semid, int semnum, int cmd, ...);
该函数用来直接控制信号量信息。
semid: 为通过semget()创建的信号量标识符。
semnum:指定集合中的第semnum个信号量。
cmd: 通过cmd指定控制操作。函数有三个还是四个变量,取决于cmd。
当有四个变量时,第四个入参为union semun类型。定义如下:

点击(此处)折叠或打开

  1. union semun {
  2.    int val; /* value for setval */
  3.    struct semid_ds *buf; /* buffer for ipc_stat, ipc_set */
  4.    unsigned short *array; /* array for getall, setall */
  5.    struct seminfo *__buf; /* buffer for ipc_info
  6.                              (linux-specific) */
  7. }
struct semid_ds的定义如下:

点击(此处)折叠或打开

  1. struct semid_ds {
  2.    struct ipc_perm sem_perm; /* ownership and permissions */
  3.    time_t sem_otime; /* last semop time */
  4.    time_t sem_ctime; /* last change time */
  5.    unsigned long sem_nsems; /* no. of semaphores in set */
  6. };
cmd通常为以下两个值:
setval: 用来把信号量初始化一个信号量的值,即对信号的量的初始化。
ipc_rmid: 用于删除一个信号量标识符。

四、进程信号量通信测试

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/ipc.h>
  3. #include <sys/sem.h>

  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <fcntl.h>
  7. #include <stdlib.h>
  8. #include <stdio.h>

  9. union semun
  10. {
  11.     int val;
  12.     struct semid_ds *buf;
  13.     unsigned short *arry;
  14. };


  15. int set_semval(int sem_id)
  16. {
  17.     union semun sem_union;
  18.     int ret;

  19.     sem_union.val = 1; // 二进制信号量
  20.     return semctl(sem_id, 0, setval, sem_union);
  21. }

  22. static void del_sem(int sem_id)
  23. {
  24.     union semun sem_union;

  25.     semctl(sem_id, 0, ipc_rmid, sem_union);
  26. }

  27. int sem_p(int sem_id)
  28. {
  29.     // 等待获取信号量,值-1
  30.     struct sembuf sem_b;

  31.     sem_b.sem_num = 0;
  32.     sem_b.sem_op = -1;
  33.     sem_b.sem_flg = sem_undo;

  34.     return semop(sem_id, &sem_b, 1);
  35. }

  36. int sem_v(int sem_id)
  37. {
  38.     // 释放信号量,发送信号
  39.     struct sembuf sem_b;

  40.     sem_b.sem_num = 0;
  41.     sem_b.sem_op = 1;
  42.     sem_b.sem_flg = sem_undo;

  43.     return semop(sem_id, &sem_b, 1);
  44. }

  45. int main(int argc, char *argv[])
  46. {
  47.     char process_log = 's';
  48.     int sem_id;
  49.     key_t key;

  50.     key = ftok(argv[0], 0x18);
  51.     if (key == -1) {
  52.         fprintf(stderr, "failed to get key.\n");
  53.         goto out;
  54.     }

  55.     sem_id = semget(key, 1, ipc_creat | 0666);
  56.     if (sem_id == -1) {
  57.         fprintf(stderr, "failed to get sem object.\n");
  58.         goto out;

  59.     }

  60.     // 带参数的为主进程,负责初始化信号量
  61.     if (argc > 1) {
  62.         if (set_semval(sem_id) == -1)
  63.             goto out;

  64.         process_log = argv[1][0];
  65.         sleep(2);
  66.     }

  67.     for (int i = 0; i < 10; i) {
  68.         if (sem_p(sem_id) == -1)
  69.             goto out;
  70.         printf("%c", process_log);

  71.         fflush(stdout);
  72.         sleep(1);
  73.         fflush(stdout);

  74.         if (sem_v(sem_id) == -1)
  75.             goto out;
  76.         sleep(1);
  77.     }

  78.     printf("\n%d - finished\n", getpid());

  79.     if (argc > 1) {
  80.         sleep(3);
  81.         del_sem(sem_id);
  82.     }

  83.     return 0;

  84. out:
  85.     return -1;
  86. }

带有参数的进程为{banned}中国第一个进程,负责初始化信号量为二进制信号量。不带参数的进程打印s,直接使用信号量。结果运行如下:



六、信号量的总结

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即p(信号变量))和发送(即v(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。



阅读(734) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图