?? 4.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富國,http://winking.126.com"> <TITLE>進程</TITLE></HEAD><BODY style="font-family: 宋體; font-size: 9pt"> <CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" ><TR><TD ALIGN=CENTER><FONT SIZE=+2><!--標題由此開始-->進程</TD></TR></TABLE></CENTER><p><h3>目 錄</h3><!--目錄由此開始--><A NAME="Content" ID="Content"></A><OL><LI><A HREF="#I407">進程</A></LI><OL><LI><A HREF="#I408">信號</A></LI><LI><A HREF="#I409">sched.c</A></LI><LI><A HREF="#I410">進程信號隊列</A></LI><LI><A HREF="#I411">SMP</A></LI><LI><A HREF="#I412">內核線程頁目錄的借用</A></LI><LI><A HREF="#I413">代碼分析</A></LI><LI><A HREF="#I414">線程</A></LI><LI><A HREF="#I415">進程描述符</A></LI><LI><A HREF="#I416">init進程從內核態(tài)切換到用戶態(tài)</A></LI><LI><A HREF="#I417">SET_LINKS</A></LI><LI><A HREF="#I418">REMOVE_LINKS</A></LI><LI><A HREF="#I419">get_wchan()</A></LI><LI><A HREF="#I420">sigframe的結構</A></LI><LI><A HREF="#I421">rt_sigframe結構</A></LI><LI><A HREF="#I422">信號隊列的結構</A></LI><LI><A HREF="#I423">內核線程簡介</A></LI><LI><A HREF="#I424">進程切換簡介</A></LI><LI><A HREF="#I425">同步機制</A></LI></OL></OL><hr><br><A NAME="I407" ID="I407"></A><center><b><font size=+2>進程</font></b></center><br>一 進程調度<p> 進程的狀態(tài)([include/linux.h]):<p>TASK_RUNNING, it means that it is in the "Ready List"<br>TASK_INTERRUPTIBLE, task waiting for a signal or a resource (sleeping)<br>TASK_UNINTERRUPTIBLE, task waiting for a resource (sleeping), it is in same "Wait Queue"<br>TASK_ZOMBIE, task child without father<br>TASK_STOPPED, task being debugged<p> ______________ CPU Available ______________<br> | | ----------------> | |<br> | TASK_RUNNING | | Real Running |<br> |______________| <---------------- |______________|<br> CPU Busy<br> | /|\<br>Waiting for | | Resource<br> Resource | | Available<br> \|/ |<br> ______________________<br> | |<br> | TASK_INTERRUPTIBLE / |<br> | TASK-UNINTERRUPTIBLE |<br> |______________________|<p> Main Multitasking Flow<p> 從系統(tǒng)內核的角度看來,一個進程僅僅是進程控制表(process table)中的一項。進程控制表中的每一項都是一個task_struct 結構,而task_struct 結構本身是在include/linux/sched.h中定義的。在task_struct結構中存儲各種低級和高級的信息,包括從一些硬件設備的寄存器拷貝到進程的工作目錄的鏈接點。<p> 進程控制表既是一個數(shù)組,又是一個雙向鏈表,同時又是一個樹。其物理實現(xiàn)是一個包括多個指針的靜態(tài)數(shù)組。此數(shù)組的長度保存在include/linux/tasks.h 定義的常量NR_TASKS中,其缺省值為128,數(shù)組中的結構則保存在系統(tǒng)預留的內存頁中。鏈表是由next_task 和prev_task兩個指針實現(xiàn)的,而樹的實現(xiàn)則比較復雜。<p> 系統(tǒng)啟動后,內核通常作為某一個進程的代表。一個指向task_struct的全局指針變量current用來記錄正在運行的進程。變量current只能由kernel/sched.c中的進程調度改變。當系統(tǒng)需要查看所有的進程時,則調用for_each_task,這將比系統(tǒng)搜索數(shù)組的速度要快得多。<p>二、用戶進程和內核線程<p> 某一個進程只能運行在用戶方式(user mode)或內核方式(kernel mode)下。用戶程序運行在用戶方式下,而系統(tǒng)調用運行在內核方式下。在這兩種方式下所用的堆棧不一樣:用戶方式下用的是一般的堆棧,而內核方式下用的是固定大小的堆棧(一般為一個內存頁的大小)<p> 盡管linux是一個宏內核系統(tǒng),內核線程依然存在,以便并行地處理一些內核的“家務室”。這些任務不占用USER memory(用戶空間),而僅僅使用KERNEL memory。和其他內核模塊一樣,它們也在高級權限(i386系統(tǒng)中的RING 0)下工作作。內核線程是被kernel_thread [arch/i386/kernel/process]創(chuàng)建的,它又通過調用著名的clone系統(tǒng)調用[arch/i386/kernel/process.c] (類似fork系統(tǒng)調用的所有功能都是由它最終實現(xiàn)):<p>int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)<br>{<br> long retval, d0;<p> __asm__ __volatile__(<br> "movl %%esp,%%esi\n\t"<br> "int $0x80\n\t" /* Linux/i386 system call */<br> "cmpl %%esp,%%esi\n\t" /* child or parent? */<br> "je 1f\n\t" /* parent - jump */<br> /* Load the argument into eax, and push it. That way, it does<br> * not matter whether the called function is compiled with<br> * -mregparm or not. */<br> "movl %4,%%eax\n\t"<br> "pushl %%eax\n\t"<br> "call *%5\n\t" /* call fn */<br> "movl %3,%0\n\t" /* exit */<br> "int $0x80\n"<br> "1:\t"<br> :"=&a" (retval), "=&S" (d0)<br> :"0" (__NR_clone), "i" (__NR_exit),<br> "r" (arg), "r" (fn),<br> "b" (flags | CLONE_VM)<br> : "memory");<br> return retval;<br>}<p> 一旦調用,我們就有了一個新的任務(Task) (一般PID都很小, 例如2,3,等) 等待一個響應很慢的資源,例如swap或者usb事件,以便同步。下面是一些最常用的內核線程(你可以用ps x命令):<p>PID COMMAND<br> 1 init<br> 2 keventd<br> 3 kswapd<br> 4 kreclaimd<br> 5 bdflush<br> 6 kupdated<br> 7 kacpid<br>67 khubd<p> init內核線程也是啟動以后最初的進程。 它會調用其它用戶模式的任務,(/etc/inittab)例如控制臺守護進程(daemons), tty守護進程以及網絡守護進程(rc腳本)。<p>下面是一個典型的內核線程kswapd [mm/vmscan.c].<br>kswapd是被clone()建立的 [arch/i386/kernel/process.c]''<p>|do_initcalls<br> |kswapd_init<br> |kernel_thread<br> |syscall fork (in assembler)<p>·do_initcalls [init/main.c]<br>·kswapd_init [mm/vmscan.c]<br>·kernel_thread [arch/i386/kernel/process.c]<p>三 進程創(chuàng)建,運行和消失<p> Linux系統(tǒng)使用系統(tǒng)調用fork( )來創(chuàng)建一個進程,使用exit( )來結束進程。fork( )和exit( )的源程序保存在kernel/fork.c and kernel/exit.c中。fork( )的主要任務是初始化要創(chuàng)建進程的數(shù)據(jù)結構,其主要的步驟有:<p>1)申請一個空閑的頁面來保存task_struct。<br>2)查找一個空的進程槽(find_empty_process( ))。<br>3)為kernel_stack_page申請另一個空閑的內存頁作為堆棧。<br>4)將父進程的LDT表拷貝給子進程。<br>5)復制父進程的內存映射信息。<br>6)管理文件描述符和鏈接點。<p>|sys_fork<br> |do_fork<br> |alloc_task_struct<br> |__get_free_pages<br> |p->state = TASK_UNINTERRUPTIBLE<br> |copy_flags<br> |p->pid = get_pid<br> |copy_files<br> |copy_fs<br> |copy_sighand<br> |copy_mm // should manage CopyOnWrite (I part)<br> |allocate_mm<br> |mm_init<br> |pgd_alloc -> get_pgd_fast<br> |get_pgd_slow<br> |dup_mmap<br> |copy_page_range<br> |ptep_set_wrprotect<br> |clear_bit // set page to read-only<br> |copy_segments // For LDT<br> |copy_thread<br> |childregs->eax = 0<br> |p->thread.esp = childregs // child fork returns 0<br> |p->thread.eip = ret_from_fork // child starts from fork exit<br> |retval = p->pid // parent fork returns child pid<br> |SET_LINKS // insertion of task into the list pointers<br> |nr_threads++ // Global variable<br> |wake_up_process(p) // Now we can wake up just created child<br> |return retval<p>·sys_fork [arch/i386/kernel/process.c]<br>·do_fork [kernel/fork.c]<br>·alloc_task_struct [include/asm/processor.c]<br>·__get_free_pages [mm/page_alloc.c]<br>·get_pid [kernel/fork.c]<br>·copy_files<br>·copy_fs<br>·copy_sighand<br>·copy_mm<br>·allocate_mm<br>·mm_init<br>·pgd_alloc -> get_pgd_fast [include/asm/pgalloc.h]<br>·get_pgd_slow<br>·dup_mmap [kernel/fork.c]<br>·copy_page_range [mm/memory.c]<br>·ptep_set_wrprotect [include/asm/pgtable.h]<br>·clear_bit [include/asm/bitops.h]<br>·copy_segments [arch/i386/kernel/process.c]<br>·copy_thread<br>·SET_LINKS [include/linux/sched.h]<br>·wake_up_process [kernel/sched.c]<p> 撤消一個進程可能稍微復雜些,因為撤消子進程必須通知父進程。另外,使用kill( )也可以結束一個進程。sys_kill( )、sys_wait( )和sys_exit( )都保存在文件exit.c中。<p> 使用fork ( )創(chuàng)建一個進程后,程序的兩個拷貝都在運行。通常一個拷貝使用exec ( )調用另一個拷貝。系統(tǒng)調用exec ( )負責定位可執(zhí)行文件的二進制代碼,并負責裝入和運行。Linux系統(tǒng)中的exec ( )通過使用linux_binfmt結構支持多種二進制格式。每種二進制格式都代表可執(zhí)行代碼和鏈接庫。linux _binfmt結構種包含兩個指針,一個指向裝入可執(zhí)行代碼的函數(shù),另一個指向裝入鏈接庫的函數(shù)。<p> Unix系統(tǒng)提供給程序員6種調用exec( ) 的方法。其中的5種是作為庫函數(shù)實現(xiàn),而sys_execve( )是由系統(tǒng)內核實現(xiàn)的。它執(zhí)行一個十分簡單的任務:裝入可執(zhí)行文件的文件頭,并試圖執(zhí)行它。如果文件的頭兩個字節(jié)是#! ,那么它就調用在文件第一行中所指定的解釋器,否則,它將逐個嘗試注冊的二進制格式。<center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I408" ID="I408"></A><center><b><font size=+2>信號</font></b></center><br>struct semaphore {<br> atomic_t count; 進程抓取semaphore時減1<br> int sleepers; 抓取semaphore失敗時增1<br> wait_queue_head_t wait; semaphore的等待隊列<br>};<br> down(&sem) 編繹成:<p> movl $sem,% ecx 通過寄存器ecx向__down函數(shù)傳遞sem指針<br> decl sem<br> js 2f 如果為負值,表示semaphore已被占用,執(zhí)行__down_failed過程<br>1:<br>由于出現(xiàn)semaphore競爭的可能性比較小,將分支代碼轉移到.text.lock段,以縮短正常的指令路徑.<br>.section .text.lock,"ax"<br>2: call __down_failed<br> jmp 1b<br>.previous<br> ...<p> up(&sem) 編繹成:<p> movl $sem,% ecx<br> incl sem<br> jle 2f 如果小于或等于0,表示該semaphore有進程在等待,就去調用__up_wakeup<br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -