linux进程等待自身关闭信号,进程状态、信号机制、进程追踪与进程等待

进程状态、信号机制、进程追踪与进程等待

SIGNAL、ptrace()、wait()

一、在linux下进程的信号机制

SIGNAL是这样一个机制:当操作系统监测到与进程有关的异常或特殊事件发生后,操作系统会向进程发送一个指令性质的信息,这

个信息称为“信号”。信号传递通知或异常信息。同样的,一个进程也可以通过一些指令向其他进程发送一个信号(如果这被允许

的话)。

进程一旦收到信号,就会自动对信号做出响应,这个响应机制是独立于程序之外的,对信号的响应动作往往是改变自己的运行状态

(定义见第二部分),事实上,信号是改变进程运行状态的主要方式。

响应动作有4种,对于不同的SIGNAL,C++程序的进程有不同的“默认响应方式”,下面是对响应方式的介绍,摘自C++ MAN PAGE

Term: Default action is to terminate the process.

Ign: Default action is to ignore the signal.

Core: Default action is to terminate the process and dump core.

Stop: Default action is to stop the process.

Cont: Default action is to continue the process if it is currently stopped.

大体解释一下,Terminate响应就是直接终止当前进程;Ignore响应是忽略这个信号,程序继续进行;Core响应是把当前进程转储后

终止进程;Stop则是暂停当前进程的执行流,即阻塞当前进程;而Cont则是继续当前进程的执行流。从这里也可以看到,信号往往

会改变接收信号进程的运行状态。

默认动作为Terminate或是Core的信号往往代表了严重的运行时错误,例如SIGFPE为浮点数运算错误(除以0等等);而Ignore的信

号往往只是一条消息,例如SIGCHLD只是告诉进程:你的一个子进程已经终止。往往你可以通过signal()函数来修改程序对各个信号

的响应方式,但只有一个信号是例外:那就是9号信号SIGKILL,他是要求进程立即终止的命令,这个信号不能被程序截获、不能修

改响应方式,必须立即执行Terminate动作。

每个信号都有一个代号来代表,体现在c++上,即宏定义了这些SIGNAL名称。如kill信号,他的本质是9,而c++宏定义了SIGKILL。

下面是所有SIGNAL的列表,从左到右分别是信号名称(也是宏定义后的信号名称)、信号真值、默认响应动作以及解释。

Signal Value Action Comment

SIGHUP

or death of controlling process

SIGINT 2 Term Interrupt from keyboard

SIGQUIT 3 Core Quit from keyboard

SIGILL 4 Core Illegal Instruction

SIGABRT 6 Core Abort signal from abort(3)

SIGFPE 8 Core Floating point exception

SIGKILL 9 Term Kill signal

SIGSEGV 11 Core Invalid memory reference

SIGPIPE 13 Term Broken pipe: write to pipe with no readers

SIGALRM 14 Term Timer signal from alarm(2)

SIGTERM 15 Term Termination signal

SIGUSR1 30,10,16 Term User-defined signal 1

SIGUSR2 31,12,17 Term User-defined signal 2

SIGCHLD 20,17,18 Ign Child stopped or terminated

SIGCONT 19,18,25 Cont Continue if stopped

SIGSTOP 17,19,23 Stop Stop process

SIGTSTP 18,20,24 Stop Stop typed at tty

SIGTTIN 21,21,26 Stop tty input for background process

SIGTTOU 22,22,27 Stop tty output for background process

The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

Next the signals not in the POSIX.1-1990 standard but described in SUSv2 and POSIX.1-2001. Signal Value Action

Comment

SIGBUS

SIGPOLL Term Pollable event (Sys V). Synonym of SIGIO

SIGPROF 27,27,29 Term Profiling timer expired

SIGSYS 12,-,12 Core Bad argument to routine (SVr4)

SIGTRAP 5 Core Trace/breakpoint trap

SIGURG 16,23,21 Ign Urgent condition on socket (4.2BSD)

SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2BSD)

SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2BSD)

SIGXFSZ 25,25,31 Core File size limit exceeded (4.2BSD)

