信号量 订阅
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。 展开全文
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。
信息
中文名称
信号量
作    用
两个或多个关键代码不被并发调用
别    名
信号灯
外文名称
Semaphore
要    求
线程必须获取一个信号量
类    型
计算机、电子
信号量描述
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
收起全文
精华内容
参与话题
问答
  • 信号量

    万次阅读 2017-03-10 20:44:41
    一、首先:我们要知道信号量是什么?  信号量的本质是数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中...

    一、首先:我们要知道信号量是什么?

           信号量的本质是数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。

    这是书本上介绍的信号量的概念,自己的理解比较简单:

         信号量就是具有原子性的计数器,就相当于一把锁,在每个进程要访问临界资源时,必须要向信号量拿个锁”,它才能进去临界资源这个“房间”,并锁上门,不让其他进程进来,此时信号量执行P()操作,锁的数目减少了一个,所以计数器减1,;当它访问完成时,它出来,将锁还给信号量,执行V()操作,计数器加1;然后是下面的进程继续。这也体现了各个进程访问临时资源是互斥的。

    “原子性”:表示的是一件事情的两种状态,做了这件事和没做这件事;

    “计数器”:信号量通常描述的是临界资源的数目;同时信号量本身就是临界资源,它的目的也是保护临界资源,解决数                 据不一致问题;

    “临界资源”:不同进程可以看到的那份共同的资源;

    “临界区”:多个进程访问临界资源的代码;

    “互斥”:任何时刻,只允许一个临时区访问临时资源,并且属性是原子的。


    二、我们为什么要使用信号量

         为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。


    三、写出程序实例

          让两个进程分别向显示器(linux下一切皆文件,临界资源)打印AA和BB,当没有信号量进行保护时,会出现数据混乱,例如:“AABBABAAAB...”,为了解决这一问题,我们创建信号量进行保护。打印“AA”或“BB”。

     我们要写出代码接口:

    创建信号量 creat_sems:


    key:ftok函数生成


    第一个参数_key,为整型值,是允许其他的进程访问信号量的一个整型的变量。所以的信号都是通过间接的方式获得的,运行的程序会提供一个信号的键值,系统为每一个键值赋予一个信号量,其他的处理函数只能通过对semget函数的返回值进行处理。

    第二个参数_nsems,参数表示信号量的编号,几乎总是取值为1.

    第三个参数_semflg,信号量的权限。

    打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和 IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCLIPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。


    销毁信号量destory_sems()


    功能:控制信号量的信息

    返回值:成功返回0,失败返回-1

    第一个参数semid:信号量标识码,还是通过semget来获得的,是semget函数的一个返回值

    第二个参数semnum:信号量的编号,当用到信号量数组的时候,这个编号就会起到作用

    第三个参数cmd:为允许的命令

    第四个参数是个联合体:


    初始化信号量init_sem()

    与销毁信号量所用函数相同,但参数不同


    p、v操作:


    函数功能:用户改变信号量的值.

    返回值:函数调用成功返回0,失败返回-1

    参数semid:系统分配给该信号量的一个ID号,通过semget函数的返回值来获得,也称为信号的标识码

    参数sops:一个指向信号量结构体数组的指针,信号量的结构体至少有3个成员。

    nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

    注意这里的结构体sembuf


    struct sembuf

    {

    unsigned short sem_num;操作信号在信号集中的编号,第一个信号的编号为0,第二个为1,一次后推

    short sem_op;信号资源,如果有就分配,如果为负值就等待(wait),如果正值就分配信号资源

    short sem_flg;信号操作标志,有两种状态,一个是SEM_UNDO,另一个是SEM_NOWAIT

    }

    SEM_NOWAIT//对信号的操作不能满足是,semop()函数就会阻塞,并立即返回,同时设置错误信息。

    SEM_UNDO//程序结束时(无论是否正常结束),保证信号值会被重新设为semop()调用前的值。这样做

    的目的在于避免在异常情况下结束时未将锁定资源解锁,造成该资源永远锁定。


    flag一般为0,若flag包含IPC_NOWAIT,则该操作为非阻塞操作。若flag包含SEM_UNDO,则当进程退出的时候会还原该进程的信号量操作,这个标志在某些情况下是很有用的,比如某进程做了P操作得到资源,但还没来得及做V操作时就异常退出了,此时,其他进程就只能都阻塞在P操作上,于是造成了死锁。若采取SEM_UNDO标志,就可以避免因为进程异常退出而造成的死锁。


    下面写一个小例子声明一个结构体:

     struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};

     struct sembuf sem1_opt_wait[1]={1,-1,SEM_UNDO};


        只有将 sem_flg 指定为 SEM_UNDO 标志后,semadj (所指定信号量针对调用进程的调整值)才会更新。   此外,如果此操作指定SEM_UNDO,系统更新过程中会撤消此信号灯的计数(semadj)。此操作可以随时进行---它永远不会强制等待的过程。调用进程必须有改变信号量集的权限。


    验证信号量的实例:

    comn.h文件


    comn.c文件:

    各函数接口


    test_sems测试文件


    展开全文
  • 操作系统之信号量

    万次阅读 多人点赞 2018-03-29 23:13:32
    操作系统中的信号量在解决线程之间的同步中起着非常大的作用,那么什么是信号量呢?百度百科:信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发...

      操作系统中的信号量在解决线程之间的同步中起着非常大的作用,那么什么是信号量呢?

    百度百科:信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。

    维基百科:信号量(英语:Semaphore)又称为信号量旗语,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.

    其中,信号量又分为两种:二进制信号量和计数信号量。

    信号量的概念是由荷兰计算机科学家艾兹赫尔·戴克斯特拉Edsger W. Dijkstra)发明的,广泛的应用于不同的操作系统中。在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。在linux系统中,二进制信号量(binary semaphore)又称互斥锁(Mutex)。

    其中,信号量又存在着两种操作,分别为V操作与P操作,V操作会增加信号量 S的数值,P操作会减少它。

    具体的运作方式如下:

     

    1. 初始化,给与它一个非负数的整数值。
    2. 运行 P(wait()),信号量S的值将被减少。企图进入临界区块的进程,需要先运行 P(wait())。当信号量S减为负值时,进程会被挡住,不能继续;当信号量S不为负值时,进程可以获准进入临界区块。
    3. 运行 V(又称signal()),信号量S的值会被增加。结束离开临界区块的进程,将会运行 V(又称signal())。当信号量S不为负值时,先前被挡住的其他进程,将可获准进入临界区块

    以上是维基百科对于信号量的具体解释,下面是我自己对于信号量的一个理解。

     

     

        信号量实际上就是一个值,这个值被用来解决临界区问题以及实现进程在多处理器环境下的进程同步。

        其中,两个最重要的信号量为二进制信号量和计数信号量,计数信号量可以表示为非负的整数而二进制信号量只存在0和1两个值。

        现在让我们看一下他们是怎样工作的吧。

    首先,让我们看一下这两个可以读取以及改变信号量值的操作。

    其中,p,v,操作接受一个共同的senaphore(信号量)作为参数,执行p操作时,当信号量s为0时,进行等待操作,此时进程被挂起。直到s的值不为0时,即s=1时,进程进入临界区,信号量值减1。当执行v操作时,进程执行完任务,从临界区出来,信号量s的值增加1。

     

    一些关于P,V操作的关键点:

    1.p操作也叫做wait操作,sleep操作或者down操作,而v操作也被叫做signal操作,wake-up以及up操作。

    2.这两个操作都是原子化的并且信号量总是被初始化为1。

    3.一个临界区总是被这一对p,v操作所包围从而去实现进程的同步。看下面这张图片,进程p的临界区在p操作与v操作之间。

        现在,让我们看看信号量是怎样实现多进程共同的排斥效应。下面有2个进程p1进程和p2进程以及一个被初始化值为1的信号量s。现在我们假设p1进入了临界区导致信号量s的值变成了0,之后如果p2进程想进入临界区必须等到信号量s>0,这种情况只发生在当p1进程完成了在临界区需要执行的操作后对共同的信号量执行v操作,使得s的值由0增加1变为1,从p2进程可以结束等待,成功进入临界区执行操作。这种方法成功实现了多进程的同步,让我们看下面这张图来具体的分析一下。

        上面描述的是一个值只能为0或者1的二进制信号量,另外还有一种其它的叫做计数信号量的信号量,它可以表示的值得数量超过1。现在假设有一个含有4个进程的资源队列,现在我们初始化信号量s的值为4,剩下的操作和二进制信号量一样。无论何时,进程想要使用资源需要调用p操作或者wait操作,当进程使用完资源需要调用v操作或者signal操作。如果s变为0则则进程不得不执行wait等待操作直到信号量s的值变为正数。举个栗子,假设有p1,p2,p3,p4四个进程并且它们都对信号量s执行了P操作(信号量s的值初始化为4),如果另一个进程p5想要进入临界区想要使用资源,它需要等到p1,p2,p3,p4这四个进程中的任意一个执行v操作使得信号量s的值变为正数。

    实现信号量中存在的一些问题:

        无论何时,进程在等待的过程中持续不断的去检查信号量的值(在p操作中看到while(s==0)这一语句),并且浪费cpu的周期,为了避免这种情况的发生,下面提供了一种更好的解决思路:

     

        在这种实现方法中,无论什么时候,如果进程执行p操作,这个进程都会被添加进一个与信号量相关的进程等待队列,这是通过操作系统中叫做block()的方法实现进程的入队操作的,当一个进程完成资源的使用后,它调用v操作,并调用系统方法wakeup( ),接着等待队列中的一个进程重新开始进入临界区使用资源......

     

    参考资料:

    【1】维基百科-信号量

    【2】 https://www.geeksforgeeks.org/semaphores-operating-system   by Ashish Sharma 翻译:刘扬俊

     

    博客文章版权说明

     

     

     

    第一条 本博客文章仅代表作者本人的观点,不保证文章等内容的有效性。

    第二条 本博客部分内容转载于合作站点或摘录于部分书籍,但都会注明作/译者和原出处。如有不妥之处,敬请指出。

    第三条 征得本博客作者同意的情况下,本博客的作品允许非盈利性引用,并请注明出处:“作者:____转载自____”字样,以尊重作者的劳动成果。版权归原作/译者所有。未经允许,严禁转载

    第四条 对非法转载者,“扬俊的小屋”和作/译者保留采用法律手段追究的权利

    第五条 本博客之声明以及其修改权、更新权及最终解释权均属“扬俊的小屋”。

    第六条 以上声明的解释权归扬俊的小屋所有。

     

    展开全文
  • 文章目录前言信号量的使用信号量的用法一:互斥锁信号量的用法二 : 计数信号量 此篇文章参考于:Linux系统编程—信号量 前言 信号量的使用方法与 共享内存,消息队列均不同,后者是主要方便进程间通信,而信号量的...

    系列文章:
    Linux IPC进程间通信(一):管道
    Linux IPC进程间通信(二):共享内存
    Linux IPC进程间通信(三):信号量
    Linux IPC进程间通信(四):消息队列


    此篇文章参考于:Linux系统编程—信号量

    前言

    信号量的使用方法与 共享内存,消息队列均不同,后者是主要方便进程间通信,而信号量的作用是 对共享资源的互斥访问
    当信号量的资源数量 <= 0 时,则会阻塞,直到资源数量 >0 时才开始进行操作,另外信号量的操作均为原子操作。

    信号量的主要两种用法(根据信号量初始资源数量的不同, 或者说有没有利用到信号量的资源值):

    1. 互斥锁(信号量的初始值为1)
      可以将信号量的资源数量设置为1, P操作加锁,V操作解锁,主要用于实现对公共资源的互斥访问

    2. 计数信号量(信号量的初始值 > 1)
      信号量的初始值大于1时,进行P操作进行消费, V操作进行生产,当信号量的资源值<=0 时阻塞等待,主要用于实现生产者消费者案例


    信号量的使用

    • int semget(key_t keys, int nsems, int flags)

    创建一个(或多个信号量)
    keys: 用来判断是创建新的信号量还是用已有的信号量
    nsems: 信号量的数量
    flags: 创建的权限及特性

    • int semop(int semid, struct sembuf* sops, size_t nops)

    将要进行的操作 sop 添加到信号量semid中
    semid: 信号量的标识
    sops: 添加的操作结构体
    nops: 添加操作结构体的个数,若添加的操作只有1个,那么 nops=1。

    struct sembuf* sops 有三个成员
    sem_num : 代表着要操作的信号量的下标(从0开始)
    sem_op : 要进行的操作, 一般是 1 或 -1 , 也就是 P/V操作
    sem_flg : 一般为0, 表示undo

    • int semctl(int semid, int semnum, int cmd)

    对信号量semid进行操作

    • sennum 信号量集合中的下标,一般来说只有一个信号量,也就是为 0
    • cmd 执行的命令,一般有如 GETVAL , IPC_RMID, SETVAL, SETVALL 等操作

    另外,我们通常会使用 ipcs 命令来查看信号量的使用:
    在这里插入图片描述

    通过以下示例程序可以更深入理解。

    1.针对一个信号量集合中只有一个信号量的情况

    int test1()
    {
        int ret;
        int semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
        ERROR_CHECK(semid, -1, "semget");
    
        semctl(semid, 0, SETVAL, 10); //设置信号量的初始值为10
        struct sembuf sop;
        sop.sem_num = 0; //下标为0的信号量
        sop.sem_op = 1;  //V操作
        sop.sem_flg = 0; //undo操作
        ret = semop(semid, &sop, 1);
        ERROR_CHECK(ret, -1, "semop");
        cout << semctl(semid, 0, GETVAL) << endl;
        ret = semctl(semid, 0, IPC_RMID);
        ERROR_CHECK(ret, -1, "semctl");
        return 0;
    }
    

    2.针对一个信号量集合中有多个信号量的情况

    int test2()
    {
        int ret;
        int semid = semget((key_t)0x1234, 3, 0666 | IPC_CREAT);
        ERROR_CHECK(semid, -1, "semget");
        unsigned short array[3] = {10, 20, 30}; //为三个信号量设定初始值
        semctl(semid, 0, SETALL, array);
    
        //为三个信号量分别 +1 +2 +3
        struct sembuf sop[3];
        sop[0].sem_num = 0;
        sop[0].sem_op = 1;
        sop[0].sem_flg = 0;
    
        sop[1].sem_num = 1;
        sop[1].sem_op = 2;
        sop[1].sem_flg = 0;
    
        sop[2].sem_num = 2;
        sop[2].sem_op = 3;
        sop[2].sem_flg = 0;
    
        ret = semop(semid, sop, 3);
        ERROR_CHECK(ret, -1, "semop");
        cout << "第0个信号量的值: " << semctl(semid, 0, GETVAL) << endl;
        cout << "第1个信号量的值: " << semctl(semid, 1, GETVAL) << endl;
        cout << "第2个信号量的值: " << semctl(semid, 2, GETVAL) << endl;
        semctl(semid, 0, IPC_RMID);
        return 0;
    }
    

    信号量的用法一:互斥锁

    案例:两个进程同时加一千万(在共享内存中),并查看最终值是否为两千万

    int add_and_count()
    {
        int N = 10000000;
        int shmid = shmget((key_t)0x1000, 4, 0666 | IPC_CREAT);
        ERROR_CHECK(shmid, -1, "shmget");
        int *sum = (int *)shmat(shmid, NULL, 0);
        ERROR_CHECK(sum, (int *)-1, "shmat");
        *sum = 0;
        int semid = semget((key_t)0x1234, 1, 0666 | IPC_CREAT);
        ERROR_CHECK(semid, -1, "semget");
        semctl(semid, 0, SETVAL, 1);
        struct sembuf P, V;
        P = {0, -1, 0};
        V = {0, 1, 0};
        if (fork() > 0)
        {
            for (int i = 0; i < N; i++)
            {
                semop(semid, &P, 1);
                (*sum)++;
                semop(semid, &V, 1);
            }
            wait(NULL);
            cout << "final sum = " << *sum << endl;
            semctl(semid, 0, IPC_RMID);
            shmctl(shmid, IPC_RMID, NULL);
        }
        else
        {
            for (int i = 0; i < N; i++)
            {
                semop(semid, &P, 1);
                (*sum)++;
                semop(semid, &V, 1);
            }
        }
    
        return 0;
    }
    

    信号量的用法二 : 计数信号量

    案例:生产者消费者
    一个仓库有10个空位置存放产品,生产者生产产品(没有空位子时等待),消费者消费产品(没有产品时等待)

    int producer_consumer()
    {
        int ret;
        int semid = semget((key_t)0x1234, 2, 0666 | IPC_CREAT);
        ERROR_CHECK(semid, -1, "semget");
        unsigned short array[2] = {10, 0}; //空位子,成品数
        semctl(semid, 0, SETALL, array);
        struct sembuf P1, V1, P2, V2;
        P1 = {0, -1, 0};
        P2 = {1, -1, 0};
        V1 = {0, 1, 0};
        V2 = {1, 1, 0};
    
        //生产者
        if (fork() > 0)
        {
            for (int i = 0; i < 5; i++)
            {
                semop(semid, &P1, 1);
                cout << "Producing..." << endl;
                semop(semid, &V2, 1);
                cout << "after producing, space = " << semctl(semid, 0, GETVAL) << " , production = " << semctl(semid, 1, GETVAL) << endl;
                sleep(2);
            }
            wait(NULL);
            semctl(semid, 0, IPC_RMID);
        }
        //消费者
        else
        {
            for (int i = 0; i < 5; i++)
            {
                semop(semid, &P2, 1);
                cout << "Consuming..." << endl;
                semop(semid, &V1, 1);
                cout << "after consuming, space = " << semctl(semid, 0, GETVAL) << " , production = " << semctl(semid, 1, GETVAL) << endl;
                sleep(1);
            }
        }
        return 0;
    }
    
    
    展开全文
  • 进程间通信之-信号量semaphore

    万次阅读 2018-03-23 13:41:16
    转载自: https://blog.csdn.net/gatieme/article/details/50994533信号量什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。信号量的值为正的时候,说明它空闲。所...

    转载自: https://blog.csdn.net/gatieme/article/details/50994533

     

    信号量


    什么是信号量


    信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

    信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

    为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域

    临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

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

    最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

    信号量的工作原理


    由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

    • P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行

    • V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

    举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

    信号量的分类


    在学习信号量之前,我们必须先知道——Linux提供两种信号量:

    • 内核信号量,由内核控制路径使用

    • 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量。

    POSIX信号量又分为有名信号量无名信号量 
    有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。无名信号量,其值保存在内存中。

    POSIX 信号量与SYSTEM V信号量的比较


    1. 对POSIX来说,信号量是个非负整数。常用于线程间同步。 
      而SYSTEM V信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEM V IPC服务的,信号量只不过是它的一部分。常用于进程间同步。

    2. POSIX信号量的引用头文件是<semaphore.h>,而SYSTEM V信号量的引用头文件是<sys/sem.h>

    3. 从使用的角度,System V信号量是复杂的,而Posix信号量是简单。比如,POSIX信号量的创建和初始化或PV操作就很非常方便。

    内核信号量


    Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它绝不可能在内核之外使用,它是一种睡眠锁。

    如果有一个任务想要获得已经被占用的信号量时,信号量会将其放入一个等待队列(它不是站在外面痴痴地等待而是将自己的名字写在任务队列中)然后让其睡眠。

    当持有信号量的进程将信号释放后,处于等待队列中的一个任务将被唤醒(因为队列中可能不止一个任务),并让其获得信号量。

    这一点与自旋锁不同,处理器可以去执行其它代码。

    关于 他们的不同之处,请参见

    自旋锁,Mutex和信号量的使用

    linux 自旋锁和信号量

    它与自旋锁的差异:由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况

    相反,锁被短时间持有时,使用信号量就不太适宜了,因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁占用的全部时间表还要长;

    由于执行线程在锁被争用时会睡眠,所以只能在进程上下文中才能获得信号量锁,因为在中断上下文中是不能进行调试的;持有信号量的进行也可以去睡眠,当然也可以不睡眠,因为当其他进程争用信号量时不会因此而死锁;不能同时占用信号量和自旋锁,因为自旋锁不可以睡眠而信号量锁可以睡眠。相对而来说信号量比较简单,它不会禁止内核抢占,持有信号量的代码可以被抢占。

    信号量还有一个特征,就是它允许多个持有者,而自旋锁在任何时候只能允许一个持有者。

    当然我们经常遇到也是只有一个持有者,这种信号量叫二值信号量或者叫互斥信号量。允许有多个持有者的信号量叫计数信号量,在初始化时要说明最多允许有多少个持有者(Count值) 
    信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为1就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。 
    当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加1实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。

    关于内核信号量的其他信息

    请参见

    大话Linux内核中锁机制之信号量、读写信号量

    内核信号量的构成


    内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。只有在资源被释放时,进程才再次变为可运行。 
    只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。 
    内核信号量是struct semaphore类型的对象,在内核源码中位于include\linux\semaphore.h文件

    struct semaphore
    {
       atomic_t count;
       int sleepers;
       wait_queue_head_t wait;
    }

     

    成员 描述
    count 相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源
    wait 存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中
    sleepers 存放一个标志,表示是否有一些进程在信号量上睡眠

    内核信号量中的等待队列


    上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。

    当某任务由于没有某种条件没有得到满足时,它就被挂到等待队列中睡眠。当条件得到满足时,该任务就被移出等待队列,此时并不意味着该任务就被马上执行,因为它又被移进工作队列中等待CPU资源,在适当的时机被调度。

    内核信号量是在内部使用等待队列的,也就是说该等待队列对用户是隐藏的,无须用户干涉。由用户真正使用的等待队列我们将在另外的篇章进行详解。

    内核信号量的相关函数


    这里写图片描述

    初始化


    #define __SEMAPHORE_INITIALIZER(name, n)                                \
    {                                                                       \
            .lock           = __SPIN_LOCK_UNLOCKED((name).lock),            \
            .count          = n,                                            \
            .wait_list      = LIST_HEAD_INIT((name).wait_list),             \
    }
    

     

    该宏声明一个信号量name是直接将结构体中count值设置成n,此时信号量可用于实现进程间的互斥量。

    #define DECLARE_MUTEX(name)     \
            struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

    该宏声明一个互斥锁name,但把它的初始值设置为1

    void sema_init (struct semaphore *sem, int val);

     

    该函用于数初始化设置信号量的初值,它设置信号量sem的值为val。

    #define init_MUTEX(sem)         sema_init(sem, 1)

     

    该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1。

    #define init_MUTEX_LOCKED(sem)  sema_init(sem, 0)
    

     

    该函数也用于初始化一个互斥锁,但它把信号量sem的值设置为0,即一开始就处在已锁状态。

    注意:对于信号量的初始化函数Linux最新版本存在变化,如init_MUTEX和init_MUTEX_LOCKED等初始化函数目前新的内核中已经没有或者更换了了名字等

    因此建议以后在编程中遇到需要使用信号量的时候尽量采用sema_init(struct semaphore *sem, int val)函数,因为这个函数就目前为止从未发生变化。

    获取信号量–申请内核信号量所保护的资源


    void down(struct semaphore * sem);

     

    该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文(包括IRQ上下文和softirq上下文)使用该函数。该函数将把sem的值减1,如果信号量sem的值非负,就直接返回,否则调用者将被挂起,直到别的任务释放该信号量才能继续运行。

    int down_interruptible(struct semaphore * sem);

     

    该函数功能与down类似,不同之处为,down不会被信号(signal)打断,但down_interruptible能被信号(比如Ctrl+C)打断,因此该函数有返回值来区分是正常返回还是被信号中断,如果返回0,表示获得信号量正常返回,如果被信号打断,返回-EINTR

    int down_trylock(struct semaphore * sem);

     

    该函数试着获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,表示不能获得信号量sem,返回值为非0值。因此,它不会导致调用者睡眠,可以在中断上下文使用

    释放内核信号量所保护的资源


    void up(struct semaphore * sem);

     

    该函数释放信号量sem,即把sem的值加1,如果sem的值为非正数,表明有任务等待该信号量,因此唤醒这些等待者。

    内核信号量的使用例程


    在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的 
    共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。Linux内核中 
    解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

    ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
    {
     //获得信号量
     if (down_interruptible(&sem))
     {
      return - ERESTARTSYS;
     }
     //将用户空间的数据复制到内核空间的global_var
     if (copy_from_user(&global_var, buf, sizeof(int)))
     {
      up(&sem);
      return - EFAULT;
     }
     //释放信号量
     up(&sem);
     return sizeof(int);
    }

     

    读-写信号量


    跟自旋锁一样,信号量也有区分读-写信号量之分

    如果一个读写信号量当前没有被写者拥有并且也没有写者等待读者释放信号量,那么任何读者都可以成功获得该读写信号量;

    否则,读者必须被挂起直到写者释放该信号量。如果一个读写信号量当前没有被读者或写者拥有并且也没有写者等待该信号量,那么一个写者可以成功获得该读写信号量,否则写者将被挂起,直到没有任何访问者。因此,写者是排他性的,独占性的。

    读写信号量有两种实现,一种是通用的,不依赖于硬件架构,因此,增加新的架构不需要重新实现它,但缺点是性能低,获得和释放读写信号量的开销大;另一种是架构相关的,因此性能高,获取和释放读写信号量的开销小,但增加新的架构需要重新实现。在内核配置时,可以通过选项去控制使用哪一种实现。 
    读写信号量的相关API有:

    DECLARE_RWSEM(name)
    • 1

    该宏声明一个读写信号量name并对其进行初始化。

    void init_rwsem(struct rw_semaphore *sem);
    • 1

    该函数对读写信号量sem进行初始化。

    void down_read(struct rw_semaphore *sem);
    • 1

    读者调用该函数来得到读写信号量sem。该函数会导致调用者睡眠,因此只能在进程上下文使用。

    int down_read_trylock(struct rw_semaphore *sem);
    • 1

    该函数类似于down_read,只是它不会导致调用者睡眠。它尽力得到读写信号量sem,如果能够立即得到,它就得到该读写信号量,并且返回1,否则表示不能立刻得到该信号量,返回0。因此,它也可以在中断上下文使用。

    void down_write(struct rw_semaphore *sem);
    • 1

    写者使用该函数来得到读写信号量sem,它也会导致调用者睡眠,因此只能在进程上下文使用。

    int down_write_trylock(struct rw_semaphore *sem);
    • 1

    该函数类似于down_write,只是它不会导致调用者睡眠。该函数尽力得到读写信号量,如果能够立刻获得,就获得该读写信号量并且返回1,否则表示无法立刻获得,返回0。它可以在中断上下文使用。

    void up_read(struct rw_semaphore *sem);
    • 1

    读者使用该函数释放读写信号量sem。它与down_read或down_read_trylock配对使用。如果down_read_trylock返回0,不需要调用up_read来释放读写信号量,因为根本就没有获得信号量。

    void up_write(struct rw_semaphore *sem);
    • 1

    写者调用该函数释放信号量sem。它与down_write或down_write_trylock配对使用。如果down_write_trylock返回0,不需要调用up_write,因为返回0表示没有获得该读写信号量。

    void downgrade_write(struct rw_semaphore *sem);
    • 1

    该函数用于把写者降级为读者,这有时是必要的。因为写者是排他性的,因此在写者保持读写信号量期间,任何读者或写者都将无法访问该读写信号量保护的共享资源,对于那些当前条件下不需要写访问的写者,降级为读者将,使得等待访问的读者能够立刻访问,从而增加了并发性,提高了效率。 
    读写信号量适于在读多写少的情况下使用,在linux内核中对进程的内存映像描述结构的访问就使用了读写信号量进行保护。 
    究竟什么时候使用自旋锁什么时候使用信号量,下面给出建议的方案 
    当对低开销、短期、中断上下文加锁,优先考虑自旋锁;当对长期、持有锁需要休眠的任务,优先考虑信号量。

    POSIX信号量详解


    无名信号量


    无名信号量的创建就像声明一般的变量一样简单,例如:sem_t sem_id。然后再初始化该无名信号量,之后就可以放心使用了。

    无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。

    常见的无名信号量相关函数


    int sem_init(sem_t *sem, int pshared, unsigned int value);
    • 1
    • pshared==0 用于同一多线程的同步;

    • 若pshared>0 用于多个相关进程间的同步(即由fork产生的)

    int sem_getvalue(sem_t *sem, int *sval);
    • 1

    取回信号量sem的当前值,把该值保存到sval中。 
    若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:

    • 返回0

    • 返回阻塞在该信号量上的进程或线程数目

    linux采用返回的第一种策略。

    sem_wait(或sem_trywait)相当于P操作,即申请资源。

    int sem_wait(sem_t *sem); // 这是一个阻塞的函数
    • 1

    测试所指定信号量的值,它的操作是原子的。

    • 若sem>0,那么它减1并立即返回。

    • 若sem==0,则睡眠直到sem>0,此时立即减1,然后返回。

    int sem_trywait(sem_t *sem); // 非阻塞的函数
    • 1

    其他的行为和sem_wait一样,除了: 
    若sem==0,不是睡眠,而是返回一个错误EAGAIN。

    sem_post相当于V操作,释放资源。

    int sem_post(sem_t *sem);
    • 1

    把指定的信号量sem的值加1;

    呼醒正在等待该信号量的任意线程。 
    注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数

    无名信号量在多线程间的同步


    无名信号量的常见用法是将要保护的变量放在sem_wait和sem_post中间所形成的 
    临界区内,这样该变量就会被保护起来,例如:

    #include <pthread.h>
    #include <semaphore.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int number; // 被保护的全局变量
    sem_t sem_id;
    
    void* thread_one_fun(void *arg)
    {
        sem_wait(&sem_id);
        printf("thread_one have the semaphore\n");
        number++;
    
        printf("thread_one : number = %d\n", number);
    
        sem_post(&sem_id);
    
        return NULL;
    }
    
    void* thread_two_fun(void *arg)
    {
        sem_wait(&sem_id);
        printf("thread_two have the semaphore \n");
    
        number--;
        printf("thread_two : number = %d\n", number);
    
        sem_post(&sem_id);
    
        return NULL;
    }
    int main(int argc, char *argv[])
    {
        number = 1;
        pthread_t id1, id2;
    
        sem_init(&sem_id, 0, 1);
    
        pthread_create(&id1, NULL, thread_one_fun, NULL);
        pthread_create(&id2, NULL, thread_two_fun, NULL);
    
        pthread_join(id1, NULL);
        pthread_join(id2, NULL);
    
        printf("main...\n");
    
        return 0;
    }

     

    上面的例程,到底哪个线程先申请到信号量资源,这是随机的。

    进程1先执行,进城2后执行 
    这里写图片描述

    进程2先执行,进城1后执行 
    这里写图片描述

    如果想要某个特定的顺序的话,可以用2个信号量来实现。例如下面的例程是线程1先执行完,然后线程2才继续执行,直至结束。

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int number;                 // 被保护的全局变量
    sem_t sem_id1, sem_id2;
    
    
    
    /*
     *  线程1,
     *  对sem_id1加锁(P操作)以后
     *  将number增加1
     *  同时对sem_id2进行释放,V操作
     *
     *  */
    void* thread_one_fun(void *arg)
    {
        sem_wait(&sem_id1);
        printf("thread_one have the semaphore\n");
    
        number++;
    
        printf("number = %d\n",number);
        sem_post(&sem_id2);
    
        return NULL;
    }
    
    /*
     *  线程2,
     *  对sem_id2加锁(P操作)以后
     *  将number减少1
     *  同时对sem_id1进行释放,V操作
     *
     *  */
    void* thread_two_fun(void *arg)
    {
        sem_wait(&sem_id2);
        printf("thread_two have the semaphore \n");
    
        number--;
    
        printf("number = %d\n",number);
        sem_post(&sem_id1);
    
        return NULL;
    }
    
    int main(int argc,char *argv[])
    {
        number = 1;
        pthread_t id1, id2;
    
        /*
         *  由于程序初始时, sem_id1可进入, sem_id2不可进入
         *  两个线程的动作如下:
         *  thread one P(id1)  number++ V(id2)
         *  thread two P(id2)  number-- V(id1)
         *  而id1可进入, id2不可进入
         *  因此thread one先执行
         *  如果将id1与id2的顺序交换, 则执行顺序相反
         * */
        sem_init(&sem_id1, 0, 1);   // 空闲的
        sem_init(&sem_id2, 0, 0);   // 忙的
    
        pthread_create(&id1, NULL, thread_one_fun, NULL);
        pthread_create(&id2, NULL, thread_two_fun, NULL);
    
        pthread_join(id1, NULL);
        pthread_join(id2, NULL);
    
        printf("main...\n");
    
    
        return EXIT_SUCCESS;
    }
    

     

    无名信号量在相关进程间的同步


    说是相关进程,是因为本程序中共有2个进程,其中一个是另外一个的子进程(由fork产生)的。

    本来对于fork来说,子进程只继承了父进程的代码副本,mutex理应在父子进程中是相互独立的两个变量,但由于在初始化mutex的时候,由pshared = 1指定了mutex处于共享内存区域,所以此时mutex变成了父子进程共享的一个变量。此时,mutex就可以用来同步相关进程了。

    #include <stdio.h>
    #include <stdlib.h>
    
    
    #include <semaphore.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    
    int main(int argc, char **argv)
    {
        int     fd, i;
        int     nloop = 10, zero = 0;
        int     *ptr;
        sem_t   mutex;
    
        //  open a file and map it into memory
        fd = open("log.txt", O_RDWR | O_CREAT, S_IRWXU);
    
        write(fd,&zero,sizeof(int));
    
        ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
        close(fd);
    
        /* create, initialize semaphore */
        if(sem_init(&mutex, 1, 1) < 0) //
        {
            perror("semaphore initilization");
            exit(0);
        }
    
        if (fork() == 0)
        {   /* child process*/
            for (i = 0; i < nloop; i++)
            {
                sem_wait(&mutex);
    
                printf("child: %d\n", (*ptr)++);
                //sleep(1);
                sem_post(&mutex);
            }
            exit(0);
        }
    
    
        /* back to parent process */
        for (i = 0; i < nloop; i++)
        {
            sem_wait(&mutex);
    
            printf("parent: %d\n", (*ptr)++);
            //sleep(1);
            sem_post(&mutex);
        }
        exit(0);
    }
    

     

    这里写图片描述

    有名信号量


    有名信号量的特点是把信号量的值保存在文件中。

    这决定了它的用途非常广:既可以用于线程,也可以用于相关进程间,甚至是不相关进程。

    有名信号量能在进程间共享的原因


    由于有名信号量的值是保存在文件中的,所以对于相关进程来说,子进程是继承了父进程的文件描述符,那么子进程所继承的文件描述符所指向的文件是和父进程一样的,当然文件里面保存的有名信号量值就共享了。

    有名信号量相关函数说明


    有名信号量在使用的时候,和无名信号量共享sem_wait和sem_post函数。 
    区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。

    • 打开一个已存在的有名信号量,或创建并初始化一个有名信号量。一个单一的调用就完 
      成了信号量的创建、初始化和权限的设置。
    sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
    • 1
    参数 描述
    name 文件的路径名;
    Oflag 有O_CREAT或O_CREAT
    mode_t 控制新的信号量的访问权限;
    Value 指定信号量的初始化值。

    注意:

    这里的name不能写成/tmp/aaa.sem这样的格式,因为在linux下,sem都是创建在/dev/shm目录下。你可以将name写成“/mysem”或“mysem”,创建出来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。也千万不要写“/tmp/mysem”之类的。

    当oflag = O_CREAT时,若name指定的信号量不存在时,则会创建一个,而且后面的mode和value参数必须有效。若name指定的信号量已存在,则直接打开该信号量,

    同时忽略mode和value参数。

    当oflag = O_CREAT|O_EXCL时,若name指定的信号量已存在,该函数会直接返回error。

    • 一旦你使用了一信号量,销毁它们就变得很重要。 
      在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量, 
      注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作用。

    也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。

    有名信号量在无相关进程间的同步


    前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于共享内存区,只有这样才能被无相关的进程所共享。 
    在下面这个例子中,服务进程和客户进程都使用shmgetshmat来获取得一块共享内存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。

    服务器程序

    //server.c
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <semaphore.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define SHMSZ 27
    
    char SEM_NAME[]= "vik";
    
    
    int main()
    {
        char    ch;
        int     shmid;
        key_t   key;
        char    *shm,*s;
        sem_t   *mutex;
    
        //name the shared memory segment
        key = 1000;
    
        //create & initialize semaphore
        mutex = sem_open(SEM_NAME, O_CREAT, 0644, 1);
        if(mutex == SEM_FAILED)
        {
            perror("unable to create semaphore");
            sem_unlink(SEM_NAME);
    
            exit(-1);
        }
    
        //create the shared memory segment with this key
        shmid = shmget(key, SHMSZ, IPC_CREAT | 0666);
        if(shmid < 0)
        {
            perror("failure in shmget");
    
            exit(-1);
        }
    
        //attach this segment to virtual memory
        shm = shmat(shmid, NULL, 0);
        //start writing into memory
        s = shm;
        for(ch = 'A'; ch <= 'Z'; ch++)
        {
            sem_wait(mutex);
    
            *s++ = ch;
    
            sem_post(mutex);
        }
    
        //the below loop could be replaced by binary semaphore
        while(*shm != '*')
        {
            sleep(1);
        }
    
        sem_close(mutex);
    
        sem_unlink(SEM_NAME);
    
        shmctl(shmid, IPC_RMID, 0);
    
        return EXIT_SUCCESS;
    
    }
    

    客户端程序

    // client.c
    #include <stdio.h>
    #include <stdlib.h>
    
    
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <semaphore.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define SHMSZ 27
    
    char SEM_NAME[]= "vik";
    
    int main()
    {
        int     shmid;
        key_t   key;
        char    *shm, *s;
        sem_t   *mutex;
    
        //  name the shared memory segment
        key = 1000;
    
        //  create & initialize existing semaphore
        mutex = sem_open(SEM_NAME, 0, 0644, 0);
        if(mutex == SEM_FAILED)
        {
            perror("reader:unable to execute semaphore");
            sem_close(mutex);
    
            exit(-1);
        }
    
        //  create the shared memory segment with this key
        shmid = shmget(key, SHMSZ, 0666);
        if(shmid < 0)
        {
            perror("reader:failure in shmget");
    
            exit(-1);
        }
    
        //  attach this segment to virtual memory
        shm = shmat(shmid, NULL, 0);
    
        //  start reading
        s = shm;
        for(s = shm; *s != '\0'; s++)
        {
            sem_wait(mutex);
    
            putchar(*s);
    
            sem_post(mutex);
        }
    
        //  once done signal exiting of reader:This can be replaced by another semaphore
        *shm = '*';
        sem_close(mutex);
        shmctl(shmid, IPC_RMID, 0);
    
        return EXIT_SUCCESS;
    }
    

     

    SYSTEM V信号量


    这是信号量值的集合,而不是单个信号量。相关的信号量操作函数由<sys/ipc.h>引用。

    ystem V 信号量在内核中维护,其中包括二值信号量 、计数信号量、计数信号量集。

    • 二值信号量 : 其值只有0、1 两种选择,0表示资源被锁,1表示资源可用;

    • 计数信号量:其值在0 和某个限定值之间,不限定资源数只在0 1 之间;

    • 计数信号量集 :多个信号量的集合组成信号量集

    信号量结构体


    内核为每个信号量集维护一个信号量结构体,可在

    struct semid_ds
    {
        struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */
        struct sem *sem_base; /* 某个信号量sem结构数组的指针,当前信号量集中的每个信号量对应其中一个数组元素 */
        ushort sem_nsems; /* sem_base 数组的个数 */
        time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */
        time_t sem_ctime; /* 成功创建时间 */
    };

     

    其中ipc_perm 结构是内核给每个进程间通信对象维护的一个信息结构,其成员包含所有者用户id,所有者组id、创建者及其组id,以及访问模式等;semid_ds结构体中的sem结构是内核用于维护某个给定信号量的一组值的内部结构,其结构定义:

    struct sem {
    ushort semval; /* 信号量的当前值 */
    short sempid; /* 最后一次返回该信号量的进程ID 号 */
    ushort semncnt; /* 等待semval大于当前值的进程个数 */
    ushort semzcnt; /* 等待semval变成0的进程个数 */
    };

     

    常见的SYSTEM V信号量函数


    关键字和描述符


    SYSTEM V信号量是SYSTEM V IPC(即SYSTEM V进程间通信)的组成部分,其他的有SYSTEM V消息队列,SYSTEM V共享内存。而关键字和IPC描述符无疑是它们的共同点,也使用它们,就不得不先对它们进行熟悉。这里只对SYSTEM V信号量进行讨论。

    IPC描述符相当于引用ID号,要想使用SYSTEM V信号量(或MSG、SHM),就必须用IPC描述符来调用信号量。而IPC描述符是内核动态提供的(通过semget来获取),用户无法让服务器和客户事先认可共同使用哪个描述符,所以有时候就需要到关键字KEY来定位描述符。

    某个KEY只会固定对应一个描述符(这项转换工作由内核完成),这样假如服务器和

    客户事先认可共同使用某个KEY,那么大家就都能定位到同一个描述符,也就能定位到同一个信号量,这样就达到了SYSTEM V信号量在进程间共享的目的。

    创建和打开信号量


    创建一个信号量或访问一个已经存在的信号量集。

    int semget(key_t key, int nsems, int oflag)
    • 1

    该函数执行成功返回信号量标示符,失败返回-1

    参数 描述
    key 通过调用ftok函数得到的键值
    nsems 代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0,我们一旦创建了该信号量,就不能更改其信号量个数,只要你不删除该信号量,你就是重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;
    semflg 指定该信号量的读写权限,当创建信号量时不许加IPC_CREAT ,若指定IPC_CREAT

    semget函数执行成功后,就产生了一个由内核维持的类型为semid_ds结构体的信号量集,返回semid就是指向该信号量集的引索。

    • nsems>0 : 创建一个信的信号量集,指定集合中信号量的数量,一旦创建就不能更改。

    • nsems==0 : 访问一个已存在的集合

    • 返回的是一个称为信号量标识符的整数,semop和semctl函数将使用它。

    • 创建成功后信号量结构被设置:

    .sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID
    .oflag 参数中的读写权限位存入sem_perm.mode
    .sem_otime 被置为0,sem_ctime被设置为当前时间
    .sem_nsems 被置为nsems参数的值

     

    该集合中的每个信号量不初始化,这些结构是在semctl,用参数SET_VAL,SETALL初始化的。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/sem.h>
    #include <sys/ipc.h>
    
    #define SEM_R    0400   //用户(属主)读
    #define SEM_A    0200   //用户(属主)写
    
    #define SVSEM_MODE (SEM_R | SEM_A | SEM_R>>3 | SEM_R>>6)
    
    int main(int argc,char *argv[])
    {
        int   c, oflag, semid, nsems;
    
        oflag = SVSEM_MODE | IPC_CREAT;   //设置创建模式
    
        //根据命令行参数e判断是否制定了IPC_EXCL模式
        while((c = getopt(argc,argv,"e")) != -1)
        {
            switch(c)
            {
                case 'e':
                    oflag |= IPC_EXCL;
                    break;
            }
        }
    
        //判断命令行参数是否合法
        if (optind != argc -2)
        {
            printf("usage: semcreate [-e] <pathname> <nsems>");
            exit(-1);
        }
    
        //获取信号量集合中的信号量个数
        nsems = atoi(argv[optind+1]);
    
        //创建信号量,通过ftok函数创建一个key,返回信号量 标识符
        semid = semget(ftok(argv[optind],0),nsems,oflag);
    
    
        return EXIT_SUCCESS;
    }
    

     

    关键字的获取


    有多种方法使客户机和服务器在同一IPC结构上会合: 
    * 服务器可以指定关键字IPC_PRIVATE创建一个新IPC结构,将返回的标识符存放在某处(例如一个文件)以便客户机取用。关键字 IPC_PRIVATE保证服务器创建一个新IPC结构。这种技术的缺点是:服务器要将整型标识符写到文件中,然后客户机在此后又要读文件取得此标识符。

    IPC_PRIVATE关键字也可用于父、子关系进程。父进程指定 IPC_PRIVATE创建一个新IPC结构,所返回的标识符在fork后可由子进程使用。子进程可将此标识符作为exec函数的一个参数传给一个新程序。

    • 在一个公用头文件中定义一个客户机和服务器都认可的关键字。然后服务器指定此关键字创建一个新的IPC结构。这种方法的问题是该关键字可能已与一个 IPC结构相结合,在此情况下,get函数(msgget、semget或shmget)出错返回。服务器必须处理这一错误,删除已存在的IPC结构,然后试着再创建它。当然,这个关键字不能被别的程序所占用。

    • 客户机和服务器认同一个路径名和课题I D(课题I D是0 ~ 2 5 5之间的字符值) ,然后调用函数ftok将这两个值变换为一个关键字。这样就避免了使用一个已被占用的关键字的问题。 
      使用ftok并非高枕无忧。有这样一种例外:服务器使用ftok获取得一个关键字后,该文件就被删除了,然后重建。此时客户端以此重建后的文件来ftok所获取的关键字就和服务器的关键字不一样了。所以一般商用的软件都不怎么用ftok。 
      一般来说,客户机和服务器至少共享一个头文件,所以一个比较简单的方法是避免使用ftok,而只是在该头文件中存放一个大家都知道的关键字。

    设置信号量的值(PV操作)


    int semop(int semid, struct sembuf *opsptr, size_t nops);
    • 1
    参数 描述
    semid 是semget返回的semid信号量标示符
    opsptr 指向信号量操作结构数组
    nops opsptr所指向的数组中的sembuf结构体的个数

    该函数执行成功返回0,失败返回-1;

    第二个参数sops为一个结构体数组指针,结构体定义在sys/sem.h中,结构体如下

    struct sembuf {
       unsigned short sem_num; /* semaphore index in array */
       short sem_op; /* semaphore operation */
       short sem_flg; /* operation flags */
    };

     

    sem_num 操作信号的下标,其值可以为0 到nops 
    sem_flg为该信号操作的标志:其值可以为0、IPC_NOWAIT 、 SEM_UNDO

    sem_flg标识 描述
    0 在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止;
    IPC_NOWAIT 在对信号量的操作不能执行的情况下,该操作立即返回;
    SEM_UNDO 当操作的进程推出后,该进程对sem进行的操作将被取消;
    sem_op取值 描述
    >0>0 则信号量加上它的值,等价于进程释放信号量控制的资源
    =0=0 若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回
    <0<0 则信号量加上它的值,等价于进程申请信号量控制的资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回。

    例如,当前semval为2,而sem_op = -3,那么怎么办?

    注意:semval是指semid_ds中的信号量集中的某个信号量的值

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/sem.h>
    #include <sys/ipc.h>
    
    int main(int argc,char *argv[])
    {
        int     c,i,flag,semid,nops;
        struct  sembuf *ptr;
        flag = 0;
            //根据命令行参数设置操作模式
        while( ( c = getopt(argc,argv,"nu")) != -1)
        {
            switch(c)
            {
                case 'n':
                    flag |= IPC_NOWAIT;   //非阻塞
                    break;
                case 'u':
                    flag |= SEM_UNDO;   //不可恢复
                    break;
            }
        }
        if(argc - optind < 2)
        {
            printf("usage: semops [-n] [-u] <pathname> operation...");
            exit(0);
        }
        //打开一个已经存在的信号量集合
        if((semid = semget(ftok(argv[optind],0),0,0)) == -1)
        {
            perror("semget() error");
            exit(-1);
        }
        optind++;  //指向当前第一个信号量的位置
        nops = argc - optind;   //信号量个数
        ptr = calloc(nops,sizeof(struct sembuf));
        for(i=0;i<nops;++i)
        {
            ptr[i].sem_num = i;  //信号量变换
            ptr[i].sem_op = atoi(argv[optind+i]);   //设置信号量的值
            ptr[i].sem_flg = flag;   //设置操作模式
        }
        //对信号量执行操作
        if(semop(semid,ptr,nops) == -1)
        {
            perror("semop() error");
            exit(-1);
        }
    
        return EXIT_SUCCESS;
    }
    

    对信号集实行控制操作(semval的赋值等)


    int semctl(int semid, int semum, int cmd, ../* union semun arg */);
    • 1
    参数 描述
    semid 是信号量集合;
    semnum 是信号在集合中的序号;
    semum 是一个必须由用户自定义的结构体,在这里我们务必弄清楚该结构体的组成:
    union semun
    {
    int val; // cmd == SETVAL
    struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT
    ushort *array; // cmd == SETALL,或 cmd = GETALL
    };
    描述
    IPC_STAT 读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    IPC_SET 设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    IPC_RMID 将信号量集从系统中删除
    GETALL 用于读取信号量集中的所有信号量的值,存于semnu的array中
    SETALL 设置所指定的信号量集的每个成员semval的值
    GETPID 返回最后一个执行semop操作的进程的PID。
    LSETVAL 把的val数据成员设置为当前资源数
    GETVAL 把semval中的当前值作为函数的返回,即现有的资源数,返回值为非负数。

    val只有cmd ==SETVAL时才有用,此时指定的semval = arg.val。

    注意:当cmd == GETVAL时,semctl函数返回的值就是我们想要的semval。千万不要以为指定的semval被返回到arg.val中。

    array指向一个数组,

    当cmd==SETALL时,就根据arg.array来将信号量集的所有值都赋值;

    当cmd ==GETALL时,就将信号量集的所有值返回到arg.array指定的数组中。

    buf 指针只在cmd==IPC_STAT 或IPC_SET 时有用,作用是semid 所指向的信号量集

    (semid_ds机构体)。一般情况下不常用,这里不做谈论。

    另外,cmd == IPC_RMID还是比较有用的。

    示例程序


    调用semctl函数设置信号量的值程序


    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/sem.h>
    #include <sys/ipc.h>
    
    //定义信号量操作共用体结构
    union semun
    {
        int                val;
        struct semid_ds    *buf;
        unsigned short     *array;
    };
    
    int main(int argc,char *argv[])
    {
        int semid,nsems,i;
        struct semid_ds seminfo;
        unsigned short *ptr;
        union semun arg;
    
        if(argc < 2)
        {
                printf("usage: semsetvalues <pathname>[values ...]");
                exit(0);
        }
    
        //打开已经存在的信号量集合
        semid = semget(ftok(argv[1],0),0,0);
        arg.buf = &seminfo;
    
        //获取信号量集的相关信息
        semctl(semid,0,IPC_STAT,arg);
        nsems = arg.buf->sem_nsems;  //信号量的个数
        if(argc != nsems + 2 )
        {
            printf("%s semaphores in set,%d values specified",nsems,argc-2);
            exit(0);
        }
    
        //分配信号量
        ptr = calloc(nsems,sizeof(unsigned short));
        arg.array = ptr;
    
        //初始化信号量的值
        for(i=0;i<nsems;i++)
        {
            ptr[i] = atoi(argv[i+2]);
        }
    
    
        //通过arg设置信号量集合
        semctl(semid,0,SETALL,arg);
    
        return EXIT_SUCCESS;
    }
    

    调用semctl获取信号量的值


    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/sem.h>
    #include <sys/ipc.h>
    
    union semun
    {
        int             val;
        struct semid_ds *buf;
        unsigned short     *array;
    };
    
    int main(int argc,char *argv[])
    {
        int     semid,nsems,i;
        struct semid_ds seminfo;
        unsigned short *ptr;
        union semun arg;
    
        if(argc != 2)
        {
            printf("usage: semgetvalues<pathname>");
            exit(0);
        }
    
        //打开已经存在的信号量
        semid = semget(ftok(argv[1], 0), 0, 0);
        arg.buf = &seminfo;
    
        //获取信号量集的属性,返回semid_ds结构
        semctl(semid, 0, IPC_STAT, arg);
        nsems = arg.buf->sem_nsems;     //信号量的数目
    
        ptr = calloc(nsems,sizeof(unsigned short));
        arg.array = ptr;
    
        //获取信号量的值
        semctl(semid, 0, GETALL, arg);
        for(i = 0; i < nsems; i++)
        {
            printf("semval[%d] = %d\n", i, ptr[i]);
        }
    
        return EXIT_SUCCESS;
    }
    

    通过semctl实现PV操作的函数库

    #include <semaphore.h>
    #include <sys/sem.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    union semun
    {
        int                 val;
        struct semid_ds     *buf;
        unsigned short      *array;
    };
    
    // 将信号量sem_id设置为init_value
    int init_sem(int sem_id, int init_value)
    {
        union semun     sem_union;
        sem_union.val = init_value;
    
        if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
        {
            perror("Sem init");
    
            exit(1);
        }
    
        return 0;
    }
    
    // 删除sem_id信号量
    int del_sem(int sem_id)
    {
        union semun sem_union;
        if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        {
            perror("Sem delete");
    
            exit(1);
        }
        return 0;
    }
    
    
    // 对sem_id执行p操作
    int sem_p(int sem_id)
    {
        struct sembuf   sem_buf;
        sem_buf.sem_num = 0;//信号量编号
        sem_buf.sem_op  = -1;//P操作
        sem_buf.sem_flg = SEM_UNDO;//系统退出前未释放信号量,系统自动释放
    
        if (semop(sem_id, &sem_buf, 1) == -1)
        {
            perror("Sem P operation");
    
            exit(1);
        }
    
        return 0;
    }
    
    
    // 对sem_id执行V操作
    int sem_v(int sem_id)
    {
        struct sembuf   sem_buf;
        sem_buf.sem_num = 0;
        sem_buf.sem_op  = 1;//V操作
        sem_buf.sem_flg = SEM_UNDO;
    
        if (semop(sem_id, &sem_buf, 1) == -1)
        {
            perror("Sem V operation");
    
            exit(1);
        }
    
        return 0;
    }
    
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <stdio.h>
    static int nsems;
    static int semflg;
    static int semid;
    int errno=0;
    union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    }arg;
    int main()
    {
    struct sembuf sops[2]; //要用到两个信号量,所以要定义两个操作数组
    int rslt;
    unsigned short argarray[80];
    arg.array = argarray;
    semid = semget(IPC_PRIVATE, 2, 0666);
    if(semid < 0 )
    {
    printf("semget failed. errno: %d\n", errno);
    exit(0);
    }
    //获取0th信号量的原始值
    rslt = semctl(semid, 0, GETVAL);
    printf("val = %d\n",rslt);
    //初始化0th信号量,然后再读取,检查初始化有没有成功
    arg.val = 1; // 同一时间只允许一个占有者
    semctl(semid, 0, SETVAL, arg);
    rslt = semctl(semid, 0, GETVAL);
    printf("val = %d\n",rslt);
    sops[0].sem_num = 0;
    sops[0].sem_op = -1;
    sops[0].sem_flg = 0;
    sops[1].sem_num = 1;
    sops[1].sem_op = 1;
    sops[1].sem_flg = 0;
    rslt=semop(semid, sops, 1); //申请0th信号量,尝试锁定
    if (rslt < 0 )
    {
    printf("semop failed. errno: %d\n", errno);
    exit(0);
    }
    //可以在这里对资源进行锁定
    sops[0].sem_op = 1;
    semop(semid, sops, 1); //释放0th信号量
    rslt = semctl(semid, 0, GETVAL);
    printf("val = %d\n",rslt);
    rslt=semctl(semid, 0, GETALL, arg);
    if (rslt < 0)
    {
    printf("semctl failed. errno: %d\n", errno);
    exit(0);
    }
    printf("val1:%d val2: %d\n",(unsigned int)argarray[0],(unsigned int)argarray[1]);
    if(semctl(semid, 1, IPC_RMID) == -1)
    {
        perror(“semctl failure while clearing reason”);
    }
    return(0);
    }

    信号量的牛刀小试——生产者与消费者问题


    1.问题描述: 
    有一个长度为N的缓冲池为生产者和消费者所共有,只要缓冲池未满,生产者便可将 
    消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。生产者往缓冲池 
    放信息的时候,消费者不可操作缓冲池,反之亦然。

    2.使用多线程和信号量解决该经典问题的互斥

    #include <stdio.h>
    #include <stdlib.h>
    
    
    #include <pthread.h>
    #include <stdio.h>
    #include <semaphore.h>
    
    #define BUFF_SIZE 10
    
    char buffer[BUFF_SIZE];
    char count;                         //  缓冲池里的信息数目
    sem_t sem_mutex;                    //  生产者和消费者的互斥锁
    sem_t p_sem_mutex;                  //  空的时候,对消费者不可进
    sem_t c_sem_mutex;                  //  满的时候,对生产者不可进
    
    void * Producer()
    {
        while(1)
        {
            sem_wait(&p_sem_mutex);     //  当缓冲池未满时
            sem_wait(&sem_mutex);       //  等待缓冲池空闲
    
            count++;
    
            sem_post(&sem_mutex);
    
            if(count < BUFF_SIZE)       //  缓冲池未满
            {
                sem_post(&p_sem_mutex);
            }
    
            if(count > 0)               //  缓冲池不为空
            {
                sem_post(&c_sem_mutex);
            }
        }
    }
    
    void * Consumer()
    {
        while(1)
        {
            sem_wait(&c_sem_mutex); //  缓冲池未空时
            sem_wait(&sem_mutex);   //  等待缓冲池空闲
    
            count--;
            sem_post(&sem_mutex);
    
            if(count > 0)
            {
                sem_post(&c_sem_mutex);
            }
        }
    
        return NULL;
    }
    
    
    
    int main()
    {
        pthread_t ptid,ctid;
    
        //  initialize the semaphores
        //sem_init(&empty_sem_mutex, 0, 1);
        //sem_init(&full_sem_mutex, 0, `0);
    
        //creating producer and consumer threads
        if(pthread_create(&ptid, NULL,Producer, NULL))
        {
            printf("\n ERROR creating thread 1");
            exit(1);
        }
    
        if(pthread_create(&ctid, NULL,Consumer, NULL))
        {
            printf("\n ERROR creating thread 2");
            exit(1);
        }
    
        if(pthread_join(ptid, NULL)) /* wait for the producer to finish */
        {
            printf("\n ERROR joining thread");
            exit(1);
        }
    
        if(pthread_join(ctid, NULL)) /* wait for consumer to finish */
        {
            printf("\n ERROR joining thread");
            exit(1);
        }
    
        //sem_destroy(&empty_sem_mutex);
        //sem_destroy(&full_sem_mutex);
        //exit the main thread
        pthread_exit(NULL);
    
        return EXIT_SUCCESS;
    }
    
    

     

    展开全文
  • 信号量的实现和应用(一)

    千次阅读 2018-07-16 09:59:14
    从一个实际的问题:生产者和消费者出发,谈一谈为什么需要信号量信号量用来做什么? 问题描述:现在存在一个文件"buffer.txt"作为一个共享缓存区,缓冲区同时最多只能保存10个数。现在一个生产者进程,...
  • 信号量详解

    2017-10-20 23:16:11
    之前学习过信号量但是只知道怎么使用但是对于其使用和核心没有进行了解过因此重新写这篇文章 在头文件下> 信号量的几个函数api 主要的函数时这三个具体如下解析 int semget(key_t key,int num_sems,int sem_flgs...
  • 进程间通信之Linux信号量编程

    千次阅读 2018-11-12 16:03:03
    信号量 信号量(Semaphore)是一种用于实现计算机资源共享的IPC机制之一,其本质是一个计数器。信号量是在多进程环境下实现资源互斥访问或共享资源访问的方法,可以用来保证两个或多个关键代码段不被并发调用。在...
  • Linux进程间通信——使用信号量

    万次阅读 多人点赞 2013-08-24 00:12:54
    这篇文章将讲述别一种进程间通信的机制——信号量。注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物。下面就进入信号量的讲解。 一、什么是信号量 为了防止出现因多个程序同时访问一个共享...
  • 进程间通信之-信号量semaphore--linux内核剖析(十)

    万次阅读 多人点赞 2016-03-28 23:37:37
    信号量什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。 信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要...
  • 信号量机制

    千次阅读 2017-06-18 10:29:11
    上篇博客中(进程同步之临界区域问题及Peterson算法),...下面就为你讲解信号量机制是如何解决这一问题的。 1.信号量机制 信号量机制即利用pv操作来对信号量进行处理。什么是信号量?信号量(semaphore)的数据结构为
  • Linux信号量机制及编程

    千次阅读 2013-08-29 18:15:45
    1965年,荷兰学者Dijkstra提出了利用信号量机制解决进程同步问题,信号量正式成为有效的进程同步工具,现在信号量机制被广泛的用于单处理机和多处理机系统以及计算机网络中。 信号量S是一个整数,S大于等于零是...
  • 2.什么是信号量机制?3.整型信号量4.记录型信号量(1)举一个生动形象的例子了解记录型信号量(2)梳理一下记录型信号量的知识点(P、V) 0.思维导图 1.为什么引入信号量机制? 为了更好的解决进程互斥与同步的...
  • 三种信号量机制

    千次阅读 2019-04-10 11:08:11
    1、整型信号量: 整型信号量定义一个用于表示资源数目的整型量数目S ,但是仅能通过两个原子操作wait(s)和sigal (s)来访问, while(S) { while(S<=0 );//如果未申请到资源则一直循环等待资源。 s--; //...
  • 进程同步之信号量机制(pv操作)

    千次阅读 2018-06-13 20:04:03
    1.信号量机制信号量机制即利用pv操作来对信号量进行处理。什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0...
  • 2.12信号量机制

    千次阅读 2020-04-02 13:26:28
    一,为什么要引入信号量机制 1,为了解决在双标志先检查法,进入区检查和上锁操作无法一气呵成,从而导致两个进程有可能进入临界区的问题 2,上一小节所有方案都无法实现让权等待 3,1965年,荷兰学者Dijkstra提出...
  • 记录型信号量机制采取了“让权等待”策略,是一种不存在“忙等”现象的进程同步机制。记录型信号量时由于它采用了记录型数据结果而得名的。在信号量机制中,除了需要一个用于代表资源数目数的整型变量`value`外,还...
  • IPC(进程间通信) | 信号量机制

    千次阅读 2019-04-19 19:39:02
    进程间的通信 进程间的通信有以下几种方式: 1. 管道:管道分为有名管道和无名管道 2. 信号量:通过控制多个进程对临界资源的访问,使得同一时刻只...本文主要讲通过信号量机制进行进程同步 什么是信号量?信...
  • System V 信号量机制

    2016-07-10 23:53:11
    有三种 IPC我们称作XSI IPC,即消息队列、... 本文主要讨论的是信号量机制。 semget()创建一个新的信号量集,或者存取一个已经存在的信号量集: 系统调用:semget(); 原型:int semget(key_t key,int nsems
  • 并发编程基础-信号量机制

    千次阅读 2019-04-16 06:53:50
    信号量机制包含以下几个核心概念: 信号量S,整型变量,需要初始化值大于0 P原语,荷兰语Prolaag(probeer te verlagen),表示减少信号量,该操作必须是原子的 V原语,荷兰语Verhogen,表示增加信号量,该操作必须是...
  • 操作系统基础-信号量机制的理解

    千次阅读 多人点赞 2019-04-20 13:02:53
    信号量机制的提出,是为了解决进程间关系通信的问题,因为进程间不可能用嘴来说“我在使用这个资源啊,你先等我用完再用”,或者说“我用完了,接下来轮到你了!”这是我们人之间通信的方式,那么现实中如何让某个...
  • C++之线程信号量机制

    千次阅读 2016-09-07 18:24:57
    #include #include using namespace std; int tickets=100; //火车票总数 HANDLE hSemaphore; DWORD WINAPI Thread1Proc(LPVOID lpParameter);//进程函数 DWORD WINAPI Thread2Proc(LPVOID lpParameter);...
  • python的信号量机制

    千次阅读 2019-07-15 19:11:09
    信号通信 一个进程向另一个进程发送一个信号来传递某种信息,接收者根据接收到的信号进行相应的行为 终端命令: kill -l 查看系统信号 kill -sig PID 向一个进程发送信号 信号介绍: 信号名称 信号含义...
  • 操作系统之信号量机制

    千次阅读 2019-03-03 21:26:07
    一、信号量 百度百科:信号量(Semaphore)是在多线程环境下使用的一种设施,是可用来保证两...二、信号量机制 1、整型信号量机制 它的值只能通过两个特定的原子操作wait和signal来改变,经典的PV操作。 v...
  • 名词解释:PV操作:执行一次p操作意味着请求分配一个单位资源,因此s的值-1,当s时,表示已经没有可用资源,进程阻塞,请求者必须等待别的进程释放该类资源,才能继续运行下去,所以当s时...1.利用信号量形成互斥 ……
  • 信号量 一、信号量的定义 引入信号量: 为了防止出现因多个程序...而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。   1、信号
  • 锁机制只能判断临界资源是否被占用,...下面就为你讲解信号量机制是如何解决这一问题的。 1.信号量机制 信号量机制即利用pv操作来对信号量进行处理。 什么是信号量?信号量(semaphore)的数据结构为一个值和一
  • 信号量机制主要用于实现进程间的同步,避免并发访问共享资源 函数 (一)获取或创建信号量 int semget(key_t key,int nsems,int semflg) key:信号量标识 nsems:信号量数量,若新创建信号量:nsems>0,若...
  • 该程序为Linux信号量机制实现程序,主要模拟了一般的生产者-消费者问题。(生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个...
  • 信号量机制解决生产者--消费者问题

    千次阅读 2016-05-25 22:39:48
    信号量机制解决生产者--消费者问题  qianghaohao(Xqiang)  以图片的形式总结了下信号量机制解决生产者--消费者问题的过程.其中  涉及到了信号量,PV原语,死锁等关键字。  稍微解释下PV原语:PV原语就是一...
  • 利用信号量机制解决进程同步和互斥问题   在讨论如何用信号量机制解决这个问题之前,我们应该先了解进程同步和互斥间的一些概念。 首先是进程间的两种关系:同步和互斥。所谓同步就是把异步环境下的一组并发进程...

空空如也

1 2 3 4 5 ... 20
收藏数 438,707
精华内容 175,482
关键字:

信号量