进程间通信——信号

进程间通信——信号

宗旨:技术的学习是有限的,分享的精神的无限的。

一、信号和中断

1、信号基本概念

(1)发送信号:产生信号,有多种发送信号的方式【一个进程到另一个进程,内核向用户,进程向自己】

(2)安装信号:设置信号到来时不再执行默认操作,而是执行自定义的代码。

(3)递送信号:一个信号被操作系统发送到目标进程引起某段处理程序的执行。

(4)捕获信号:被递送的信号在目标进程引起某段处理程序的执行。

(5)屏蔽信号:进程告诉操作系统暂时不接受某些信号。

(6)忽略信号:进程被递送到目标进程,但目标进程不处理,直接丢弃。

(7)未决信号:信号已经产生,但因目标进程暂时屏蔽该信号而不能被目标进程捕获到的信号。

(8)可靠信号和不可靠信号:编号小于32的信号是不可靠信号,大于32的是可靠信号。

信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的这一段过程。信号的“屏蔽”是一个开关动作,指的是暂时阻止该信号被处理。

输入命令kill –l查看系统定义的信号列表:

$ kill-l

1) SIGHUP      2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP

6) SIGABRT     7) SIGEMT       8) SIGFPE       9) SIGKILL     10) SIGBUS

11)SIGSEGV     12) SIGSYS      13) SIGPIPE     14) SIGALRM     15) SIGTERM

16)SIGURG      17) SIGSTOP     18)SIGTSTP     19) SIGCONT     20) SIGCHLD

21)SIGTTIN     22) SIGTTOU     23) SIGIO       24) SIGXCPU     25) SIGXFSZ

26)SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGPWR      30) SIGUSR1

31)SIGUSR2     32) SIGRTMAX

二、发送信号

发送信号是指一个进程向另一个进程发送某个信号值,但实际并不是直接发送的,而是由OS转发的。产生一个信号有多种情况:

(1) 用户按下Ctrl-C,这个键盘输入产生一个硬件中断。

(2) 硬件异常产生信号。对一个无效存储访问的进程产生一个SIGSEGV

(3) 终止进程信号。其他进程调用kill()函数可将信号发送给另一个进程或进程组。

(4) 软件异常产生信号。

1、kill发送一个信号到进程

——传递一个信号给指定进程使用kill()函数,给当前进程使用raise(),唤醒设置定时使用alarm()

(1)函数原型

#include<signal.h>

intkill(pid_t pid, int sig);

(2)函数参数

pid:要被传递信号的进程号(PID)

pid>0:将信号发送给进程的PID值为pid的进程

pid=0:将信号发送给和当前进程在同一进程组的所有进程

pid=-1:将信号发送给系统内的所有进程

pid<0:将信号发送给进程组号PGID为pid绝对值的所有信号

sig:发送的信号值

(3)返回值

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

可向某个进程发送kill -0信号以检测进程是否存在,因为当前进程总是存在的:

kill(getpid, 0);

2、raise自举一个信号

——给当前进程发送一个信号,即唤醒一个进程。

(1)函数原型

#include <signal.h>

int raise(int sig);

(2)函数参数

sig:发送的信号值

(3)返回值

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

此函数相当于:

if(kill(getpid(),sig) == -1)

{

perror(“raise”);

}

3、alarm定时

——传递定时信号,即在多少时间内产生SIGALRM信号,调用一次产生一个信号。

(1)函数原型

#include <unistd.h>

int alarm(unsigned int seconds);

(2)函数参数

seconds:多少时间内发送SIGALRM信号给当前进程

seconds=0:取消所有发出的报警请求

(3)返回值

在调用alarm之前没有调用过alarm,成功返回0,失败返回-1;

此前调用过alarm函数,则将重新设置调用进程的闹钟,成功,将以当前时间为基准,返回值为上次设置的alarm将在多少时间内产生SIGALRM信号。

4、ualarm定时

——使当前进程在指定时间内产生SIGALRM信号,然后每隔指定时间重复产生SIGALRM信号。

(1)函数原型

useconds_t ualarm(useconds_t value,useconds_t interval);

