Linux內核深度解析之進程管理丨內含贈書福利(一)

      網友投稿 996 2022-05-29

      2.1 進程

      linux內核把進程稱為任務(task),進程的虛擬地址空間分為用戶虛擬地址空間和內核虛擬地址空間,所有進程共享內核虛擬地址空間,每個進程有獨立的用戶虛擬地址空間。

      進程有兩種特殊形式:沒有用戶虛擬地址空間的進程稱為內核線程,共享用戶虛擬地址空間的進程稱為用戶線程,通常在不會引起混淆的情況下把用戶線程簡稱為線程。共享同一個用戶虛擬地址空間的所有用戶線程組成一個線程組。

      C標準庫的進程術語和Linux內核的進程術語的對應關系如表2.1所示。

      表2.1 進程術語的對應關系

      結構體task_struct是進程描述符,其主要成員如表2.2所示。

      表2.2 進程描述符task_struct的主要成員

      2.2 命名空間

      和虛擬機相比,容器是一種輕量級的虛擬化技術,直接使用宿主機的內核,使用命名空間隔離資源。Linux內核提供的命名空間如表2.3所示。

      表2.3 命名空間

      可以使用以下兩種方法創建新的命名空間。

      (1)調用clone創建子進程時,使用標志位控制子進程是共享父進程的命名空間還是創建新的命名空間。

      (2)調用unshare創建新的命名空間,不和已存在的任何其他進程共享命名空間。

      進程也可以使用系統調用setns,綁定到一個已經存在的命名空間。

      如圖2.1所示,進程描述符的成員“nsproxy”指向一個命名空間代理,命名空間代理包含除了用戶以外的所有其他命名空間的地址。如果父進程和子進程共享除了用戶以外的所有其他命名空間,那么它們共享一個命名空間代理。

      圖2.1 進程的命名空間

      本節只介紹進程號命名空間。

      進程號命名空間用來隔離進程號,對應的結構體是pid_namespace。每個進程號命名空間獨立分配進程號。進程號命名空間按層次組織成一棵樹,初始進程號命名空間是樹的根,對應全局變量init_pid_ns,所有進程默認屬于初始進程號命名空間。

      創建進程時,從進程所屬的進程號命名空間到初始進程號命名空間都會分配進程號。如圖2.2所示,假設某個進程屬于進程號命名空間b,b的父命名空間是a,a的父命名空間是初始進程號命名空間,從b到初始的每一級命名空間依次分配進程號10、20和30。

      圖2.2 進程號命名空間

      2.3 進程標識符

      進程有以下標識符。

      (1)進程標識符:進程所屬的進程號命名空間到根的每層命名空間,都會給進程分配一個標識符。

      (2)線程組標識符:多個共享用戶虛擬地址空間的進程組成一個線程組,線程組中的主進程稱為組長,線程組標識符就是組長的進程標識符。當調用系統調用clone傳入標志CLONE_THREAD以創建新進程時,新進程和當前進程屬于一個線程組。

      進程描述符的成員tgid存放線程組標識符,成員group_leader指向組長的進程描述符。

      (3)進程組標識符:多個進程可以組成一個進程組,進程組標識符是組長的進程標識符。進程可以使用系統調用setpgid創建或者加入一個進程組。會話和進程組被設計用來支持shell作業控制,shell為執行單一命令或者管道的進程創建一個進程組。進程組簡化了向進程組的所有成員發送信號的操作。

      (4)會話標識符:多個進程組可以組成一個會話。當進程調用系統調用setsid的時候,創建一個新的會話,會話標識符是該進程的進程標識符。創建會話的進程是會話的首進程。

      Linux是多用戶操作系統,用戶登錄時會創建一個會話,用戶啟動的所有進程都屬于這個會話。登錄shell是會話首進程,它所使用的終端就是會話的控制終端,會話首進程通常也被稱為控制進程。當用戶退出登錄時,所有屬于這個會話的進程都將被終止。

      假設某個進程屬于進程號命名空間b,b的父命名空間是a,a的父命名空間是初始進程號命名空間,從b到初始的每一級命名空間分配的進程號依次是10、20和30。進程標識符數據結構如圖2.3所示,進程描述符的相關成員如下。

      (1)成員pid存儲全局進程號,即初始進程號命名空間分配的進程號30。

      (2)成員pids[PIDTYPE_PID].pid指向結構體pid,存放3個命名空間分配的進程號。

      (3)成員pids[PIDTYPE_PGID].pid指向進程組組長的結構體pid(限于篇幅,圖2.3中沒畫出)。

      (4)成員pids[PIDTYPE_SID].pid指向會話首進程的結構體pid(限于篇幅,圖2.3中沒畫出)。

      進程標識符結構體pid的成員如下。

      (1)成員count是引用計數。

      (2)成員level是進程所屬的進程號命名空間的層次。

      (3)數組numbers的元素個數是成員level的值加上1,3個元素依次存放初始命名空間、a和b三個命名空間分配的進程號。numbers[i].nr是進程號命名空間分配的進程號,numbers[i].ns指向進程號命名空間的結構體pid_namespace,numbers[i].pid_chain用來把進程加入進程號散列表pid_hash,根據進程號和命名空間計算散列值。

      圖2.3 進程標識符數據結構

      2.4 進程關系

      進程1分叉生成進程2,進程1稱為父進程,進程2稱為子進程。

      進程1多次分叉生成進程2和進程3,進程2和進程3的關系是兄弟關系。

      如圖2.4所示,一個進程的所有子進程被鏈接在一條子進程鏈表上,頭節點是父進程的成員children,鏈表節點是子進程的成員sibling。子進程的成員real_parent指向父進程的進程描述符,成員parent用來干什么呢?如果子進程被某個進程(通常是調試器)使用系統調用ptrace跟蹤,那么成員parent指向跟蹤者的進程描述符,否則成員parent也指向父進程的進程描述符。

      如圖2.5所示,進程管理子系統把所有進程鏈接在一條進程鏈表上,頭節點是0號線程的成員tasks,鏈表節點是每個進程的成員tasks。對于線程組,只把組長加入進程鏈表。

      圖2.4 父子進程

      圖2.5 進程和線程鏈表

      一個線程組的所有線程鏈接在一條線程鏈表上,頭節點是組長的成員thread_group,鏈表節點是線程的成員thread_group。線程的成員group_leader指向組長的進程描述符,成員tgid是線程組標識符,成員pid存放自己的進程標識符。

      2.5 啟動程序

      當我們在shell進程里面執行命令“/sbin/hello.elf &”以啟動程序“hello”時,shell進程首先創建子進程,然后子進程裝載程序“hello.elf”,其代碼如下:

      下面描述創建新進程和裝載程序的過程。

      2.5.1 創建新進程

      在Linux內核中,新進程是從一個已經存在的進程復制出來的。內核使用靜態數據構造出0號內核線程,0號內核線程分叉生成1號內核線程和2號內核線程(kthreadd線程)。1號內核線程完成初始化以后裝載用戶程序,變成1號進程,其他進程都是1號進程或者它的子孫進程分叉生成的;其他內核線程是kthreadd線程分叉生成的。

      3個系統調用可以用來創建新的進程。

      (1)fork(分叉):子進程是父進程的一個副本,采用了寫時復制的技術。

      (2)vfork:用于創建子進程,之后子進程立即調用execve以裝載新程序的情況。為了避免復制物理頁,父進程會睡眠等待子進程裝載新程序。現在fork采用了寫時復制的技術,vfork失去了速度優勢,已經被廢棄。

      Linux內核深度解析之進程管理丨內含贈書福利(一)

      (3)clone(克隆):可以精確地控制子進程和父進程共享哪些資源。這個系統調用的主要用處是可供pthread庫用來創建線程。

      clone是功能最齊全的函數,參數多,使用復雜,fork是clone的簡化函數。

      我們先介紹Linux內核定義系統調用的獨特方式,以系統調用fork為例:

      把宏展開以后是:

      “SYSCALL_DEFINE”后面的數字表示系統調用的參數個數,“SYSCALL_DEFINE0”表示系統調用沒有參數,“SYSCALL_DEFINE6”表示系統調用有6個參數,如果參數超過6個,使用宏“SYSCALL_DEFINEx”。

      “asmlinkage”表示這個C語言函數可以被匯編代碼調用。如果使用C++編譯器,“asmlinkage”被定義為extern "C";如果使用C編譯器,“asmlinkage”是空的宏。

      系統調用的函數名稱以“sys_”開頭。

      創建新進程的進程p和生成的新進程的關系有3種情況。

      (1)新進程是進程p的子進程。

      (2)如果clone傳入標志位CLONE_PARENT,那么新進程和進程p擁有同一個父進程,是兄弟關系。

      (3)如果clone傳入標志位CLONE_THREAD,那么新進程和進程p屬于同一個線程組。

      創建新進程的3個系統調用在文件“kernel/fork.c”中,它們把工作委托給函數_do_fork。

      1.函數_do_fork

      函數_do_fork的原型如下:

      參數如下。

      (1)參數clone_flags是克隆標志,最低字節指定了進程退出時發給父進程的信號,創建線程時,該參數的最低字節是0,表示線程退出時不需要向父進程發送信號。

      (2)參數stack_start只在創建線程時有意義,用來指定新線程的用戶棧的起始地址。

      (3)參數stack_size只在創建線程時有意義,用來指定新線程的用戶棧的長度。這個參數已經廢棄。

      (4)參數parent_tidptr只在創建線程時有意義,如果參數clone_flags指定了標志位CLONE_PARENT_SETTID,那么調用線程需要把新線程的進程標識符寫到參數parent_tidptr指定的位置,也就是新線程保存自己的進程標識符的位置。

      (5)參數child_tidptr只在創建線程時有意義,存放新線程保存自己的進程標識符的位置。如果參數clone_flags指定了標志位CLONE_CHILD_CLEARTID,那么線程退出時需要清除自己的進程標識符。如果參數clone_flags指定了標志位CLONE_CHILD_SETTID,那么新線程第一次被調度時需要把自己的進程標識符寫到參數child_tidptr指定的位置。

      (6)參數tls只在創建線程時有意義,如果參數clone_flags指定了標志位CLONE_SETTLS,那么參數tls指定新線程的線程本地存儲的地址。

      如圖2.6所示,函數_do_fork的執行流程如下。

      圖2.6 函數_do_fork的執行流程

      (1)調用函數copy_process以創建新進程。

      (2)如果參數clone_flags設置了標志CLONE_PARENT_SETTID,那么把新線程的進程標識符寫到參數parent_tidptr指定的位置。

      (3)調用函數wake_up_new_task以喚醒新進程。

      (4)如果是系統調用vfork,那么當前進程等待子進程裝載程序。

      2.函數copy_process

      創建新進程的主要工作由函數copy_process實現,其執行流程如圖2.7所示。

      圖2.7 函數copy_process的執行流程

      (1)檢查標志:以下標志組合是非法的。

      1)同時設置CLONE_NEWNS和CLONE_FS,即新進程屬于新的掛載命名空間,同時和當前進程共享文件系統信息。

      2)同時設置CLONE_NEWUSER和CLONE_FS,即新進程屬于新的用戶命名空間,同時和當前進程共享文件系統信息。

      3)設置CLONE_THREAD,未設置CLONE_SIGHAND,即新進程和當前進程屬于同一個線程組,但是不共享信號處理程序。

      4)設置CLONE_SIGHAND,未設置CLONE_VM,即新進程和當前進程共享信號處理程序,但是不共享虛擬內存。

      5)新進程想要和當前進程成為兄弟進程,并且當前進程是某個進程號命名空間中的1號進程。這種標志組合是非法的,說明1號進程不存在兄弟進程。

      6)新進程和當前進程屬于同一個線程組,同時新進程屬于不同的用戶命名空間或者進程號命名空間。這種標志組合是非法的,說明同一個線程組的所有線程必須屬于相同的用戶命名空間和進程號命名空間。

      (2)函數dup_task_struct:函數dup_task_struct為新進程的進程描述符分配內存,把當前進程的進程描述符復制一份,為新進程分配內核棧。

      如圖2.8所示,進程描述符的成員stack指向內核棧。

      圖2.8 進程的內核棧

      內核棧的定義如下:

      內核棧有兩種布局。

      1)結構體thread_info占用內核棧的空間,在內核棧頂部,成員task指向進程描述符。

      2)結構體thread_info沒有占用內核棧的空間,是進程描述符的第一個成員。

      兩種布局的區別是結構體thread_info的位置不同。如果選擇第二種布局,需要打開配置宏CONFIG_THREAD_INFO_IN_TASK。ARM64架構使用第二種內核棧布局。第二種內核棧布局的好處是:thread_info結構體作為進程描述符的第一個成員,它的地址和進程描述符的地址相同。當進程在內核模式運行時,ARM64架構的內核使用用戶棧指針寄存器SP_EL0存放當前進程的thread_info結構體的地址,通過這個寄存器既可以得到thread_info結構體的地址,也可以得到進程描述符的地址。

      內核棧的長度是THREAD_SIZE,它由各種處理器架構自己定義,ARM64架構定義的內核棧長度是16KB。

      結構體thread_info存放匯編代碼需要直接訪問的底層數據,由各種處理器架構定義,ARM64架構定義的結構體如下。

      1)flags:底層標志,常用的標志是_TIF_SIGPENDING和_TIF_NEED_RESCHED,前者表示進程有需要處理的信號,后者表示調度器需要重新調度進程。

      2)addr_limit:進程可以訪問的地址空間的上限。對于進程,它的值是用戶地址空間的上限;對于內核線程,它的值是內核地址空間的上限。

      3)preempt_count:搶占計數器。

      (3)檢查用戶的進程數量限制:如果擁有當前進程的用戶創建的進程數量達到或者超過限制,并且用戶不是根用戶,也沒有忽略資源限制的權限(CAP_SYS_RESOURCE)和系統管理權限(CAP_SYS_ADMIN),那么不允許創建新進程。

      (4)函數copy_creds:函數copy_creds負責復制或共享證書,證書存放進程的用戶標識符、組標識符和訪問權限。

      如果設置了標志CLONE_THREAD,即新進程和當前進程屬于同一個線程組,那么新進程和當前進程共享證書,如圖2.9所示。

      圖2.9 線程共享證書

      否則,子進程復制當前進程的證書,如果設置了標志CLONE_NEWUSER,那么需要為新進程創建新的用戶命名空間,新的用戶命名空間是當前進程的用戶命名空間的子命名空間。

      最后把用戶的進程數量統計值加1。

      (5)檢查線程數量限制:如果線程數量達到允許的線程最大數量,那么不允許創建新進程。

      全局變量nr_threads 存放當前的線程數量;max_threads存放允許創建的線程最大數量,默認值是MAX_THREADS。

      (6)函數sched_fork:函數sched_fork為新進程設置調度器相關的參數,其主要代碼如下。

      第6行代碼,調用函數__sched_fork以執行基本設置。

      第7行代碼,把新進程的狀態設置為TASK_NEW。

      第9行代碼,把新進程的調度優先級設置為當前進程的正常優先級。為什么不設置為當前進程的調度優先級?因為當前進程可能因為占有實時互斥鎖而被臨時提升了優先級。

      第11~23行代碼,如果當前進程使用sched_setscheduler設置調度策略和相關參數時設置了標志SCHED_RESET_ON_FORK,要求創建新進程時把新進程的調度策略和優先級設置為默認值,那么處理如下。

      第12~15行代碼,如果當前進程是限期進程或實時進程,那么把新進程的調度策略恢復成SCHED_NORMAL,把nice值設置成默認值0,對應靜態優先級120。

      第16行和第17行代碼,如果當前進程是普通進程,并且nice值小于0,那么把新進程的nice值恢復成默認值0,對應靜態優先級120。

      第25~32行代碼,根據新進程的調度優先級設置調度類。

      第25~27行代碼,如果調度優先級是限期調度類的優先級,那么返回“-EAGAIN”,因為不允許限期進程分叉生成新的限期進程。

      第28行和第29行代碼,如果調度優先級是實時調度類的優先級,那么把調度類設置為實時調度類。

      第30行和第31行代碼,如果調度優先級是公平調度類的優先級,那么把調度類設置為公平調度類。

      第37行代碼,調用函數__set_task_cpu,設置新進程在哪個處理器上,如果開啟公平組調度和實時組調度,那么還需要設置新進程屬于哪個公平運行隊列和哪個實時運行隊列。

      第38行和第39行代碼,執行調度類的task_fork方法。

      第46行代碼,初始化新進程的搶占計數器,在搶占式內核中設置為2,在非搶占式內核中設置為0。因為在搶占式內核中,如果函數schedule()在調度進程時選中了新進程,那么調用函數rq_unlock_irq()和sched_preempt_enable_no_resched()時會把新進程的搶占計數減兩次。

      (7)復制或者共享資源如下。

      1)UNIX系統5信號量。只有屬于同一個線程組的線程之間才會共享UNIX系統5信號量。函數copy_semundo處理UNIX系統5信號量的共享問題,其代碼如下:

      第6~11行代碼,如果調用者傳入標志CLONE_SYSVSEM,表示共享UNIX系統5信號量,那么新進程和當前進程共享UNIX系統5信號量的撤銷請求鏈表,對應結構體sem_undo_list,把計數加1。當進程退出時,內核需要把信號量的計數值加上該進程曾經減去的數值。

      否則,在第12行和第13行代碼中,新進程的UNIX系統5信號量的撤銷請求鏈表是空的。

      2)打開文件表。只有屬于同一個線程組的線程之間才會共享打開文件表。函數copy_files復制或者共享打開文件表,其代碼如下:

      第10~13行代碼,如果調用者傳入標志CLONE_FILES,表示共享打開文件表,那么新進程和當前進程共享打開文件表的結構體files_struct,把計數加1。

      否則,在第15行代碼中,新進程把當前進程的打開文件表復制一份。

      3)文件系統信息。進程的文件系統信息包括根目錄、當前工作目錄和文件模式創建掩碼。只有屬于同一個線程組的線程之間才會共享文件系統信息。

      函數copy_fs復制或者共享文件系統信息,其代碼如下:

      第4~13行代碼,如果調用者傳入標志CLONE_FS,表示共享文件系統信息,那么新進程和當前進程共享文件系統信息的結構體fs_struct,把計數users加1。

      否則,在第14行代碼中,新進程把當前進程的文件系統信息復制一份。

      4)信號處理程序。只有屬于同一個線程組的線程之間才會共享信號處理程序。函數copy_sighand復制或者共享信號處理程序,其代碼如下:

      第5~8行代碼,如果調用者傳入標志CLONE_SIGHAND,表示共享信號處理程序,那么新進程和當前進程共享信號處理程序的結構體sighand_struct,把計數加1。

      否則,在第9~15行代碼中,新進程把當前進程的信號處理程序復制一份。

      5)信號結構體。只有屬于同一個線程組的線程之間才會共享信號結構體。函數copy_signal復制或共享信號結構體,其代碼如下:

      第5行代碼,如果調用者傳入標志CLONE_THREAD,表示創建線程,那么新進程和當前進程共享信號結構體signal_struct。

      否則,在第8~20行代碼中,為新進程分配信號結構體,然后初始化,繼承當前進程的資源限制。

      6)虛擬內存。只有屬于同一個線程組的線程之間才會共享虛擬內存。函數copy_mm復制或共享虛擬內存,其主要代碼如下:

      第15~19行代碼,如果調用者傳入標志CLONE_VM,表示共享虛擬內存,那么新進程和當前進程共享內存描述符mm_struct,把計數mm_users加1。

      否則,在第22~28行代碼中,新進程復制當前進程的虛擬內存。

      7)命名空間。函數copy_namespaces創建或共享命名空間,其代碼如下:

      第7~12行代碼,如果共享除了用戶以外的所有其他命名空間,那么新進程和當前進程共享命名空間代理結構體nsproxy,把計數加1。

      第14行和第15行代碼,如果進程沒有系統管理權限,那么不允許創建新的命名空間。

      第17~19行代碼,如果既要求創建新的進程間通信命名空間,又要求共享UNIX系統5信號量,那么這種要求是不合理的。

      第21行代碼,創建新的命名空間代理,然后創建或者共享命名空間。

      如果設置了標志CLONE_NEWNS,那么創建新的掛載命名空間,否則共享掛載命名空間。

      如果設置了標志CLONE_NEWUTS,那么創建新的UTS命名空間,否則共享UTS命名空間。

      如果設置了標志CLONE_NEWIPC,那么創建新的進程間通信命名空間,否則共享進程間通信命名空間。

      如果設置了標志CLONE_NEWPID,那么創建新的進程號命名空間,否則共享進程號命名空間。

      如果設置了標志CLONE_NEWCGROUP,那么創建新的控制組命名空間,否則共享控制組命名空間。

      如果設置了標志CLONE_NEWNET,那么創建新的網絡命名空間,否則共享網絡命名空間。

      8)I/O上下文。函數copy_io創建或者共享I/O上下文,其代碼如下:

      第10~12行代碼,如果調用者傳入標志CLONE_IO,表示共享I/O上下文,那么共享I/O上下文的結構體io_context,把計數nr_tasks加1。

      否則,在第13~20行代碼中,創建新的I/O上下文,然后初始化,繼承當前進程的I/O優先級。

      9)復制寄存器值。調用函數copy_thread_tls復制當前進程的寄存器值,并且修改一部分寄存器值。如圖2.10所示,進程有兩處用來保存寄存器值:從用戶模式切換到內核模式時,把用戶模式的各種寄存器保存在內核棧底部的結構體pt_regs中;進程調度器調度進程時,切換出去的進程把寄存器值保存在進程描述符的成員thread中。因為不同處理器架構的寄存器不同,所以各種處理器架構需要自己定義結構體pt_regs和thread_struct,實現函數copy_thread_tls。

      圖2.10 進程保存寄存器值處

      ARM64架構的函數copy_thread_tls把主要工作委托給函數copy_thread,函數copy_thread的代碼如下:

      執行過程如下。

      第6行代碼,把新進程的進程描述符的成員thread.cpu_context清零,在調度進程時切換出去的進程使用這個成員保存通用寄存器的值。

      第8~30行代碼,如果是用戶進程,其處理過程如下。

      第9行代碼,子進程把當前進程內核棧底部的pt_regs結構體復制一份。當前進程從用戶模式切換到內核模式時,把用戶模式的各種寄存器保存一份放在內核棧底部的pt_regs結構體中。

      第10行代碼,把子進程的X0寄存器設置為0,因為X0寄存器存放系統調用的返回值,調用fork或clone后,子進程返回0。

      第16行代碼,把子進程的TPIDR_EL0寄存器設置為當前進程的TPIDR_EL0寄存器的值。TPIDR_EL0是用戶讀寫線程標識符寄存器(User Read and Write Thread ID Register),pthread庫用來存放每線程數據的基準地址,存放每線程數據的區域通常被稱為線程本地存儲(Thread Local Storage,TLS)。

      第18~23行代碼,如果使用系統調用clone創建線程時指定了用戶棧的起始地址,那么把新線程的棧指針寄存器SP_EL0設置為用戶棧的起始地址。

      第29行和第30行代碼,如果使用系統調用clone創建線程時設置了標志位CLONE_SETTLS,那么把新線程的TPIDR_EL0寄存器設置為系統調用clone第4個參數tls指定的線程本地存儲的地址。

      第31~39行代碼,如果是內核線程,其處理過程如下。

      第32行代碼,把子進程內核棧底部的pt_regs結構體清零。

      第33行代碼,把子進程的處理器狀態設置為PSR_MODE_EL1h,值為5,其中第0位是棧指針選擇符(ARM64架構在異常級別1時可以使用異常級別1的棧指針寄存器SP_EL1,也可以使用異常級別0的棧指針寄存器SP_EL0),值為1表示選擇棧指針寄存器SP_EL1;第2、3位是異常級別,值為1表示異常級別1。

      第37行代碼,把子進程的x19寄存器設置為線程函數的地址,注意參數stack_start存放線程函數的地址,即用來創建內核線程的函數kernel_thread的第一個參數fn。

      第38行代碼,把子進程的x20寄存器設置為傳給線程函數的參數,注意參數stk_sz存放傳給線程函數的參數,即用來創建內核線程的函數kernel_thread的第二個參數arg。

      第40行代碼,把子進程的程序計數器設置為函數ret_from_fork。當子進程被調度時,從函數ret_from_fork開始執行。

      第41行代碼,把子進程的棧指針寄存器SP_EL1設置為內核棧底部pt_regs結構體的起始位置。

      (8)設置進程號和進程關系。函數copy_process的最后部分為新進程設置進程號和進程關系,其主要代碼如下:

      第1~7行代碼,為新進程分配進程號。從新進程所屬的進程號命名空間一直到根,每層進程號命名空間為新進程分配一個進程號。

      pid等于init_struct_pid的地址,這是什么意思呢?在內核初始化時,引導處理器為每個從處理器分叉生成一個空閑線程(參考函數idle_threads_init),所有處理器的空閑線程使用進程號0,全局變量init_struct_pid存放空閑線程的進程號。

      第12~23行代碼,分情況設置新進程退出時發送給父進程的信號,設置新進程所屬的線程組。

      1)第12~15行代碼,如果是創建線程,那么把新線程的成員exit_signal設置為?1,新線程退出時不需要發送信號給父進程;因為新線程和當前線程屬于同一個線程組,所以成員group_leader指向同一個組長,成員tgid存放組長的進程號。

      2)第16~23行代碼,如果是創建進程,執行過程如下。

      第17行和第18行代碼,如果傳入標志CLONE_PARENT,新進程和當前進程是兄弟關系,那么新進程的成員exit_signal等于當前進程所屬線程組的組長的成員exit_signal。

      第19行和第20行代碼,如果沒有傳入標志CLONE_PARENT,新進程和當前進程是父子關系,那么新進程的成員exit_signal是調用者指定的信號。

      第21行和第22行代碼,新進程所屬線程組的組長是自己。

      第27~29行代碼,控制組的進程數控制器檢查是否允許創建新進程:從當前進程所屬的控制組一直到控制組層級的根,如果其中一個控制組的進程數量大于或等于限制,那么不允許使用fork和clone創建新進程。

      控制組(cgroup)的進程數(PIDs)控制器:用來限制控制組及其子控制組中的進程使用fork和clone創建的新進程的數量,如果進程p所屬的控制組到控制組層級的根,其中有一個控制組的進程數量大于或等于限制,那么不允許進程p使用fork和clone創建新進程。

      第33~39行代碼,為新進程設置父進程。

      第34行代碼,如果傳入了標志CLONE_PARENT(表示擁有相同的父進程)或者CLONE_THREAD(表示創建線程),那么新進程和當前進程擁有相同的父進程。

      第37行代碼,否則,新進程的父進程是當前進程。

      第46行代碼,把新進程的成員pids[PIDTYPE_PID].pid指向第2行代碼分配的進程號結構體。

      第47~64行代碼,如果是創建新進程,執行下面的處理過程。

      第48行代碼,因為新進程和當前進程屬于同一個進程組,所以成員pids[PIDTYPE_PGID].pid指向同一個進程組的組長的進程號結構體。

      第49行代碼,因為新進程和當前進程屬于同一個會話,所以成員pids[PIDTYPE_SID].pid指向同一個會話的控制進程的進程號結構體。

      第51~53行代碼,如果新進程是1號進程,那么新進程是進程號命名空間中的孤兒進程領養者,忽略致命的信號,因為1號進程是不能殺死的。如果把1號進程殺死了,誰來領養孤兒進程呢?

      第60行代碼,把新進程添加到父進程的子進程鏈表中。

      第61行代碼,把新進程添加到進程鏈表中,鏈表節點是成員tasks,頭節點是空閑線程的成員tasks(init_task.tasks)。

      第62行代碼,把新進程添加到進程組的進程鏈表中。

      第63行代碼,把新進程添加到會話的進程鏈表中。

      第65~73行代碼,如果是創建線程,執行下面的處理過程。

      第66行代碼,把線程組的線程計數值加1。

      第67行代碼,把線程組的第2個線程計數值加1,這個計數值是原子變量。

      第68行代碼,把信號結構體的引用計數加1。

      第69行和第70行代碼,把線程加入線程組的線程鏈表中,鏈表節點是成員thread_group,頭節點是組長的成員thread_group。

      第71行和第72行代碼,把線程加入線程組的第二條線程鏈表中,鏈表節點是成員thread_node,頭節點是信號結構體的成員thread_head。

      第74行代碼,把新進程添加到進程號結構體的進程鏈表中。

      第75行代碼,把線程計數值加1。

      第83行代碼,調用函數proc_fork_connector,通過進程事件連接器向用戶空間通告進程事件PROC_EVENT_FORK。進程可以通過進程事件連接器監視進程事件:創建協議號為NETLINK_CONNECTOR的netlink套接字,然后綁定到多播組CN_IDX_PROC。

      本文轉載自異步社區

      任務調度 Linux

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:《Office 2019高效辦公三合一從入門到精通 : 視頻自學版》 —2.3.3修訂文檔
      下一篇:云合同電子合同:6項國家標準正式發布,電子證照將有自己的“身份證”
      相關文章
      亚洲综合图片小说区热久久| 亚洲国产精品久久66| 亚洲精彩视频在线观看| 亚洲乱码日产一区三区| 精品亚洲成α人无码成α在线观看 | 亚洲av日韩av无码av| 亚洲熟妇av一区二区三区下载| 久久久久久亚洲Av无码精品专口| 亚洲国产精品久久| 久久精品国产亚洲AV香蕉| 中文字幕亚洲精品资源网| 亚洲黄色中文字幕| 亚洲精品在线电影| 亚洲一区在线视频观看| 久久久国产亚洲精品| 亚洲日韩国产欧美一区二区三区 | 亚洲中文字幕无码亚洲成A人片| 精品亚洲AV无码一区二区三区 | 亚洲日本va在线视频观看| 国产av天堂亚洲国产av天堂| 久久亚洲国产精品| 久久精品国产亚洲av麻豆小说| 亚洲电影唐人社一区二区| 亚洲国产成人九九综合| 美女视频黄免费亚洲| 亚洲av无码成人影院一区| 亚洲片国产一区一级在线观看 | 中文字幕在线观看亚洲日韩| 亚洲精品无码av片| 亚洲人成网站观看在线播放| 国产AV无码专区亚洲AV漫画| 亚洲av伊人久久综合密臀性色| 亚洲综合精品一二三区在线| 亚洲制服丝袜一区二区三区| 亚洲色中文字幕在线播放| 精品久久久久亚洲| 亚洲熟妇av一区二区三区| 亚洲成a人片在线观看中文动漫| 亚洲欧洲日产国码在线观看| 亚洲综合小说另类图片动图| 国产综合成人亚洲区|