歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux Kernel 學習筆記 Process Management

Linux Kernel 學習筆記 Process Management

日期:2017/2/28 16:33:35   编辑:Linux教程

struct task_struct;
struct thread_info;


Linux的Process信息保存在struct task_struct中,是由slab分配的

而另一個重要的數據結構是thread_info,是存在每個process堆棧的底部,這樣的好處是訪問的時候可以少用一個寄存器,thread_info有一個task的成員,指向它所隸屬的task_struct

有一個重要的宏current來表明當前的task,在x86平台,current是通過計算當前thread_info的task成員來得到的,宏或者是函數current_thread_info()可以非常方便的從當前process stack中得到當前的thread_info,而其成員task即是當前task即current_thread_info()->task;

Process State

  • TASK_RUNNING (Running or waiting on a runqueue)
  • TASK_INTERRUPTIBLE (Sleeping/blocked, waiting for some condition to exit)
  • TASK_UNINTERRUPTILBLE
  • TASK_ZOMBIE (Terminated, waiting parent's wait4() system call)
  • TASK_STOPPED (This occurs if the task receives the SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU signal or if itreceives any signal while it is being debugged.)

兩個函數可以操作

set_task_state(task, state);

set_current_state(state);


Process Creation

Linux通過clone()系統調用來實現的fork(), clone() 在內核中調用是的do_fork()函數, 而do_fork()又調用了copy_process()來forking process,copy_process()的流程如下:

  • call dup_task_struct,這個創建一個kernel stack/thread_info/task_struct給新的process;
  • 檢測是否超過了當前user的資源限制;
  • 將child和parent區分開,將process descriptor的成員清空或設置初始值;
  • child的state被置為TASK_UNINTERRUPTIBLE,來保證child還不被運行;
  • 設置child的task_struct的flags成員,標示是超級用戶權限的PF_SUPERPRIV被清空,標示還未調用exec()的PF_FORKNOEXEC被置上;
  • 通過get_pid()給pid成員賦值;
  • 根據傳給clone()的參數來決定是否賦值open files/file system info/signal handlers/process address space/name space
  • 分割parent的剩余時間片
  • 最後copy_process返回一個指向新child的指針給caller

The Linux Implementation of Threads


Linux實現thread的方法比較特別,在linux內核中並沒有thread這麼一個概念,所有的線程在Linux內核中被看做是標准進程,Linux Kernel並不提供針對線程的調度,取而代之的是,在Linux中線程僅僅是一個與其他進程共享某些特定資源的進程,每個線程有獨立的task_struct。


Linux的線程是由帶有如下參數的clone()系統調用創建的:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

參數的含義:

CLONE_VM: Parent and child share address space.

CLONE_FS: Parent and child share filesystem information

CLONE_FILES: Parent and child share open files.

CLONE_SIGHAND: Parent and child share signal handlers and blocked
signals.

Kernel Threads

Kernel thread可以由如下函數創建:

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)

這個函數實際上也是由clone()帶有一個CLONE_KERNEL參數而創建的

Process Termination

一個進程的結束,是由調用exit()系統調用來完成的,exit()將在內核中調起do_exit()函數,流程如下:

  • 將current->flag的PF_EXITING置上;
  • 調用del_timer_sync()來取消內核timer,當此函數返回時,可以保證沒有timer handler在運行以及沒有timer在queue中
  • 如果BSD accounting is enable, 則調用acct_process()來寫accounting info
  • 調用__exit_mm()來釋放被進程占用的mm_struct,如果沒有其他進程(線程)在使用這個內存空間,則deallocate
  • 調用exit_sem().若process有在queue中等待的信號量(sem),則在這裡將其dequeue
  • 調用__exit_files(), __exit_fs(), exit_namespace(), exit_sighand()用來減少對文件操作符以及filesystem data,process namespace,signal handler的引用計數;如果有引用計數為0,則這個對象沒有被任何process使用,於是就將其移除
  • 隨後,current->exit_code被設置,用於之後parent取得該值
  • 之後,調用exit_notify()來發送一個信號給parent,同時將current->state置為TASK_ZOMBIE
  • 最後,調用 schedule()將當前進程換出

Removal of the Process Descriptor


當do_exit()調用之後,task_struct實際上還是存在的,這個重要是要等parent取得有關child process的相關信息,當完成之後,wait4()系統調用將被調起,之後在內核中release_task()函數將被調起,流程如下:

  • 首先,調用free_uid(),用於減少process user的引用計數,linux維持一個per-user的cache,來表明當前用戶有多少個打開的file和打開的process,當這個引用計數為0的時候,這個cache將會銷毀
  • 運行unhash_process(),把process從pidhash和task list中移除
  • 若process是ptraced狀態(Debug),則reparents(不懂啥意思)to parent,然後從ptrace list中移除
  • 最後,運行put_task_struct()釋放包含著process kernel stack和thread_info的頁,以及由slab對收回有slab分配的task_struct結構體

The Dilemma of the Parentless Task

當一個進程沒有parent的時候(比如parent比child先退出),child進程會將current thread group中的一個作為自己的parent,或者如果沒有的話,將init作為自己的parent

Copyright © Linux教程網 All Rights Reserved