Linux应用 之 信号

1.  信号可以理解为软件中断,是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是差不多的。

2. 信号机制的根本是每个进程都维护着一张信号表,通过信号表的操作。信号是用位图存的,只有一个bit。

3. 信号的转发需要经过内核控制,这是为了安全考虑。

3.1. 信号是如何产生的呢?有三种,第一种是访问非法内存啊,哎这时候产生一个信号(中断),第二种是用户按ctrl+z的时候,哎,这时候会终止进程是吧,这就是一个信号,第三种就是自己使用kill函数,综上所述,信号的实质就是中断。

4. 信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达。

5. 收到信号的进程三种处理方法,其实信号就像中断,要么不理它,要么就用中断函承接,要么就用默认方法处理。进程收到一个信号后,会检查对该信号的处理机制。如果是SIG_IGN,就忽略该信号;如果是SIG_DFT,则会采用系统默认的处理动作,通常是终止进程或忽略该信号;如果给该信号指定了一个处理函数(捕捉),则会中断当前进程正在执行的任务,转而去执行该信号的处理函数,返回后再继续执行被中断的任务。

6. 一开始的信号比较简单(0~31),会丢失,后来新添了一些信号,这些信号就不会丢失了,这就是不可靠信号与可靠信号的来源。信号值小于SIGRTMIN的信号都是不可靠信号。信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号。信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

举个例子:

信号处理的大致流程如下:信号产生 -> 信号注册 -> 信号在进程中注销 -> 信号处理函数执行完毕

更简单的说,就是用signal注册信号函数即可,这对于说的是本身已存在的那几十个信号。而信号用另一函数发送时,用kill即可,kill(pid, 信号值),Sigaction()是POXIS的

A进程发送的信号消息,其实就是根据自己的信号表,根据需要对相应的表项进行设置。内核接受到这个信号消息后,会先检查A进程是否有权限对B进程的信号表对应的项进行设置,如果可以,就会对B进程的信号表进行设置,这里面信号处理有个特点,就是没有排队的机制,也就是说某个信号被设置之后,如果B进程还没有来及进行响应,那么如果后续第二个同样的信号消息过来,就会被阻塞掉,也就是丢弃。

内核对B进程信号设置完成后,就会发送中断请求给B进程,这样B进程就进入到内核态,这个时候进程B根据那个信号表,查找对应的此信号的处理函数,然后设置frame,设置好之后,跳回到用户态执行信号处理函数,处理完成后,再次返回到内核态,再次设置frame,然后再次返回用户态,从中断位置开始继续执行。

这个frame其实就是在用户态和内核态之间跳转的时候,对堆栈现场的压栈保存。

大致的原理就是这么一个过程

这里就不得不说kill命令了,我们常用kill -9,实际上一共有64个信号,用kill -l可以展示如下:

Linux应用 之 信号_第1张图片

第9号只是其中的一种。两个常用的信号:SIGTERM和SIGKILL,第一个仅仅是比较友好。

Linux支持的信号列表如下。很多信号是与机器的体系结构相关的

信号值 默认处理动作 发出信号的原因

SIGHUP 1 A 终端挂起或者控制进程终止

SIGINT 2 A 键盘中断(如break键被按下)

SIGQUIT 3 C 键盘的退出键被按下

SIGILL 4 C 非法指令

SIGABRT 6 C 由abort(3)发出的退出指令

SIGFPE 8 C 浮点异常

SIGKILL 9 AEF Kill信号

SIGSEGV 11 C 无效的内存引用

SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道

SIGALRM 14 A 由alarm(2)发出的信号

SIGTERM 15 A 终止信号

SIGUSR1 30,10,16 A 用户自定义信号1,这两个是自己用的,蛮重要的,就是用这两个。

SIGUSR2 31,12,17 A 用户自定义信号2

SIGCHLD 20,17,18 B 子进程结束信号

SIGCONT 19,18,25 进程继续(曾被停止的进程)

SIGSTOP 17,19,23 DEF 终止进程

SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键

SIGTTIN 21,21,26 D 后台进程企图从控制终端读

