博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux 进程间通信 - 信号量
阅读量:6336 次
发布时间:2019-06-22

本文共 7139 字,大约阅读时间需要 23 分钟。

0. 前言

   进程是一个独立的资源管理单元,不同进程间的资源是独立的,不能在一个进程中访问另一个进程的用户空间和内存空间。但是,进程不是孤立的,不同进程之间需要信息的交互和状态的传递,因此需要进程间数据的传递、同步和异步的机制。

    当然,这些机制不能由哪一个进程进行直接管理,只能由操作系统来完成其管理和维护,Linux提供了大量的进程间通信机制,包括同一个主机下的不同进程和网络主机间的进程通信,如下图所示:

mark

  • 同主机间的信息交互

    • 无名管道
      特点:多用于亲缘关系进程间通信,方向为单向;为阻塞读写;通信进程双方退出后自动消失
      问题:多进程用同一管道通信容易造成交叉读写的问题
    • 有名管道
      FIFO(First In First Out),方向为单向(双向需两个FIFO),以磁盘文件的方式存在;通信双方一方不存在则阻塞
    • 消息队列
      可用于同主机任意多进程的通信,但其可存放的数据有限,应用于少量的数据传递
    • 共享内存
      可实现同主机任意进程间大量数据的通信,但多进程对共享内存的访问存在着竞争
  • 同主机进程间同步机制:信号量(Semaphore)
  • 同主机进程间异步机制:信号(Signal)
  • 网络主机间数据交互:Socket(套接字)

1. 信号量的原理

所谓信号量,主要用来实现进程间同步,避免并发访问共享资源;同时,信号量也可以标记资源的个数。

1). 信号量集合 - semid_ds

/*  come from /usr/include/linux/sem.h  */struct semid_ds {    struct ipc_perm sem_perm;       /* permissions .. see ipc.h */    __kernel_time_t sem_otime;      /* last semop time */    __kernel_time_t sem_ctime;      /* last change time */    struct sem  *sem_base;      /* ptr to first semaphore in array */    struct sem_queue *sem_pending;      /* pending operations to be processed */    struct sem_queue **sem_pending_last;    /* last pending operation */    struct sem_undo *undo;          /* undo requests on this array */    unsigned short  sem_nsems;      /* no. of semaphores in array */};struct sem{    int semval;     /*信号量的值*/    int sempid;     /*最近一次操作的进程PID*/}

2). 信号量系统信息 - struct seminfo

/*  come from /usr/include/linux/sem.h  */struct  seminfo {   int semmap;  /* Number of entries in semaphore map; unused within kernel */   int semmni;  /* Maximum number of semaphore sets */   int semmns;  /* Maximum number of semaphores in all semaphore sets */   int semmnu;  /* System-wide maximum number of undo structures; unused within kernel */   int semmsl;  /* Maximum number of semaphores in a set */   int semopm;  /* Maximum number of operations for semop(2) */   int semume;  /* Maximum number of undo entries per process; unused within kernel */   int semusz;  /* Size of struct sem_undo */   int semvmx;  /* Maximum semaphore value */   int semaem;  /* Max. value that can be recorded for semaphore adjustment (SEM_UNDO) */};

3). 信号量设置的联合体 - union semun

针对信号量的不同操作,semctl的第四个参数有所不同,为了方便起见,一般我们使用一个联合体对所有的可能性进行封装(自行封装,系统无此联合体

union semun{    int value;    struct semid_ds *buf;    unsigned short *array;};

4). 函数semop参数结构体 - sembuf

可通过该结构体实现信号量的 P(get)、V(release) 操作,具体看sem_op的值为正(v)还是负(P)

/*  come from /usr/include/linux/sem.h  *//* semop system calls takes an array of these. */struct sembuf {    unsigned short  sem_num;    /* semaphore index in array */    short       sem_op;     /* semaphore operation */ 信号量操作:正数,增加信号量的值;负数,减少信号量的值    short       sem_flg;    /* operation flags */ 包括:1.IPC_NOWAIT: 阻塞立刻返回;2.SEM_UNDO: 进程退出后,该进程对sem的操作将会撤销};

2. 信号量的操作

1). 创建信号量集合 - semget

  • 作用
    创建一个信号量的集合
  • 头文件

    #include
  • 函数原型

    int semget(key_t key, int nsems, int semflg)
  • 参数

    • key: 有函数ftok产生的返回值
    • nsems: 创建的信号量的个数,以数组的形式存储
    • semflg: 信号量集合的权限,最终值为 perm & ~umask
    Macro No. Description
    IPC_CREAT 00001000 若key不存在,这创建
    IPC_EXCL 00002000 若key存在,返回失败
    IPC_NOWAIT 00004000 若需要等待,直接返回错误
  • 返回值

    成功: 返回sem id
    失败: -1

2). 控制信号量(集合) - semctl

  • 作用
    对信号量集合或者对信号量集合中的某个或者某几个信号进程操作
  • 头文件

    #include
  • 函数原型

    int semctl(int semid, int semnum, int cmd, ...)
  • 参数

    • semid : 由semget函数返回的semaphore id 号
    • semnum : 集合中,信号量处于信号量集合数组的下标
    • cmd: 要执行的操作(以宏的形式出现)
    Macro No. Description Return Argument 4
    IPC_RMID 0 删除 以下皆针对整个信号量集合,/usr/include/linux/ipc.h
    IPC_SET 1 设置ipc_perm参数 struct semid_ds
    IPC_STAT 2 获取ipc_perm参数 struct semid_ds
    IPC_INFO 3 获取系统信息 struct seminfo
    GETPID 11 获取信号量拥有者的PID 以下皆针对某个信号量 /usr/include/linux/sem.h
    GETVAL 12 获取信号量的值,函数返回信号的值 当前信号量的值:进程数; 失败:-1
    GETALL 13 获取所有信号量的值 成功:0; 失败:-1 数组地址
    GETNCNT 14 获取等待信号量递增的进程数 成功:进程数; 失败:-1
    GETZCNT 15 获取等待信号量值递减的进程数 成功:进程数; 失败:-1 int value (信号量的值)
    SETVAL 16 设置信号量的值,设置的值在第四个参数中 成功:0; 失败:-1
    SETALL 17 设置所有信号量的值 成功:0; 失败:-1 数组地址
  • 返回值

    成功: 0
    失败:-1

