Board logo

标题: TASK_KILLABLE:Linux 中的新进程状态(1) [打印本页]

作者: look_w    时间: 2018-5-23 17:32     标题: TASK_KILLABLE:Linux 中的新进程状态(1)

类似于文件,进程是任何 UNIX® 操作系统的基本元素。进程是执行可执行文件的指令的动态实体。除了执行其指令之外,进程有时还会管理  打开文件、处理器上下文、地址空间以及与程序相关的数据等。Linux 内核将关于进程的完整信息保存在进程描述符 中,它的结构被定义为 struct task_struct。您可以在 Linux 内核源文件 include/linux/sched.h 中看到 struct task_struct 的各个字段。
关于进程状态在进程的生命周期内,可能会经历一系列互斥的状态。内核将进程的状态信息保存在 struct task_struct 的 state 字段中。图 1 展示了进程状态之间的转换。
图 1. 进程状态转换我们先来了解一下各种进程状态:
有关进程状态转换的详细信息,请参阅  一节中的 UNIX 操作系统设计
如前所述,进程状态                TASK_UNINTERRUPTIBLE 和                TASK_INTERRUPTIBLE 都是睡眠状态。现在,我们来看看内核如何将进程置为睡眠状态。
内核映射 Linux 内核提供了两种方法将进程置为睡眠状态。
将进程置为睡眠状态的普通方法是将进程状态设置为 TASK_INTERRUPTIBLE 或                TASK_UNINTERRUPTIBLE 并调用调度程序的 schedule() 函数。这样会将进程从 CPU 运行队列中移除。如果进程处于可中断模式的睡眠状态(通过将其状态设置为 TASK_INTERRUPTIBLE),那么可以通过显式的唤醒呼叫(wakeup_process())或需要处理的信号来唤醒它。                           
但是,如果进程处于非可中断模式的睡眠状态(通过将其状态设置为 TASK_UNINTERRUPTIBLE),那么只能通过显式的唤醒呼叫将其唤醒。除非万不得已,否则我们建议您将进程置为可中断睡眠模式,而不是不可中断睡眠模式(比如说在设备 I/O 期间,处理信号非常困难时)。
当处于可中断睡眠模式的任务接收到信号时,它需要处理该信号(除非它已被屏弊),离开之前正在处理的任务(此处需要清除代码),并将 -EINTR 返回给用户空间。再一次,检查这些返回代码和采取适当操作的工作将由程序员完成。因此,懒惰的程序员可能比较喜欢将进程置为不可中断模式的睡眠状态,因为信号不会唤醒这类任务。但需要注意的一种情况是,对不可中断睡眠模式的进程的唤醒呼叫可能会由于某些原因不会发生,这会使进程无法被终止,从而最终引发问题,因为惟一的解决方法就是重启系统。一方面,您需要考虑一些细节,因为不这样做会在内核端和用户端引入 bug。另一方面,您可能会生成永远不会停止的进程(被阻塞且无法终止的进程)。
现在,我们在内核中实现了一种新的睡眠方法!
新睡眠状态:TASK_KILLABLE Linux Kernel 2.6.25 引入了一种新的进程睡眠状态,TASK_KILLABLE:当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号。清单 1 给出了内核 2.6.18 与内核 2.6.26 进程状态(定义在 include/linux/sched.h 中)之间的比较:
清单 1.  2.6.18 和 2.6.26 进程状态之间的比较
1
2
3
4
5
6
7
8
9
10
11
12
13
Linux Kernel 2.6.18                    Linux Kernel 2.6.26
=================================      ===================================
#define TASK_RUNNING            0      #define TASK_RUNNING            0
#define TASK_INTERRUPTIBLE      1      #define TASK_INTERRUPTIBLE      1
#define TASK_UNINTERRUPTIBLE    2      #define TASK_UNINTERRUPTIBLE    2
#define TASK_STOPPED            4      #define __TASK_STOPPED          4
#define TASK_TRACED             8      #define __TASK_TRACED           8
/* in tsk->exit_state */            /* in tsk->exit_state */
#define EXIT_ZOMBIE             16     #define EXIT_ZOMBIE             16
#define EXIT_DEAD               32     #define EXIT_DEAD               32
/* in tsk->state again */           /* in tsk->state again */
#define TASK_NONINTERACTIVE     64     #define TASK_DEAD               64
                                    #define TASK_WAKEKILL           128




注意,状态 TASK_INTERRUPTIBLE 和                TASK_UNINTERRUPTIBLE 并未修改。                TASK_WAKEKILL 用于在接收到致命信号时唤醒进程。
清单 2 展示了状态 TASK_STOPPED 和                TASK_TRACED 的修改之处(以及 TASK_KILLABLE 的定义):
清单 2. 内核 2.6.26 中的新状态定义
1
2
3
#define TASK_KILLABLE   (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED    (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED     (TASK_WAKEKILL | __TASK_TRACED)




换句话说,TASK_UNINTERRUPTIBLE +                TASK_WAKEKILL =                TASK_KILLABLE。
使用 TASK_KILLABLE 的新内核 API关于  的一些信息 完成机制的适用情况是:您希望将某个任务置为睡眠状态,但随后需要在某些事件完成时唤醒它。它提供了一种简单的、无竞态条件的同步机制。例程                    wait_for_completion(struct completion *comp)                    将使调用任务处于不可中断睡眠状态,除非完成已经发生。它要求通过                    complete(struct completion *comp) 或                    complete_all(struct completion *comp)                    函数来唤醒进程。
除了 wait_for_completion_killable() 之外,其他正在等待的例程包括:
有关完成结构的定义,请参阅 include/linux/completion.h。

让我们来看看这种新状态中的新函数。





欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0