Linux进程间通信1 使用信号

发布 2022-09-20 18:31:28 阅读 3175

一、什么是信号。

用过windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在linux上是通过生成信号和捕获信号来实现的,运行中的进程捕获到这个信号然后作出一定的操作并最终被终止。

信号是unix和linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。

一个信号的产生叫生成,接收到一个信号叫捕获。

二、信号的种类。

信号的名称是在头文件中定义的,信号都以sig开头,常用的信号并不多,常用的信号如下:

更多的信号类型可查看附录表。

三、信号处理——signal函数。

程序可用使用signal函数来处理指定的信号,主要通过忽略和恢复其默认行为来工作。signal函数的原型如下:

#include<>

2. void(*signal(intsig,void(*func)(int)))int);

这是一个相当复杂的声明,耐心点看可以知道signal是一个带有sig和func两个参数的函数,func是一个类型为void (*int)的函数指针。该函数返回一个与func相同类型的指针,指向先前指定信号处理函数的函数指针。准备捕获的信号的参数由sig给出,接收到的指定信号后要调用的函数由参数func给出。

其实这个函数的使用是相当简单的,通过下面的例子就可以知道。注意信号处理函数的原型必须为void func(int),或者是下面的特殊值:

sig_ign:忽略信号,定义在中。

sig_dfl:恢复信号的默认行为定义在中。

说了这么多,还是给出一个例子来说明一下吧,源文件名为**如下:

#include<>

#include<>

#include<>

5. voidouch(intsig)

12. intmain()

22. return0;

运行结果如下:

可以看到,第一次按下终止命令(ctrl+c)时,进程并没有被终止,面是输出ouch! -i got signal 2,因为sigint的默认行为被signal函数改变了,当进程接受到信号sigint时,它就去调用函数ouch去处理,注意ouch函数把信号sigint的处理方式改变成默认的方式,所以当你再按一次ctrl+c时,进程就像之前那样被终止了。

四、信号处理——sigaction函数。

前面我们看到了signal函数对信号的处理,但是一般情况下我们可以使用一个更加健壮的信号接口——sigaction函数。它的原型为:

#include<>

2. intsigaction(intsig,conststructsigaction*act,structsigaction*oact);

该函数与signal函数一样,用于设置与信号sig关联的动作,而oact如果不是空指针的话,就用它来保存原先对该信号的动作的位置,act则用于设置指定信号的动作。

sigaction结构体定义在中,但是它至少包括以下成员:

void (*int) sa_handler;处理函数指针,相当于signal函数的func参数。

sigset_t sa_mask; 指定一个。信号集,在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中。信号屏蔽字是指当前被阻塞的一组信号,它们不能被当前进程接收到。

int sa_flags;信号处理修改器;

sa_mask的值通常是通过使用信号集函数来设置的,关于信号集函数,我将会在我的下一篇文章——linux进程间通信——信号集函数,详细讲述。

sa_flags,通常可以取以下的值:

此外,现在有一个这样的问题,我们使用signal或sigaction函数来指定处理信号的函数,但是如果这个信号处理函数建立之前就接收到要处理的信号的话,进程会有怎样的反应呢?它就不会像我们想像的那样用我们设定的处理函数来处理了。sa_mask就可以解决这样的问题,sa_mask指定了一个信号集,在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中,设置信号屏蔽字可以防止信号在它的处理函数还未运行结束时就被接收到的情况,即使用sa_mask字段可以消除这一竞态条件。

承接上面的例子,下面给出用sigaction函数重写的例子**,源文件为**如下:

#include<>

#include<>

#include<>

5. voidouch(intsig)

10. intmain()

26. return0;

运行结果与前一个例子中的相同。注意sigaction函数在默认情况下是不被重置的,如果要想它重置,则sa_flags就要为sa_resethand。

五、发送信号。

上面说到的函数都是一些进程接收到一个信号之后怎么对这个信号作出反应,即信号的处理的问题,有没有什么函数可以向一个进程主动地发出一个信号呢?我们可以通过两个函数kill和alarm来发送一个信号。

1、kill函数。

先来看看kill函数,进程可以通过kill函数向包括它本身在内的其他进程发送一个信号,如果程序没有发送这个信号的权限,对kill函数的调用就将失败,而失败的常见原因是目标进程由另一个用户所拥有。想一想也是容易明白的,你总不能控制别人的程序吧,当然超级用户root,这种上帝般的存在就除外了。

kill函数的原型为:

#include

#include<>

3. intkill(pid_tpid,intsig);

它的作用把信号sig发送给进程号为pid的进程,成功时返回0。

kill调用失败返回-1,调用失败通常有三大原因:

1、给定的信号无效(errno = einval)

2、发送权限不够( errno = eperm )

3、目标进程不存在( errno = esrch )

2、alarm函数。

这个函数跟它的名字一样,给我们提供了一个闹钟的功能,进程可以调用alarm函数在经过预定时间后向发送一个sigalrm信号。

alarm函数的型如下:

#include<>

2. unsignedintalarm(unsignedintseconds);

alarm函数用来在seconds秒之后安排发送一个sigalrm信号,如果seconds为0,将取消所有已设置的闹钟请求。alarm函数的返回值是以前设置的闹钟时间的余留秒数,如果返回失败返回-1。

马不停蹄,下面就给合fork、sleep和signal函数,用一个例子来说明kill函数的用法吧,源文件为**如下:

#include<>

#include

#include<>

#include<>

#include<>

7. staticintalarm_fired=0;

9. voidouch(intsig)

14. intmain()

31. /设置处理函数。

实验3Linux进程通信

printf client sent msgsnd msgqid,msg,1024,0 发送消息msg入msgqid消息队列 exit 0 结束该进程。void server while 消息类型为1时,退出循环 msgctl msgqid,ipc rmid,0 释放 删除 消息队列 exit 0 ...

实验4 进程通信

1 熟悉操作系统进程通信原理。2 设计程序,实现共享内存 管道通信 消息通信。1 进程间通信的几种方法简介。1 消息队列 消息队列是消息的链接表,包括posix消息队列systemv消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。2 共享内存 使得多个进程可...

操作系统作业2进程和进程通信

实验二进程和进程通信。实验报告。仅供参考仅供参考!一 实验目的。通过使用进程和进程通信方面的系统调用的,加深理解有关进程方面的基本概念。通过实验对进程有进一步的感性认识,掌握系统v的ipc机制。二 实验题目。1 设计一个程序,创建一个子进程,使父子进程合作,协调地完成某一功能。要求在该程序中还要使用...