zoukankan      html  css  js  c++  java
  • Linux进程间通信(System V) --- 信号量

    信号量 IPC 原理

    信号量通信机制主要用来实现进程间同步,避免并发访问共享资源。信号量可以标识系统可用资源的个数。最简单的信号量为二元信号量

    下图为 Linux 信号量通信机制的概念图。在实际应用中,两个进程通信可能会使用多个信号量,因此,Linux 在管理时以信号量集合的概念来管理。

    通常所说的创建一个信号量实际上是创建了一个信号量集合,在这个信号量集合中,可能有多个信号量。整个信号量集合由以下部分组成。

    1.信号量集合数据结构:在此数据结构中定义了整个信号量集合的基本属性,如访问权限。
    
    2.信号量:信号量集合使用指针指向一个由数组构成的信号量单元,在此信号量单元中存储了各信号量的值。
    

    信号量集合的数据结构定义如下:

    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 最近semop时间 */
        __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 undo队列 */
        unsigned short  sem_nsems;      /* no. of semaphores in array 信号量数 */
    };
    

    信号量的数据结构定义如下:

    from /usr/src/kernels/xxx/include/linux/sem.h
    xxx 为 uname -r 命令所得
    
    struct sem {
        int semval;     /* current value 信号量的值 */
        int sempid;     /* pid of last operation 最近一个操作的进程号PID */
    };
    
    Linux 信号量管理操作

    1.创建信号量集合

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    
    /*
     * 第一个参数为 key 值,一般由 ftok() 函数产生
     * 第二个参数为创建的信号量个数,以数组的方式存储
     * 第三个参数用来标识信号量集合的权限
     */
    int semget(key_t key, int nsems, int semflg);
    

    2.控制信号量集合、信号量

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    
    /* 
     * 第一个参数为要操作的信号量标识符
     * 第二个参数,如果要操作的是信号量则它是信号量的下标;如果操作集合,此参数无意义
     * 第三个参数为要执行的操作
     * 第四个参数则需根据第三个参数进行设置,其类型为 senum 的共用体
     */
    int semctl(int semid, int semnum, int cmd, ...);
    
    semun 共用体如下:
    
    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) */
    };
    

    3.信号量的操作

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    
    /*
     * 第一个参数为要操作的信号量的标识符
     * 第二个参数为 sembuf 结构体
     * 第三个参数为 sops 个数
     */
    int semop(int semid, struct sembuf *sops, unsigned nsops);
    
    sembuf 结构体如下:
    
    struct sembuf {
        unsigned short  sem_num;    /* semaphore index in array 信号量下标 */
        short       sem_op;         /* semaphore operation 信号量操作 */
        short       sem_flg;        /* operation flags 操作标识 */
    };
    
    sem_flg 为操作标识。可选为以下各值:
    IPC_NOWAIT:在对信号量集合的操作不能执行的情况下,调用立即返回。
    SEM_UNDO:当进程退出后,该进程对 sem 进行的操作将被撤销。
    
    程序实例

    下面用一个程序来演示 SEM_UNDO 的效果:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    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 */
    }; 
    
    static void sem_init(int id)
    {
    	union semun sem;
    
    	sem.val = 10;
    
        /* 初始化信号量的值 */
    	semctl(id, 0, SETVAL, sem);
    }
    
    static void sem_v(int id)
    {
    	struct sembuf buf = {
    		.sem_num = 0,
    		.sem_op = -1,
    		/* 可改为 SEM_UNDO 查看结果 */
    	//	.sem_flg = 0,
    		.sem_flg = SEM_UNDO,
    	};
    
        /* 操作信号量 */
    	semop(id, &buf, 1);
    }
    
    static int get_val(int id)
    {
        /* 获取信号量的值 */
    	return semctl(id, 0, GETVAL);
    }
    
    int main()
    {
    	int sem_id, pid;
    
        /* 创建信号量集合 */
    	sem_id = semget((key_t)1004, 1, IPC_CREAT | 0600);
    
    	sem_init(sem_id);
    
    	if((pid = fork()) == -1){
    		perror("fork Err");
    		exit(0);
    	}
    	else if(!pid){
    		sem_v(sem_id);		
    
    		printf("child : %d 
    ", get_val(sem_id));
    	}
    	else{
    		sleep(1);
    
    		printf("parent : %d 
    ", get_val(sem_id));
    }
    
    	return 0;
    }
    

    如果未采用 SEM_UNDO 标识,子进程输出 9 ,父进程输出 9;

    如果采用了 SEM_UNDO 标识,子进程输出 9,父进程输出 10。

  • 相关阅读:
    VINS_Fusion 框架
    VINS_Fusion 前端源码解析
    堆与优先队列
    LSD-SLAM简介
    直接法和特征点法的区别与优缺点
    CV::Mat介绍
    C++ 位运算
    OPENCV重要函数
    C++ 优先队列
    特征点法的巅峰之作—ORBSLAM2
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/8424641.html
Copyright © 2011-2022 走看看