进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,那么需要注意的是,被中断的是系统调用,而不是函数!!!
1.为了正常运作,某些守护进程实现为单实例的,也就是在任一时刻只运行该守护进程的一个副本。如果有多个副本同时运行,可能会造成混乱。以cron守护进程为例,如果同时有多个cron运行,那么每个cron都可能试图开始某个预定的操作,造成该操作的重复执行!
2.kill命令向进程发送信号,既可以使用信号名字,也可以使用信号代码
经常用的kill -9 pid实际上与kill -KILL pid等效,都是向进程发送SIGKILL信号,在使用信号名字时,不需要加前面的SIG前缀
3.一些函数不可重入有可能是因为
1)他们使用静态数据结构
2)他们调用malloc或free
3)他们是标准I/O函数,标准I/O函数的很多实现都以不可重入的方式使用全局数据结构
同一个库函数printf,在不同的进程中可能有不同的虚拟内存地址,但是在一个进程中,所有对printf函数的引用地址都是相同的。下面说明为什么printf函数不可重入。
因为printf函数可能使用了全局数据结构,那么当进程正在执行printf函数,并且在全局数据结构中已经写入了某些数据的时候,被信号中断,在信号的处理函数中再次引用了printf,此时的printf对全局数据结构写入自己的数据,这样先前的printf的数据就被破坏了,因此printf函数不可重入
APUE中举了getpwnam的例子来说明函数不可重入的原因,引用如下
进程正在执行getpwnam,该函数将其结果存放在静态存储单元中,如果该函数执行期间插入信号处理函数handler,handler又调用这样的函数时,返回给正常调用者的信息就可能被返回给信号处理程序的信息覆盖
还有errno的例子,信号处理程序可能会更改errno的值。因此在信号处理程序开始的地方,要保存errno,结束时,恢复errno
4.在bash中启动的程序,首先会fork一个进程,得到该进程的id,并且设置该进程的进程组id等于进程id,即这个新创建的进程是进程组的组长,然后execl执行程序
5.在设置SIGQUIT为阻塞时,我们保存了旧屏蔽字。为了解除对该信号的阻塞,用旧屏蔽字重新设置了进程信号屏蔽字(SIG_SETMASK)。另一种方法是用SIG_UNBLOCK使阻塞的信号不再被阻塞。但是,应当了解,如果我们编写了一个可能由其他人使用的函数,而且我们需要在函数中阻塞一个信号,则不能用SIG_UNBLOCK简单的解除对此信号的阻塞,这是因为此函数的调用者在调用本函数之前可能也阻塞了该信号,如果用我们在自己的函数中用SIG_UNBLOCK解除了对该信号的阻塞,那么调用者对该信号的阻塞也就失效了,必须使用SIG_SETMASK将信号屏蔽字恢复为原先的值,这样就能继续阻塞该信号。
6.关于进程状态
A process (which includes a thread) on a Linux machine can be in any of the following states -
- TASK_RUNNING - The process is either executing on a CPU or waiting to be executed.
- TASK_INTERRUPTIBLE - The process is suspended (sleeping) until some condition becomes true. Raising a hardware interrupt, releasing a system resource the process is waiting for, or delivering a signal are examples of conditions that might wake up the process (put its state back to TASK_RUNNING). Typically blocking IO calls (disk/network) will result in the task being marked as TASK_INTERRUPTIBLE. As soon as the data it is waiting on is ready to be read an interrupt is raised by the device and the interrupt handler changes the state of the task to TASK_INTERRUPTIBLE. Also processes in idle mode (ie not performing any task) should be in this state.
- TASK_UNINTERRUPTIBLE - Like TASK_INTERRUPTIBLE, except that delivering a signal to the sleeping process leaves its state unchanged. This process state is seldom used. It is valuable, however, under certain specific conditions in which a process must wait until a given event occurs without being interrupted. Ideally not too many tasks will be in this state.
- For instance, this state may be used when a process opens a device file and the corresponding device driver starts probing for a corresponding hardware device. The device driver must not be interrupted until the probing is complete, or the hardware device could be left in an unpredictable state.
- Atomic write operations may require a task to be marked as UNINTERRUPTIBLE
- NFS access sometimes results in access processes being marked as UNINTERRUPTIBLE
- reads/writes from/to disk can be marked thus for a fraction of a second
- I/O following a page fault marks a process UNINTERRUPTIBLE
- I/O to the same disk that is being accessed for page faults can result in a process marked as UNINTERRUPTIBLE
- Programmers may markl a task as UNINTERRUPTIBLE instead of using INTERRUPTIBLE
- TASK_STOPPED - Process execution has been stopped; the process enters this state after receiving a SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU signal
- TASK_TRACED - Process execution has been stopped by a debugger
- EXIT_ZOMBIE - Process execution is terminated, but the parent process has not yet issued a wait4( ) or waitpid( ) system call. The OS will not clear zombie processes until the parent issues a wait()-like call
- EXIT_DEAD - The final state: the process is being removed by the system because the parent process has just issued a wait4( ) or waitpid( ) system call for it. Changing its state from EXIT_ZOMBIE to EXIT_DEAD avoids race conditions due to other threads of execution that execute wait( )-like calls on the same process.