(2)函数参数

value:指定时间(us)内产生SIGALRM信号

interval:每隔指定时间重复产生SIGALRM

(3)返回值

成功返回0,

三、安装信号和捕捉信号

1、信号处理方法

(1) 忽略此信号。

(2) 执行该信号的默认处理动作。

(3) 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方 式称为捕捉(Catch)一个信号。

2、signal安装信号

——信号都有默认的处理方式,未做特殊处理,将执行默认操作;若要做特殊处理,则要安装信号处理函数。

(1)函数原型

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int sig, sighandlerhandler);

(2)函数参数

sig:接收到的信号

handler:接收到此信号后的处理代码入口

(3)返回值

成功返回指向针对此信号的上一次设置,执行失败返回SIG_EER(-1)错误。

3、sigaction安装信号

——signal只能提供简单的信号安装操作,并逐步并淘汰。此函数可用来检查和更改信号处理操作。

(1)函数原型

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

struct sigaction
{
  void (*sa_handler)(int);/* addr of signal handler, */
  /* or SIG_IGN, or SIG_DFL*/
  sigset_t sa_mask; /*additional signals to block */
  int sa_flags; /* signaloptions, Figure 10.16 */
  /* alternate handler */
  void (*sa_sigaction)(int, siginfo_t *, void *);
};

(2)函数参数

signo:指定信号的编号

若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。

(3)返回值

成功返回0,失败返回-1。

将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默 认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以 用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返 回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返 回时自动恢复原来的信号屏蔽字。 sa_flags字段包含一些选项,我把sa_flags设为0, sa_sigaction是实时信号的处理函数。

四、信号集操作

信号忽略:系统仍然传递该信号,指示相应进程对该信号不做任何处理。

信号屏蔽:即使传递信号给该进程,该进程也不捕捉信号。

#define SIGSET_NWOEDS (1024 / (8 * sizeof(unsigned long int)))

typedef struct
{
  unsigned long int val[SIGSET_NWOEDS];
} sigset_t;

1、sigprocmask

——设置进程屏蔽信号集

(1)函数原型

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

(2)函数参数

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。


SIG_BLOCK


set包含了我们希望添加到当前信号屏蔽字的信号,相当 于mask=mask|set


SIG_UNBLOCK


set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当 于mask=mask&~set


SIG_SETMASK


设置当前信号屏蔽字为set所指向的值,相当于mask=set

(3)返回值

若成功则为0,若出错则为-1

2、sigpending

#include <signal.h>

int sigpending(sigset_t *set);

——读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

#include<signal.h>
#include<stdio.h>
#include<unistd.h>

void printsigset(const sigset_t *set)
{
  int i;
  for (i = 1; i < 32; i++)
    if (sigismember(set, i) == 1)
    {
      putchar('1');
    }
    else
    {
      putchar('0');
    }
  puts("");
}

