当一个进程调用fork
时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。
特殊的是exec
,因为exec
运行新的程序后会覆盖从父进程继承来的存储映像,那么信号捕捉函数在新程序中已无意义,所以exec
会将原先设置为要捕捉的信号都更改为默认动作。
SIGTERM
可以被捕捉、被忽略,因此使用 kill $PID
不能终止子进程
SIGKILL
不能被捕捉、被忽略,因此推荐使用 kill -9 $PID
来终止子进程
fork exec
[fork exe对子进程继承父进程信号处理机制的影响]http://blog.csdn.net/guozhiyingguo/article/details/53837424)
当一个进程调用fork时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。
特殊的是exec,因为exec运行新的程序后会覆盖从父进程继承来的存储映像,那么信号捕捉函数在新程序中已无意义,所以exec会将原先设置为要捕捉的信号都更改为默认动作。
- fork后子进程会继承父进程的信号屏蔽字,再继续exec后仍会继承这个信号屏蔽字。同样地,直接调用system后子进程也会继承父进程的信号屏蔽字。
- fork后子进程会继承父进程的信号处理设置,再继续exec后就不会继承这个信号处理设置了。
- fork后子进程会继承父进程的控制终端,且子进程在父进程的进程组和会话组中;再继续exec后仍会继承这个控制终端,仍在父进程的进程组和会话组中。同样地,调用system后子进程会继承父进程的控制终端,且子进程在父进程的进程组和会话组中。
- Ctrl+c产生的SIGINT信号会发送给父进程、fork后的子进程以及继续exec的子进程;同样地,也会发给system调用运行的子进程。
对于fork():
- 子进程复制父进程的所有进程内存到其内存地址空间中。父、子进程的 “数据段”,“堆栈段”和“代码段”完全相同,即子进程中的每一个字节都和父进程一样。
- 子进程的当前工作目录、umask掩码值和父进程相同,fork()之前父进程打开的文件描述符,在子进程中同样打开,并且都指向相同的文件表项。
- 子进程拥有自己的进程ID。
对于exec():
- 进程调用exec()后,将在同一块进程内存里用一个新程序来代替调用exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”,“堆栈段”和“代码段”背新程序改写。
- 新程序会保持调用exec()进程的ID不变。
- 调用exec()之前打开打开的描述字继续打开(好像有什么参数可以令打开的描述字在新程序中关闭)
###
fork()
一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。
事实上,目前大多数的unix系统在实现上并没有作真正的copy。一般的,CPU都是以“页”为单位分配空间的,象INTEL的CPU,其一页在通常情况下是4K字节大小,而无论是数据段还是堆栈段都是由许多“页”构成的,fork函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的“页”从物理上也分开。系统在空间上的开销就可以达到最小。
对于exec系列函数
一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到。
###
子进程继承了父进程的几乎所有的属性,包括如下:
- 实际UID,GID和有效UID,GID.
- 环境变量.
- 附加GID.
- 调用exec()时的关闭标志.
- UID设置模式比特位.
- GID设置模式比特位.
- 进程组号.
- 会话ID.
- 控制终端.
- 当前工作目录.
- 根目录.
- 文件创建掩码UMASK.
- 文件长度限制ULIMIT.
- 预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承.
- 还有一些其它属性.
但子进程也有与父进程不同的属性,这点很重要:
- 进程号, 子进程号不同与任何一个活动的进程组号.
- 父进程号.
- 子进程继承父进程的文件描述符或流时,具有自己的一个拷贝并且与父进程和其它子进程共享该资源.
- 子进程的用户时间和系统时间被初始化为0.
- 子进程的超时时钟设置为0.
- 子进程的信号处理函数指针组置为空.
- 子进程不继承父进程的记录锁.
man 7 signal
A child created via fork(2) inherits a copy of its parent's signal dispositions.
During an execve(2), the dispositions of handled signals are reset to the default;
the dispositions of ignored signals are left unchanged.
结论
- 父进程中注册的信号处理函数不会被子进程继承
- 父进程中忽略的信号会被子进程继承
- SIGTERM可以被捕获、忽略
- 父进程注册SIGTERM信号处理函数之后,子进程不受影响,如果SIGTERM子进程不受影响,应该是子进程添加了信号处理函数导致的
- SIGKILL不能被捕获、忽略,因此推荐使用
kill -9 $pid
来终止子进程