3). 信号量操作 - semop

  • 作用
    操作信号量的集合
  • 头文件

    #include 
  • 函数原型

    int semop(int semid, struct sembuf *sops, unsigned nsops)
  • 参数

    • semid: 信号量集合ID(由semget返回)
    • sops: struct sembuf结构体变量,见上面
    • nsops: 进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
  • 返回值

    成功: 0
    失败:-1

3. 示例代码

生产者/消费者问题,经典PV问题,具体过程如下图所示:

mark
本例代码分为两个文件:

  1. customer.c :
  2. producer.c :
  1. producer.c
/** Filename: producer.c*/#include 
#include
#include
#include
#include
#include
#include
#include
int sem_id;int init(void){ key_t key; unsigned short int sem_array[2]; //for semaphore set, [0] for produce, [1] for space union semun //used for function semop() { int val; struct semid_ds *buf; unsigned short *array; }arg; key = ftok(".", 0xFF); if(-1 == key) { perror("ftok"); return -1; } sem_id = semget(key, 2, IPC_CREAT|0644); if(-1 == sem_id) { perror("semget"); return -1; } sem_array[0] = 0; sem_array[1] = 100; arg.array = sem_array; if(-1 == (semctl(sem_id, 0, SETALL, arg))) { perror("semctl"); return -1; } printf("produce init is %d\n", semctl(sem_id, 0, GETVAL)); printf("space init is %d\n", semctl(sem_id, 1, GETVAL)); return 0;}void del(){ semctl(sem_id, 0, IPC_RMID);}int main(){ struct sembuf sops[2]; //for function semop() sops[0].sem_num = 0; sops[0].sem_op = 1; //increase 1 sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = -1; //decrease 1 sops[1].sem_flg = 0; signal(SIGALRM, NULL); if(-1 == init()) { fprintf(stderr,"init error!\n"); exit(EXIT_FAILURE); } printf("This is producer:\n"); while(1) { alarm(0); //cancel the previous alarm alarm(10); //set the 10s , if timeout, the process printf("Before produce:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); printf("\n\nNow, producing...\n\n"); semop(sem_id, &sops[0], 1); semop(sem_id, &sops[1], 1); printf("After produce:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); sleep(2); } del(); return 0;}
  1. customer.c
/** Filename: producer.c*/#include 
#include
#include
#include
#include
#include
#include
int sem_id;int init(void){ key_t key; key = ftok(".", 0xFF); sem_id = semget(key, 2, IPC_CREAT|0644); if(-1 == sem_id) { perror("semget"); return -1; } return 0;}void del(){ semctl(sem_id, 0, IPC_RMID);}int main(){ struct sembuf sops[2]; //for function semop() sops[0].sem_num = 0; sops[0].sem_op = -1; //increase 1 sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = 1; //decrease 1 sops[1].sem_flg = 0; signal(SIGALRM, NULL); if(-1 == init()) { fprintf(stderr,"init error!\n"); exit(EXIT_FAILURE); } printf("This is customer:\n"); while(1) { alarm(0); //cancel the previous alarm alarm(10); //set the 10s , if timeout, the process printf("Before consume:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); printf("\n\nNow, consuming...\n\n"); semop(sem_id, sops, 2); printf("After consume:\n"); printf("\tThe produce is %d\n", semctl(sem_id, 0, GETVAL)); printf("\tThe space is %d\n", semctl(sem_id, 1, GETVAL)); sleep(3); } del(); return 0;}

转载于:https://www.cnblogs.com/Jimmy1988/p/7698293.html

你可能感兴趣的文章
持续集成(CI)- 几种测试的区别(摘录)
查看>>
多用户虚拟Web3D环境Deep MatrixIP9 1.04发布
查看>>
求高手,求解释
查看>>
[MSSQL]NTILE另类分页有么有?!
查看>>
winform datagridview 通过弹出小窗口来隐藏列 和冻结窗口
查看>>
Jquery闪烁提示特效
查看>>
最佳6款用于移动网站开发的 jQuery 图片滑块插件
查看>>
C++ String
查看>>
获取系统托盘图标的坐标及文本
查看>>
log4j Test
查看>>
HDU 1255 覆盖的面积(矩形面积交)
查看>>
SQL数据库无法附加,提示 MDF" 已压缩,但未驻留在只读数据库或文件组中。必须将此文件解压缩。...
查看>>
第二十一章流 3用cin输入
查看>>
在workflow中,无法为实例 ID“...”传递接口类型“...”上的事件“...” 问题的解决方法。...
查看>>
获取SQL数据库中的数据库名、所有表名、所有字段名、列描述
查看>>
Orchard 视频资料
查看>>
简述:预处理、编译、汇编、链接
查看>>
调试网页PAIP HTML的调试与分析工具
查看>>
路径工程OpenCV依赖文件路径自动添加方法
查看>>
玩转SSRS第七篇---报表订阅
查看>>