linux进程通信(三)IPC信号

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

当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于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的信号量机制

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

1、semget函数

它的作用是创建一个新信号量或取得一个已有信号量,原型为:

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有

信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),

只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,

key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了

IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯

一的信号量,如果信号量已存在,返回一个错误。同时可以在创建的时候设置其权限,比如0x644。

semget函数成功返回一个相应信号标识符(非零),失败返回-1.

2、semop函数

它的作用是改变信号量的值,原型为:

sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:

[cpp] view
plain
 copy

print?

  1. struct sembuf{
  2. short sem_num;//信号量的个数
  3. short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
  4. //一个是+1,即V(发送信号)操作。
  5. short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
  6. //并在进程没有释放该信号量而终止时,操作系统释放信号量
  7. };

3、semctl函数

该函数用来初始化或者删除信号量信息,它的原型为:

如果有第四个参数,它通常是一个union semum结构,定义如下:

前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个

SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。

IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

四、进程使用信号量通信

下面使用一个例子来说明进程间如何使用信号量来进行通信,这个例子是两个进程同时向屏幕输出数据,我们可以看到如何使用信号量来使两个进程协调工作,使同一时间只有一个进程可以向屏幕输出数据。父进程负责信号量的创建和删除,子进程不需要创建只需要获取信号量即可。切记,要删除信号量如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

查看信号量:

ipcs -s

删除信号量:

ipcrm -s sem_id(信号量id)

程序代码:

comm.h(头文件)

comm.c

测试文件1(不使用信号量)

#include "comm.h"

int main()

{

    //int sem_id = create_sem_set(1);

    //init_sem_set(sem_id,0,1);

    pid_t id =fork();

    if(id < 0)

    {

        perror("fork");

        return -1;

    }

    if(id == 0)

    {

        //child

    //  int c_sem_id  = get_sem_set();

        while(1)

        {

            //P(c_sem_id,0);

            printf("A");

            fflush(stdout);

            usleep(rand()%12345);

            printf("A");

            fflush(stdout);

            usleep(rand()%12354);

            //V(c_sem_id ,0);

        }

    }

    else

    {

        //father

        while(1)

        {

            //P(sem_id,0);

            printf("B");

            fflush(stdout);

            usleep(rand()%123455);

            printf("B");

            fflush(stdout);

            usleep(rand()%123564);

            //V(sem_id ,0);

        }

        wait(NULL);

        //destory_sem_set(sem_id);

    }

    return 0;

}

运行效果图

测试文件2(使用信号量)

#include "comm.h"

int main()

{

    int sem_id = create_sem_set(1);

    init_sem_set(sem_id,0,1);

    pid_t id =fork();

    if(id < 0)

    {

        perror("fork");

        return -1;

    }

    if(id == 0)

    {

        //child

     int c_sem_id  = get_sem_set();

        while(1)

        {

            P(c_sem_id,0);

            printf("A");

            fflush(stdout);

            usleep(rand()%12345);

            printf("A");

            fflush(stdout);

            usleep(rand()%12354);

            V(c_sem_id ,0);

        }

    }

    else

    {

        //father

        while(1)

        {

            P(sem_id,0);

            printf("B");

            fflush(stdout);

            usleep(rand()%123455);

            printf("B");

            fflush(stdout);

            usleep(rand()%123564);

            V(sem_id ,0);

        }

        wait(NULL);

        destory_sem_set(sem_id);

    }

    return 0;

}

运行效果图

六、信号量的总结

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。

若通过kill命令把其中一个进程杀死,且该进程还没有执行V操作释放资源。若使用SEM_UNDO标志,则操作系统将自动释放该进程持有的信号量,从而使得另外一个进程可以继续工作。若没有这个标志,另外进程将P操作永远阻塞。

因此,一般建议使用SEM_UNDO标志。

注意:

1、在System V标准中创建和初始化分离,一定要初始化否则会引发错误。

2、System V可以一次创建多个信号量,称为信号量集,用数组的下标来访问,初始化时可以批量化的操作。

时间: 2024-08-18 17:33:11

linux进程通信(三)IPC信号的相关文章

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是

linux进程通信之SYSTEM V信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有.信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒. 一.信号量的分类: 在学习信号量之前,我们必须先知道--Linux提供两种信号量: (1) 内核信号量,由内核控制路径使用. (2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量. POSIX信号量又分为有名信号量和无名信号量.有名信号量,其值保存在文件

浅析linux进程通信的方式

求职笔试中,考察进程通信方式是一个老生长谈的问题,每次都让我答得一头雾水,于是我总结了一下 这些必须了解的知识点. 实现linux进程通信的方式有6种: --内存共享 --信号(Singal) --管道(Pipe) --消息队列(Message) --信号量(Semaphore) --socket 消息队列通信 请关注:http://blog.csdn.net/ljianhui/article/details/10287879 内存共享通信 请关注:http://blog.csdn.net/lj

Linux进程通信----匿名管道

Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组 表示的文件描述字.这个数组有两个文件描述字,第一个是用 于读数据的文件描述符第二个是用于写数据的文件描述符. 不能将用于写的文件描述符进行读操作或者进行读的文件描述 符进写操作,这样都会导致错误. 关于匿名管道的几点说明: 1.匿名管道是半双工的,即一个进程只能读,一个进程只能写    要实现全双工,需要两个匿名管道. 2.只能在父子进程或者兄弟进程进行通信. 3.在读的时候关闭写文件描

linux 进程通信之 信号

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式. 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它:如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程. 信号的产生 1.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如ctr+c产生SIGINT, ctr + \产生SIGQUI信号,ctr +

Linux笔记--Linux进程通信

Linux进程间通信 文章来源: http://www.cnblogs.com/linshui91/archive/2010/09/29/1838770.html 一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到.C.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).D.

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication) 进程是操作系统实现程序独占系统运行的假象的方法,是对处理器.主存.I/O设备的抽象表示.每个进程都是一个独立的资源管理单元,每个进程所看到的是自己独占使用系统的假象,因此各个进程之间是不能够直接的访问对方进程的资源的,不同的进程之间进行信息交互需要借助操作系统提供的特殊的进程通信机制. 进程之间的通信,从物理上分,可以分为同主机的进程之间的通信和不同主机间的进程之间的通信.从通信内容方式上分,可以分为数据交互.同

linux进程通信

e14: 进程间通信(进程之间发送/接收字符串/结构体): 传统的通信方式: 管道(有名管道 fifo,无名管道 pipe) 信号 signal System V(基于IPC的对象):                             IPC对象:                                                    ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ... 消息队列 mes

Linux进程通信的几种方式总结

进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程). 资源共享 多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制. 进程控制 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变.

linux 进程通信之 共享内存

共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容. 关于共享内存使用的API key_t ftok(const char *pathname, int proj_id); #在IPC中,我们经常用一个 key_t 的值来创建或者打开 信号量,共享内存和消息队列.这个 key_t 就是由ftok函数产生的. pathname:指定的文件名,该文件必须是存在而且可以访问 proj_