zoukankan      html  css  js  c++  java
  • Linux 进程间通信

    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<sys/sem.h>
      
    • 函数原型

        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<sys/sem.h>
      
    • 函数原型

        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 <sys/sem.h>
      
    • 函数原型

        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 <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <signal.h>
    
    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
    ", semctl(sem_id, 0, GETVAL));
        printf("space init is %d
    ", 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!
    ");
            exit(EXIT_FAILURE);
        }
    
        printf("This is producer:
    ");
    
        while(1)
        {
            alarm(0);   //cancel the previous alarm
            alarm(10);  //set the 10s , if timeout, the process
    
            printf("Before produce:
    ");
            printf("	The produce is %d
    ", semctl(sem_id, 0, GETVAL));
            printf("	The space is %d
    ", semctl(sem_id, 1, GETVAL));
    
            printf("
    
    Now, producing...
    
    ");
    
            semop(sem_id, &sops[0], 1);
            semop(sem_id, &sops[1], 1);
    
            printf("After produce:
    ");
            printf("	The produce is %d
    ", semctl(sem_id, 0, GETVAL));
            printf("	The space is %d
    ", semctl(sem_id, 1, GETVAL));
    
            sleep(2);
        }
    
        del();
    
        return 0;
    }
    
    
    
    1. customer.c
    /*
    * Filename: producer.c
    */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/types.h>
    #include <signal.h>
    
    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!
    ");
            exit(EXIT_FAILURE);
        }
    
        printf("This is customer:
    ");
    
        while(1)
        {
            alarm(0);   //cancel the previous alarm
            alarm(10);  //set the 10s , if timeout, the process
            printf("Before consume:
    ");
            printf("	The produce is %d
    ", semctl(sem_id, 0, GETVAL));
            printf("	The space is %d
    ", semctl(sem_id, 1, GETVAL));
    
            printf("
    
    Now, consuming...
    
    ");
    
            semop(sem_id, sops, 2);
    
            printf("After consume:
    ");
            printf("	The produce is %d
    ", semctl(sem_id, 0, GETVAL));
            printf("	The space is %d
    ", semctl(sem_id, 1, GETVAL));
    
            sleep(3);
        }
    
        del();
    
        return 0;
    }
    
    
  • 相关阅读:
    详解著名的awk Oneliner,第三部分:选择性输出特定行
    显示特定行
    nWave指令
    make 命令
    VIM脚本变量
    terninal 快捷键
    vim 实现begin end 配对 使用matchit插件
    VIM删除重复行
    linux命令--find(1)
    zabbix--创建触发器
  • 原文地址:https://www.cnblogs.com/Jimmy1988/p/7698293.html
Copyright © 2011-2022 走看看