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。

  • 相关阅读:
    Leetcode 238. Product of Array Except Self
    Leetcode 103. Binary Tree Zigzag Level Order Traversal
    Leetcode 290. Word Pattern
    Leetcode 205. Isomorphic Strings
    Leetcode 107. Binary Tree Level Order Traversal II
    Leetcode 102. Binary Tree Level Order Traversal
    三目运算符
    简单判断案例— 分支结构的应用
    用switch判断月份的练习
    java基本打印练习《我行我素购物系统》
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/8424641.html
Copyright © 2011-2022 走看看