Up to and including Linux 2.2, the default behaviour for SIGSYS, SIGXCPU, SIGXFSZ, and (on architectures other than

SPARC and MIPS) SIGBUS was to terminate the process (without a core dump). (On some other Unices the default action

for SIGXCPU and SIGXFSZ is to terminate the process without a core dump.) Linux 2.4 conforms to the POSIX.1-2001

requirements for these signals, terminating the process with a core dump.

Next various other signals. Signal Value Action Comment

SIGIOT

SIGEMT 7,-,7 Term

SIGSTKFLT -,16,- Term Stack fault on coprocessor (unused)

SIGIO 23,29,22 Term I/O now possible (4.2BSD)

SIGCLD -,-,18 Ign A synonym for SIGCHLD

SIGPWR 29,30,19 Term Power failure (System V)

SIGINFO 29,-,- A synonym for SIGPWR

SIGLOST -,-,- Term File lock lost

SIGWINCH 28,28,20 Ign Window resize signal (4.3BSD, Sun)

SIGUNUSED -,31,- Term Unused signal (will be SIGSYS)

(Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a sparc.)

SIGEMT is not specified in POSIX.1-2001, but nevertheless appears on most other Unices, where its default action is

typically to terminate the process with a core dump.

SIGPWR (which is not specified in POSIX.1-2001) is typically ignored by default on those other Unices where it

appears.

SIGIO (which is not specified in POSIX.1-2001) is ignored by default on several other Unices.

当进程接收到一个信号,而做出了非ignore的反应,则这个信号就改变了进程的状态(或者说是进程执行流的状态)。如果有其他

进程在监视当前进程,这往往意味着会触发监视进程的wait(),从而监视进程会得知当前进程的状态改变,同时也会得知当前进程

收到了怎样的信号。(这将在随后wait()函数类中详细解释)

二、进程的运行状态(STATE)

进程的运行状态即进程的执行流是否活跃,在linux下,有下面几个状态:

Running:活跃进程,即执行流正在运行或等待运行(等待cpu分配时间片而挂起也属于running)。

Waiting:进程在等待一个时间或者资源,分为可中断和不可中断两种;可中断的等待进程可以被信号中断。

Stopped:进程被停止,即进程暂停执行,执行流停止在当前位置,进程的所有信息都被保留。如果恢复到Running状态则会继续从

停止处向下执行。进程进入stopped状态往往因为收到了一个信号、并作出了stop响应动作,如被调试的程序遇到断点则进入

stopped状态。

Zombie:由于父进程死亡而被终止的进程,虽然被终止但是进程表中仍有进程记录。

这里可以看到,大多数SIGNAL即是一些要求进程改变其运行状态的命令,虽然SIGNAL同时也告知进程发生了什么(否则信号就没有这

么多种类了,我们也无法了解为什么进程被要求改变运行状态)。

三、wait()函数类

上一篇日志已经讲过wait()类了,这里会详细的来说明,同时将与信号联系起来。

wait()的本质是,监视一个进程(往往是子进程)的状态,同时挂起当前进程,直到目标进程的运行状态发生变化。即当前进程的

执行流会停在wait()上,知道目标的运行状态发生变化,wait()句停止,当前进程执行流继续进行。

从上一节可以看到,wait()所指的“变化”主要有:进程被终止(terminate)、进程被停止(stop)、进程被继续(resume)。又

由于状态的变化主要是由信号引起的,所以这些变化亦即:进程正常终止、进程被信号终止、进程被信号停止、进程被信号继续。

wait函数有很多个,但基本上的原型都为:

pid_t wait(pid_t pid, int options, int* status);

第一个参数说明要wait的进程号,第二个参数是等待命令的选项参数,第三个参数用于返回,即当wait()执行完毕时,status内保

存了目标进程刚才发生了什么。这个int可以通过一系列已经宏定义好的函数来解析:

WIFEXITED(status)

returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main

().

WEXITSTATUS(status)

returns the exit status of the child. This consists of the least significant 16-8 bits of the status argument that

the child specified in a call to exit() or _exit() or as the argument for a return statement in main(). This macro

should only be employed if WIFEXITED returned true.

WIFSIGNALED(status)