SIGTTOU 22,22,27 D 后台进程企图从控制终端写

1~31号为普通信号,34~36号信号为实时信号。

实时信号(real-time signal):编号为34~46,它们通常与普通信号有很大的不同,因为他们必须排序以便发送多个信号能被接收到。但是同种信号的普通信号并不排序,尽管在Linux内核并不使用实时信号,它还是通过几个特定的系统调用完全实现了POSIX标准。实时信号就是可靠信号?

处理动作一项中的字母含义如下

  • A 缺省的动作是终止进程
  • B 缺省的动作是忽略此信号,将该信号丢弃,不做处理
  • C 缺省的动作是终止进程并进行内核映像转储(dump core),内核映像转储是指将进程数据在内存的映像和进程在内核结构中的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员提供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。
  • D 缺省的动作是停止进程,进入停止状况以后还能重新进行下去,一般是在调试的过程中(例如ptrace系统调用)
  • E 信号不能被捕获
  • F 信号不能被忽略

因此为什么说发送信号函数叫kill,原因就是默认操作都是关闭吧。

早期Unix系统只定义了32种信号,前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。

非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

好了接下来说函数,

typedef void (*sighandler_t) (int)

sighandler_t signal(int signum, sighandler_t handler);

返回原信号处理函数,或SIG_ERR,什么叫返回原处理信号函数呢,就是你只是想钩子一下,其他的仍然super处理,哎,那么就有用了。
signal()是最简单的给进程安装信号处理器的函数,第一个参数指定信号,第二个参数为该信号指定一个处理函数。

程序被注入信号后,什么时候会处理呢?在从内核态返回到用户态的时候处理。但是编程就简单多了,就像中断一样,或者监听一样就行。

kill命令是调用 kill 函数实现的, kill 函数可以给一个特定的进程发送指定的信号;

raise函数可以给当前进程发送指定的信号(自己也可以给自己发送信号),原型如下:

#include  
int kill(pid_t pid, int signum); //给任意进程发送任意信号
int raise(int signo); //给自己发送任意信号

两者都是成功返回0,失败返回-1

例如,定时器就是借助信号实现的。


#include
unsigned int alarm(unsigned int seconds);
参数seconds:alarm函数安排内核在seconds秒内发送一个SIGALRM信号给调用进程,如果soconds等于0,那么不会调度新的闹钟(alarm),也就是说,alarm本身并不是循环计时器,它只发送一次而已。

返回值:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为0
用的是SIGALRM信号,详细见示例。

 

 

 

每日一个小常识:

& 放在命令后面表示设置此进程为后台进程

默认情况下,进程是前台进程,这时此进程(命令执行相当于本质是开启一个进程)就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个'&'实现这个目的。

通常会返回一个pid,然后这个shell就可以随便使用了。

shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接受诸如Ctrl+c这样的信号。

 

Ctrl + C 和Ctrl + Z都是中断命令,但是他们的作用却不一样.
 
Ctrl + C 是强制中断程序的执行,进程已经终止。 Ctrl + C 发送 SIGINT信号 
 
Ctrl + Z 的是将任务挂起(暂停的意思),但是此任务并没有结束,他仍然在进程中他只是维持挂起的状态,用户可以使用fg/bg操作继续前台或后台的任务,fg命令重新启动前台被中断的任务,bg命令把被中断的任务放在后台执行. 
 
例如:当你vi一个文件是,如果需要用shell执行别的操作,但是你又不打算关闭vi,因为你得存盘推出,你可以简单的按下ctrl+z,shell会将vi进程挂起~,当你结束了那个shell操作之后,你可以用fg命令继续vi你的文件。
 
Ctrl + D 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。在shell中,ctrl-d表示推出当前shell.   就是你想关闭当前shell,简单ctrl+d就行,否则用ctrl+c,就别用ctrl+z了,当然,用shell打开的程序只能依附于该shell,如果该shell关闭了,那么打开的程序自然也就都关闭了。

 

 

 

你可能感兴趣的:(linux与操作系统)