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

    春节过去了,真的过去一年了。在公司待了快一年了。2016希望自己变得越来越好。

     ps:上面那句话是年前写的,中间隔了那么久,自己也变懒了。

    一、信号量

    1,信号量本质是一个计数器,控制访问共享资源的最大并行进程总数。(和信号有很大的区别)

    2,信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
       信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

    3,信号量分类:Linux提供两种信号量:
    (1) 内核信号量,由内核控制路径使用
    (2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEMV信号量。
       POSIX信号量又分为有名信号量和无名信号量。
        有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。无名信号量,其值保存在内存中。

    干货来源:  http://blog.csdn.net/qinxiongxu/article/details/7830537

    4,最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量

      而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

    5,使用方法

      使用时给其一个初始值,假如该资源允许同时两个进程使用,初始值就设置为2,有一个进程使用该资源计数-1(原子操作),有一个进程放弃使用该资源计数+1(原子操作)。如果计数为0,不允许新的进程来访问资源,新的进程阻塞等待,直到计数重新大于0解除阻塞。
      如果有多个资源需要控制访问,就需要多个信号量,把多个信号量存入数组中,这个数组就叫信号量集。

    二,编程实现

    参考: http://blog.csdn.net/ljianhui/article/details/10243617    其实就是用这篇博客的。

    这里用的是二进制信号量,初始值是1,最多允许1个进程获取信号量。 

    这个例子采用两个相同的程序往终端输出字符,根据命令行参数加以区分。

    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<sys/sem.h>
    
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };
    
    static int sem_id = 0;
    static int set_semvalue();
    static void del_semvalue();
    static int semaphore_p();
    static int semaphore_v();
    
    int main(int argc, char **argv)
    {
        char message = 'x';
        int i = 0;
        // 创建信号量
        sem_id = semget((key_t)1234, 1, 0666|IPC_CREAT);
    
        if(argc > 1)
        {
            // 程序第一次调用,初始化信号量
            if(!set_semvalue())
            {
                fprintf(stderr, "Failed Init semaphore
    ");
                exit(EXIT_FAILURE);
            }
    
            // 设置输出到屏幕中的信息
            message = argv[1][0];
            sleep(2);
        }
    
        for(i = 0; i < 10; i++)
        {
            if(!semaphore_p()) // 进入临界区
            {
                exit(EXIT_FAILURE);
            }
    
            printf("%c", message);
            fflush(stdout); // 清理缓冲区
            sleep(rand() % 3); // 休眠随机时间
            printf("%c", message);
            fflush(stdout); 
        
            if(!semaphore_v()) // 离开临界区
            {
                exit(EXIT_FAILURE);
            }
            sleep(rand() % 2); // 休眠随机时间
        }
    
        sleep(10);
        printf("
     %d - finished
    ", getpid());
    
        if(argc > 1)
        {
            sleep(3);
            del_semvalue();
        }
    
        exit(EXIT_SUCCESS);
    }
    
    // 初始化信号量
    static int set_semvalue()
    {
        union semun sem_union;
        sem_union.val = 1;
        if(-1 == semctl(sem_id, 0, SETVAL, sem_union))
        {
            return 0;
        }
        return 1;
    }
    
    // 删除信号量 
    static void del_semvalue()
    {
        union semun sem_union;
        if(-1 == semctl(sem_id, 0, IPC_RMID, sem_union))
        {
            fprintf(stderr, "Failed delete semphore
    ");
        }
    }
    
    // 对信号量-1操作,即等待P(sv)
    static int semaphore_p()
    {
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1; // P()
        sem_b.sem_flg = SEM_UNDO;
    
        if(-1 == semop(sem_id, &sem_b, 1))
        {
            fprintf(stderr, "Failed semaphore_p()
    ");
            return 0;
        }
    
        return 1;
    }
    
    // 释放操作, +1, 发送信号V(sv)
    static int semaphore_v()
    {
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1; // P()
        sem_b.sem_flg = SEM_UNDO;
    
        if(-1 == semop(sem_id, &sem_b, 1))
        {
            fprintf(stderr, "Failed semaphore_v()
    ");
            return 0;
        }
    
        return 1;
    }

    运行结果:

    分析:第一次运行 一个程序打印 X,另一个打印 1。

    第二次运行 一个打印1  , 一个打印2。

    因为每个程序都在其进入临界区后和离开临界区前打印一个字符,所以每个字符都应该成对出现。

    一个进程在打印时,会先执行P操作,若没有打印完,也就是没有执行V操作。另一个进程要执行打印,也要进行P操作,这时候由于信号量的值为0,获取信号量失败,进程只能挂起自己。等另一个程序释放(V操作)才能打印。

     任何时刻只有一个进程得到了信号量,只有一个进程在执行打印

    总结:

    信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。

    我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,

    也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的

  • 相关阅读:
    深入了解 Flink 网络栈(二):监控、指标和处理背压
    物联网安全技术提高区块链应用数据的可信度
    威胁快报|Bulehero挖矿蠕虫升级,PhpStudy后门漏洞加入武器库
    Ververica Platform-阿里巴巴全新Flink企业版揭秘
    重磅 | 阿里云与MongoDB达成战略合作,成为全球唯一提供最新版MongoDB的云厂商
    阿里巴巴叶军:政企数字化转型,现在是最重要的时机
    Canonical 开源 MicroK8 | 云原生生态周报 Vol. 25
    nyoj42——连通图加欧拉(连通图板子)dfs
    nyoj38——最小生成树
    nyoj20——有向无环图深搜模板
  • 原文地址:https://www.cnblogs.com/xcywt/p/5199216.html
Copyright © 2011-2022 走看看