- 论坛徽章:
- 0
|
Kernel Threads
Traditional Unix systems delegate some critical tasks to intermittently running processes, including flushing disk caches, swapping out unused pages, servicing network connections, and so on. Indeed, it is not efficient to perform these tasks in strict linear fashion; both their functions and the end user processes get better response if they are scheduled in the background. Because some of the system processes run only in Kernel Mode, modern operating systems delegate their functions to kernel threads, which are not encumbered with the unnecessary User Mode context. In Linux, kernel threads differ from regular processes in the following ways:
传统Unix系统将一些关键的任务委派给一些周期运行的进程,包括刷新磁盘缓冲,交换不用的页面,提供网络连接服务等等。以严格线性的方式执行这些任务是没有效率的;如果它们是在后台调度的话,它们的函数和终端用户进程都会得到更好的响应。因为一些系统进程只在内核空间运行,现代操作系统将它们的函数委派给内核线程,它没有被不必要的用户空间上下文拖累。在Linux中,内核线程和普通进程的区别如下:
· Kernel threads run only in Kernel Mode, while regular processes run alternatively in Kernel Mode and in User Mode.
内核线程仅在内核空间运行,而普通进程可有选择地在内核空间和用户空间运行。
· Because kernel threads run only in Kernel Mode, they use only linear addresses greater than PAGE_OFFSET. Regular processes, on the other hand, use all four gigabytes of linear addresses, in either User Mode or Kernel Mode.
因为内核线程在内核空间运行,它们仅使用比PAGE_OFFSET大的线性地址空间。而普通进程,使用整个4G线性地址空间,既包括用户空间,又包括内核空间。
Creating a kernel thread
The kernel_thread( ) function creates a new kernel thread. It receives as parameters the address of the kernel function to be executed (fn), the argument to be passed to that function (arg), and a set of clone flags (flags). The function essentially invokes do_fork( ) as follows:
函数kernel_thread()创建一个新的内核线程。它接收如下参数:表示将要执行的内核函数的地址(fn),传递给这个函数的参数(arg),和一系列clone标志(flags)。这个函数最终这样调用do_fork()函数:
do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, pregs, 0, NULL, NULL);
The CLONE_VM flag avoids the duplication of the page tables of the calling process: this duplication would be a waste of time and memory, because the new kernel thread will not access the User Mode address space anyway. The CLONE_UNTRACED flag ensures that no process will be able to trace the new kernel thread, even if the calling process is being traced.
CLONE_VM标志避免了复制调用进程的页表:这种复制是时间和空间的浪费,因为新的内核线程永远不会访问用户空间地址。CLONE_UNTRACED标志保证没有进程可以跟踪这个新的内核线程,即使调用进程正被跟踪。
The pregs parameter passed to do_fork( ) corresponds to the address in the Kernel Mode stack where the copy_thread( ) function will find the initial values of the CPU registers for the new thread. The kernel_thread( ) function builds up this stack area so that:
传递给do_fork()的参数pregs对应于copy_thread()将为新的线程找到的CPU寄存器初始化值在内核空间栈的地址。函数kernel_thread()如下建立这个栈:
· The ebx and edx registers will be set by copy_thread() to the values of the parameters fn and arg, respectively.
寄存器ebx和edx将由copy_thread()分别设置为fn和arg参数的值。
· The eip register will be set to the address of the following assembly language fragment:
寄存器eip将设置为下列汇编语言块的地址:
movl %edx,%eax
pushl %edx
call *%ebx
pushl %eax
call do_exit
Therefore, the new kernel thread starts by executing the fn(arg) function. If this function terminates, the kernel thread executes the _exit( ) system call passing to it the return value of fn( ) (see the section "Destroying Processes" later in this chapter).
因此,新的内核线程执行fn(arg)函数开始。如果这个函数结束了,内核线程执行_exit()系统调用,将fn()返回值传递给它。
Process 0
The ancestor of all processes, called process 0, the idle process, or, for historical reasons, the swapper process, is a kernel thread created from scratch during the initialization phase of Linux (see Appendix A). This ancestor process uses the following statically allocated data structures (data structures for all other processes are dynamically allocated):
所有进程的先祖,称为0进程,空闲进程,或者,由于历史原因,封装器进程,是在Linux初始化阶段创建的内核线程。这个先祖进程使用下列静态分配的数据结构(所有其他进程的数据结构都是动态分配的):
· A process descriptor stored in the init_task variable, which is initialized by the INIT_TASK macro.
存储在init_task变量里的进程描述符,由INIT_TASK宏初始化。
· A thread_info descriptor and a Kernel Mode stack stored in the init_thread_union variable and initialized by the INIT_THREAD_INFO macro.
一个thread_info描述符和内核空间栈,存放在init_thread_union变量中,由INIT_THREAD_INFO宏初始化。
· The following tables, which the process descriptor points to:
进程描述符指向的下列这些表:
o init_mm
o init_fs
o init_files
o init_signals
o init_sighand
The tables are initialized, respectively, by the following macros:
分别由下列宏初始化:
o INIT_MM
o INIT_FS
o INIT_FILES
o INIT_SIGNALS
o INIT_SIGHAND
· The master kernel Page Global Directory stored in swapper_pg_dir (see the section "Kernel Page Tables" in Chapter 2).
控制内核PGD存储在swapper_pg_dir中。
The start_kernel( ) function initializes all the data structures needed by the kernel, enables interrupts, and creates another kernel thread, named process 1 (more commonly referred to as the init process ):
函数start_kernel()初始化内核需要的所有数据结构,开启中断,并创建另一个内核线程,1进程(更通常地称为init进程):
kernel_thread(init, NULL, CLONE_FS|CLONE_SIGHAND);
The newly created kernel thread has PID 1 and shares all per-process kernel data structures with process 0. When selected by the scheduler, the init process starts executing the init( ) function.
新创建的内核线程的PID为1,并与0进程共享所有每进程内核数据结构。当调度器选中时,init进程从执行init()函数开始。
After having created the init process, process 0 executes the cpu_idle( ) function, which essentially consists of repeatedly executing the hlt assembly language instruction with the interrupts enabled (see Chapter 4). Process 0 is selected by the scheduler only when there are no other processes in the TASK_RUNNING state.
创建init进程之后,0进程执行cpu_idle()函数,它实质上包含不停地执行hlt汇编指令,在中断开启状态。仅当没有其他进程处在TASK_RUNNING状态时,调度器选中0进程。
In multiprocessor systems there is a process 0 for each CPU. Right after the power-on, the BIOS of the computer starts a single CPU while disabling the others. The swapper process running on CPU 0 initializes the kernel data structures, then enables the other CPUs and creates the additional swapper processes by means of the copy_process( ) function passing to it the value 0 as the new PID. Moreover, the kernel sets the cpu field of the thread_info descriptor of each forked process to the proper CPU index.
在多处理器系统中,每个CPU都有一个0进程。在上电之后,计算机的BIOS从一个CPU开始,而关闭其他CPU。在CPU 0上运行的封装器进程初始化内核数据结构,然后开启其他的CPU,并通过传递0作为新的PID给copy_process()函数创建额外的封装器进程。此外,内核将每个复制的进程的thread_info结构的cpu成员设置为对应的CPU下标。
Process 1
The kernel thread created by process 0 executes the init( ) function, which in turn completes the initialization of the kernel. Then init( ) invokes the execve( ) system call to load the executable program init. As a result, the init kernel thread becomes a regular process having its own per-process kernel data structure (see Chapter20). The init process stays alive until the system is shut down, because it creates and monitors the activity of all processes that implement the outer layers of the operating system.
由0进程创建的内核线程,执行函数init(),它完成了内核的初始化。函数init()调用execve()系统调用来启动可执行程序init。结果,init内核线程成为一个拥有自己每进程内核数据结构的普通进程。init进程一直活着,直到系统关闭,因为它创建并监控实现操作系统外层的所有进程的活动。
Other kernel threads
Linux uses many other kernel threads. Some of them are created in the initialization phase and run until shutdown; others are created "on demand," when the kernel must execute a task that is better performed in its own execution context.
Linux使用了其他一些内核线程。一些是在初始化阶段创建的,并运行到系统关闭;其他是按需创建,当内核必须执行一个最好是在它自己的执行上下文中进行的任务。
A few examples of kernel threads (besides process 0 and process 1) are:
keventd (also called events)
Executes the functions in the keventd_wq workqueue (see Chapter 4).
执行keventd_wq工作队列上的函数。
kapmd
Handles the events related to the Advanced Power Management (APM).
处理与APM(高级电源管理)相关的事件。
kswapd
Reclaims memory, as described in the section "Periodic Reclaiming" in Chapter 17.
回收内存。
pdflush
Flushes "dirty" buffers to disk to reclaim memory, as described in the section "The pdflush Kernel Threads" in Chapter 15.
将脏缓冲区送到磁盘,以便回收内存。
kblockd
Executes the functions in the kblockd_workqueue workqueue. Essentially, it periodically activates the block device drivers, as described in the section "Activating the Block Device Driver" in Chapter 14.
执行kblockd_workqueue工作队列上的函数。实际上,它周期地启动块设备驱动程序。
ksoftirqd
Runs the tasklets (see section "Softirqs and Tasklets" in Chapter 4); there is one of these kernel threads for each CPU in the system.
运行tasklet;系统中每个CPU都有一个这样的线程。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/65228/showart_674004.html |
|