int main(void)
{
  sigset_t s, p;
  sigemptyset(&s);
  sigaddset(&s, SIGINT);
  sigprocmask(SIG_BLOCK, &s, NULL);
  while (1)
  {
    sigpending(&p);
    printsigset(&p);
    sleep(1);
  }
  return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

3、信号集

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。注意,在使用sigset_t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。这四个函数都是成功返
回0,出错返回-1。 sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。

五、等待信号

进程可以因等待某些特定的信号而阻塞,pause()函数用来等待除当前进程外的任意信号,而sigsuspend()用来等待指定信号以外的任意信号。

1、pause

——使调用进程挂起直到有信号递达

(1)函数原型

#include <unistd.h>

int pause(void);

(2)参数返回值

如果信号的处理动作是终止进程,则进程终止, pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态, pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1, errno设置为EINTR, 所以pause只有出错的返回值。

// 用alarm和pause实现sleep(3)函数
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sig_alrm(int signo)
{
  /* nothing to do */
}
unsigned int mysleep(unsigned int nsecs)
{
  struct sigaction newact, oldact;
  unsigned int unslept;
  newact.sa_handler = sig_alrm;
  sigemptyset(&newact.sa_mask);
  newact.sa_flags = 0;
  sigaction(SIGALRM, &newact, &oldact);
  alarm(nsecs);
  pause();
  unslept = alarm(0);
  sigaction(SIGALRM, &oldact, NULL);
  return unslept;
}
int main(void)
{
  while(1)
  {
    mysleep(2);
    printf("Two secondspassed\n");
  }
  return 0;
}

(1) main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数sig_alrm。

(2) 调用alarm(nsecs)设定闹钟。

(3) 调用pause等待,内核切换到别的进程运行。

(4) nsecs秒之后,闹钟超时,内核发SIGALRM给这个进程。

(5) 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数 是sig_alrm。

(6) 切换到用户态执行sig_alrm函数,进入sig_alrm函数时SIGALRM信号被自动屏蔽,从sig_alrm函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入 内核,再返回用户态继续执行进程的主控制流程( main函数调用的mysleep函数)。

(7)pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理。

可重入函数——如果一个函数符合以下条件之一则是不可重入的:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。

调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

【通俗说:使用了全局变量的都是不可重入的】

时间: 2024-10-27 01:49:36

进程间通信——信号的相关文章

Linux进程间通信——信号

一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中的进程捕获到这个信号然后作出一定的操作并最终被终止. 信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动.通常信号是由一个错误产生的.但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程.一个信号的产生叫生成,接收到一个信号

进程间通信--信号(进程间通信唯一的异步方式)

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

详解linux进程间通信-信号

前言:之前说看<C++ Primer >暂时搁浅一下,迷上公司大神写的代码,想要明白,主要是socket.进程间通信! 知道进程间通信:信号.信号量.管道.消息队列.共享内存(共享存储),也能写些简单代码进行通信,但不知道应用在哪?感觉很多小伙伴跟我有类似经历吧? 一.应用实例: 要去linux设备上去添加.改密用户:本地去linux设备添加用户,用socket实现,其实大神改的ssh源码来实现的,这不是我要讲的重点,而我要讲的是准备过程,去登陆linux设备,要准备好:管理员.密码等. 简略

Linux进程间通信 -- 信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

我们已经知道,我们可以通过信号来终止进程,也可以通过信号来在进程间进行通信,程序也可以通过指定信号的关联处理函数来改变信号的默认处理方式,也可以屏蔽某些信号,使其不能传递给进程.那么我们应该如何设定我们需要处理的信号,我们不需要处理哪些信号等问题呢?信号集函数就是帮助我们解决这些问题的. 有关Linux进程间使用信号通信的更多内容,可以参阅我的另一篇文章,Linux进程间通信 -- 信号量函数 signal().sigaction() 下面是信号函数集: 1.int sigemptyset(si

Linux进程间通信—信号

三.信号(Signal) 信号是Unix系统中使用的最古老的进程间通信的方法之一.操作系统通过信号来通知某一进程发生了某一种预定好的事件:接收到信号的进程可以选择不同的方式处理该信号,一是可以采用默认处理机制—进程中断或退出,一是忽略该信号,还有就是自定义该信号的处理函数,执行相应的动作. 内核为进程生产信号,来响应不同的事件,这些事件就是信号源.信号源可以是:异常,其他进程,终端的中断(Ctrl-C,Ctrl+\等),作业的控制(前台,后台进程的管理等),分配额问题(cpu超时或文件过大等),

进程间通信--信号

信号 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的. 信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达. 信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了. 信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息. 信号来源 信号事件的发生有两个来源: 硬件来源(比如我们按下了键盘或者其它硬件故障): 软件

Linux进程间通信 -- 信号量函数 signal()、sigaction()

一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中的进程捕获到这个信号然后作出一定的操作并最终被终止. 信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动.通常信号是由一个错误产生的.但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程.一个信号的产生叫生成,接收到一个信号

Linux信号(signal) 机制分析

[摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执行及注销等.最后介绍了应用层的相关处理,主要包括信号处理函数的安装.信号的发送.屏蔽阻塞等,最后给了几个简单的应用实例. [关键字]软中断信号,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t 1       信

Linux进程间通信 -- 信号量 semget()、semop()、semctl()

这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信 -- 信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同