returns true if the child process was terminated by a signal.

WTERMSIG(status)

returns the number of the signal that caused the child process to terminate. This macro should only be employed if

WIFSIGNALED returned true.

WCOREDUMP(status)

returns true if the child produced a core dump. This macro should only be employed if WIFSIGNALED returned true.

This macro is not specified in POSIX.1-2001 and is not available on some Unix implementations (e.g., AIX, SunOS).

Only use this enclosed in #ifdef WCOREDUMP ... #endif.

WIFSTOPPED(status)

returns true if the child process was stopped by delivery of a signal; this is only possible if the call was done

using WUNTRACED or when the child is being traced (see ptrace(2)).

WSTOPSIG(status)

returns the number of the signal which caused the child to stop. This macro should only be employed if WIFSTOPPED

returned true.

WIFCONTINUED(status)

(Since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT.

大体解释一下:

WIFEXITED()可以解析出目标进程是否正常终止。

WEXITSTATUS()可以返回目标进程正常终止时的返回值(如main()函数内的return)。

WIFSIGNALED()可以解析出目标进程是否因为一个信号而被终止。

WTERMSIG()可以返回造成目标进程终止的信号是哪个。

WCOREDUMP()可以解析出目标进程是否因为一个信号而被转储后终止。

WIFSTOPPED()可以解析出目标进程是否因为一个信号而被停止。

WSTOPSIG()可以返回造成目标进程停止的信号是哪个。

WIFCONTINUED()可以解析出目标进程是否因为一个信号而恢复继续执行。

更高级的wait命令,例如wait4,还可以传入一个rusage结构的地址。如果wait的进程终止了,则可以通过这个结构返回目标进程一

生的统计信息。

四、ptrace()与进程追踪

ptrace()提供了强大的进程调试与追踪功能。往往用于父进程监视子进程,甚至可以毫不夸张的说,只要有了ptrace(),我们也可

以做一个debugger。ptrace()通过向目标进程发送信号而影响目标进程,或是通过修改自己或目标进程“对信号的默认响应方式”

来影响进程。

long ptrace(enum __ptrace_request request, pid_t pid, void* addr, void* data);

解释一下,第一个参数最重要,是告诉ptrace要做什么事情,第二个是目标进程id,第三个第四个都是附加参数,随着第一参数的

不同也有所不同。

ptrace可以做的事情有。。。。。。好多。。。。。。直接摘抄man page

PTRACE_TRACEME

Indicates that this process is to be traced by its parent. Any signal (except SIGKILL) delivered to this process

will cause it to stop and its parent to be notified via wait(). Also, all subsequent calls to exec() by this

process will cause a SIGTRAP to be sent to it, giving the parent a chance to gain control before the new program

begins execution. A process probably shouldn't make this request if its parent isn't expecting to trace it. (pid,

addr, and data are ignored.)

The above request is used only by the child process; the rest are used only by the parent. In the following

requests, pid specifies the child process to be acted on. For requests other than PTRACE_KILL, the child process

must be stopped.

PTRACE_PEEKTEXT, PTRACE_PEEKDATA

Reads a word at the location addr in the child's memory, returning the word as the result of the ptrace() call.

Linux does not have separate text and data address spaces, so the two requests are currently equivalent. (The

argument data is ignored.)

PTRACE_PEEKUSR

Reads a word at offset addr in the child's USER area, which holds the registers and other information about the

process (see and ). The word is returned as the result of the ptrace() call. Typically

the offset must be word-aligned, though this might vary by architecture. (data is ignored.)

PTRACE_POKETEXT, PTRACE_POKEDATA

Copies the word data to location addr in the child's memory. As above, the two requests are currently equivalent.

PTRACE_POKEUSR

Copies the word data to offset addr in the child's USER area. As above, the offset must typically be word-aligned.

In order to maintain the integrity of the kernel, some modifications to the USER area are disallowed.

PTRACE_GETREGS, PTRACE_GETFPREGS

Copies the child's general purpose or floating-point registers, respectively, to location data in the parent. See

for information on the format of this data. (addr is ignored.)

PTRACE_GETSIGINFO (since Linux 2.3.99-pre6)

Retrieve information about the signal that caused the stop. Copies a siginfo_t structure (see sigaction(2)) from

the child to location data in the parent. (addr is ignored.)

PTRACE_SETREGS, PTRACE_SETFPREGS

Copies the child's general purpose or floating-point registers, respectively, from location data in the parent. As

for PTRACE_POKEUSER, some general purpose register modifications may be disallowed. (addr is ignored.)

PTRACE_SETSIGINFO (since Linux 2.3.99-pre6)

Set signal information. Copies a siginfo_t structure from location data in the parent to the child. This will only

affect signals that would normally be delivered to the child and were caught by the tracer. It may be difficult to

tell these normal signals from synthetic signals generated by ptrace() itself. (addr is ignored.)

PTRACE_SETOPTIONS (since Linux 2.4.6; see BUGS for caveats)

Sets ptrace options from data in the parent. (addr is ignored.) data is interpreted as a bitmask of options, which

are specified by the following flags:

PTRACE_O_TRACESYSGOOD (since Linux 2.4.6)

When delivering syscall traps, set bit 7 in the signal number (i.e., deliver (SIGTRAP | 0x80) This makes it easy

for the tracer to tell the difference between normal traps and those caused by a syscall. (PTRACE_O_TRACESYSGOOD

may not work on all architectures.)

PTRACE_O_TRACEFORK (since Linux 2.5.46)

Stop the child at the next fork() call with SIGTRAP | PTRACE_EVENT_FORK << 8 and automatically start tracing the

newly forked process, which will start with a SIGSTOP. The PID for the new process can be retrieved with

PTRACE_GETEVENTMSG.

PTRACE_O_TRACEVFORK (since Linux 2.5.46)

Stop the child at the next vfork() call with SIGTRAP | PTRACE_EVENT_VFORK << 8 and automatically start tracing the

newly vforked process, which will start with a SIGSTOP. The PID for the new process can be retrieved with

PTRACE_GETEVENTMSG.

PTRACE_O_TRACECLONE (since Linux 2.5.46)

Stop the child at the next clone() call with SIGTRAP | PTRACE_EVENT_CLONE << 8 and automatically start tracing the

newly cloned process, which will start with a SIGSTOP. The PID for the new process can be retrieved with

PTRACE_GETEVENTMSG. This option may not catch clone() calls in all cases. If the child calls clone() with the

CLONE_VFORK flag, PTRACE_EVENT_VFORK will be delivered instead if PTRACE_O_TRACEVFORK is set; otherwise if the

child calls clone() with the exit signal set to SIGCHLD, PTRACE_EVENT_FORK will be delivered if PTRACE_O_TRACEFORK

is set.

PTRACE_O_TRACEEXEC (since Linux 2.5.46)

Stop the child at the next exec() call with SIGTRAP | PTRACE_EVENT_EXEC << 8.

PTRACE_O_TRACEVFORKDONE (since Linux 2.5.60)

Stop the child at the completion of the next vfork() call with SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8.

PTRACE_O_TRACEEXIT (since Linux 2.5.60)

Stop the child at exit with SIGTRAP | PTRACE_EVENT_EXIT << 8. The child's exit status can be retrieved with

PTRACE_GETEVENTMSG. This stop will be done early during process exit when registers are still available, allowing

the tracer to see where the exit occurred, whereas the normal exit notification is done after the process is

finished exiting. Even though context is available, the tracer cannot prevent the exit from happening at this

point.

PTRACE_GETEVENTMSG (since Linux 2.5.46)

Retrieve a message (as an unsigned long) about the ptrace event that just happened, placing it in the location data

in the parent. For PTRACE_EVENT_EXIT this is the child's exit status. For PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK and

PTRACE_EVENT_CLONE this is the PID of the new process. (addr is ignored.)

PTRACE_CONT

Restarts the stopped child process. If data is non-zero and not SIGSTOP, it is interpreted as a signal to be

delivered to the child; otherwise, no signal is delivered. Thus, for example, the parent can control whether a

signal sent to the child is delivered or not. (addr is ignored.)

PTRACE_SYSCALL, PTRACE_SINGLESTEP

Restarts the stopped child as for PTRACE_CONT, but arranges for the child to be stopped at the next entry to or

exit from a system call, or after execution of a single instruction, respectively. (The child will also, as usual,

be stopped upon receipt of a signal.) From the parent's perspective, the child will appear to have been stopped by

receipt of a SIGTRAP. So, for PTRACE_SYSCALL, for example, the idea is to inspect the arguments to the system call

at the first stop, then do another PTRACE_SYSCALL and inspect the return value of the system call at the second

stop. (addr is ignored.)

PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP (since Linux 2.6.14)

For PTRACE_SYSEMU, continue and stop on entry to the next syscall, which will not be executed. For

PTRACE_SYSEMU_SINGLESTEP, do the same but also singlestep if not a syscall. This call is used by programs like User

Mode Linux that want to emulate all the the child's syscalls. (addr and data are ignored; not supported on all

architectures.)

PTRACE_KILL

Sends the child a SIGKILL to terminate it. (addr and data are ignored.)

PTRACE_ATTACH

Attaches to the process specified in pid, making it a traced "child" of the current process; the behavior of the

child is as if it had done a PTRACE_TRACEME. The current process actually becomes the parent of the child process

for most purposes (e.g., it will receive notification of child events and appears in ps(1) output as the child's

parent), but a getppid(2) by the child will still return the PID of the original parent. The child is sent a

SIGSTOP, but will not necessarily have stopped by the completion of this call; use wait() to wait for the child to

stop. (addr and data are ignored.)

PTRACE_DETACH

Restarts the stopped child as for PTRACE_CONT, but first detaches from the process, undoing the reparenting effect

of PTRACE_ATTACH, and the effects of PTRACE_TRACEME. Although perhaps not intended, under Linux a traced child can

be detached in this way regardless of which method was used to initiate tracing. (addr is ignored.)

说几个常用的吧,第一个是PTRACE_TRACEME,这个只能是子进程执行,作用就是告诉父进程——请监视我吧。准确来说,他的本质

是:修改了当前进程对信号的默认响应方式——无论收到什么信号,一律执行stop动作(除了SIGKILL实在没办法以外)。这样无论

收到什么信号,都会引起当前进程的状态的改变,而且会改变到可逆的stopping状态。这意味着,如果有人用wait()监视他,就可

以监视到他所收到的所有信号,并作出对策。同时,TRACEME执行后,当前进程只要执行exec()函数类,就会收到操作系统发出

的"SIGTRAP"信号,从而stop(要知道,正常情况下这是不会触发SIGNAL的)。相当于把自己“双规”了,无法通过进程注入而摆脱

监视进程的控制。儿子TRACEME后,父亲就可以通过不断地循环wait()来截获儿子收到的所有信号,如果不TRACEME,可能就无法收

到那些被儿子ignore的信号——因为这些信号没有造成儿子运行状态的改变。

PRACE_CONT,一般是父进程执行,如果目标进程处于stopping状态,则向他发送一个信号让他恢复到running状态,往往是TRACE_ME

的后续,这样达到“单步”的目的。

然后是PTRACE_SYSCALL,这个是PTRACE_CONT与TRACEME加强版的合体。首先他相当于一个PTRACE_CONT,同时,CONT后他会改变目标

进程对信号的默认响应方式——不但像TRACEME那样对所有信号都要stop,而且目标进程只要进行系统调用(System Call),就会收

到一个"SIGTRAP"信号而stop——这太狠了,要知道几乎所有语句(哪怕一个printf)都要做好几次系统调用,而正常情况下,这些

系统调用都不会触发信号机制!这个模式的强大之处就是——监视进程可以通过wait()监测到目标进程的所有系统调用(通过判断

信号是否是SIGTRAP进行区分),从而防止目标进程做一些你不希望的事情。(比如说在acm里,程序打开了答案输出,然后。。。

)

与PTRACE_SYSCALL类似的是PTRACE_SINGLESTEP,与字面意思相同,他是让目标进程单步的。

PTRACE_KILL,和字面意思一样,直接给目标进程来一个SIGKILL,去死吧。。。呵呵

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值