开发者社区> xcywt> 正文

Linux进程间通信之信号量

简介: 春节过去了,真的过去一年了。在公司待了快一年了。2016希望自己变得越来越好。  ps:上面那句话是年前写的,中间隔了那么久,自己也变懒了。   一、信号量 1,信号量本质是一个计数器,控制访问共享资源的最大并行进程总数。
+关注继续查看

春节过去了,真的过去一年了。在公司待了快一年了。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\n");
            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("\n %d - finished\n", 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\n");
    }
}

// 对信号量-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()\n");
        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()\n");
        return 0;
    }

    return 1;
}

运行结果:

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

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

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

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

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

 

总结:

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

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

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
14278 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
29153 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20695 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23580 0
linux 之进程间通信-------------InterProcess Communication
  进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间 通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。
1052 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
14900 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
36448 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
20910 0
Linux系统编程-进程间通信(管道)
标准流管道像文件操作有标准io流一样,管道也支持文件流模式。用来创建连接到另一进程的管道popen和pclose。
41 0
+关注
xcywt
C++ 机器人行业
80
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载