2.進程管理

創(chuàng)建進程

使用fork函數(shù)創(chuàng)建進程
int pid = fork();
在執(zhí)行此函數(shù)后,即從當前進程開了一個新的子進程莲趣。
pid=0表示當前是新進程趴樱,不為0代表當前為主進程,pid即子進程的編號拂到。

創(chuàng)建線程

  • 線程復制執(zhí)行二進制指令
  • 多進程缺點: 創(chuàng)建進程占用資源多; 進程間通信需拷貝內存, 不能共享
  • 線程相關操作
    • pthread_exit(A), A 是線程退出的返回值
    • pthread_attr_t 線程屬性, 用輔助函數(shù)初始化并設置值; 用完需要銷毀
    • pthread_create 創(chuàng)建線程, 四個參數(shù)(線程對象, 屬性, 運行函數(shù), 運行參數(shù))
    • pthread_join 獲取線程退出返回值, 多線程依賴 libpthread.so
    • 一個線程退出, 會發(fā)送信號給 其他所有同進程的線程
  • 線程中有三類數(shù)據(jù)
    • 線程棧本地數(shù)據(jù), 棧大小默認 8MB; 線程棧之間有保護間隔, 若誤入會引發(fā)段錯誤
    • 進程共享的全局數(shù)據(jù)
    • 線程級別的全局變量(線程私有數(shù)據(jù), pthread_key_create(key, destructer)); key 所有線程都可以訪問, 可填入各自的值(同名不同值的全局變量)
  • 數(shù)據(jù)保護
    • Mutex(互斥), 初始化; lock(沒搶到則阻塞)/trylock(沒搶到則返回錯誤碼); unlock; destroy
    • 條件變量(通知), 收到通知, 還是要搶鎖(由 wait 函數(shù)執(zhí)行); 因此條件變量與互斥鎖配合使用
    • 互斥鎖所謂條件變量的參數(shù), wait 函數(shù)會自動解鎖/加鎖
    • broadcast(通知); destroy


      image.png

      image.png

      image.png

進程數(shù)據(jù)結構

image.png
  • 內核中進程, 線程統(tǒng)一為任務, 由 taks_struct 表示
  • 通過鏈表串起 task_struct
  • task_struct 中包含: 任務ID; 任務狀態(tài); 信號處理相關字段; 調度相關字段; 親緣關系; 權限相關; 運行統(tǒng)計; 內存管理; 文件與文件系統(tǒng); 內核棧;
  • 任務 ID; 包含 pid, tgid 和 *group_leader
    • pid(process id, 線程的id); tgid(thread group id, 所屬進程[主線程]的id); group_leader 指向 tgid 的結構體
    • 通過對比 pid 和 tgid 可判斷是進程還是線程
  • 信號處理, 包含阻塞暫不處理; 等待處理; 正在處理的信號
    • 信號處理函數(shù)默認使用用戶態(tài)的函數(shù)棧, 也可以開辟新的棧專門用于信號處理, 由 sas_ss_xxx 指定
    • 通過 pending/shared_pending 區(qū)分進程和線程的信號
  • 任務狀態(tài); 包含 state; exit_state; flags
    • 準備運行狀態(tài) TASK_RUNNING
    • 睡眠狀態(tài):可中斷; 不可中斷; 可殺
      • 可中斷 TASK_INTERRUPTIBLE, 收到信號要被喚醒
      • 不可中斷 TASK_UNINTERRUPTIBLE, 收到信號不會被喚醒, 不能被kill, 只能重啟
      • 可殺 TASK_KILLABLE, 可以響應致命信號, 由不可中斷與 TASK_WAKEKILL 組合
    • 停止狀態(tài) TASK_STOPPED, 由信號 SIGSTOP, SIGTTIN, SIGTSTP 與 SIGTTOU 觸發(fā)進入
    • 調試跟蹤 TASK_TRACED, 被 debugger 等進程監(jiān)視時進入
    • 結束狀態(tài)(包含 exit_state)
      • EXIT_ZOMBIE, 父進程還沒有 wait()
      • EXIT_DEAD, 最終狀態(tài)
    • flags, 例如 PF_VCPU 表示運行在虛擬 CPU 上; PF_FORKNOEXEC _do_fork 函數(shù)里設置, exec 函數(shù)中清除
  • 進程調度; 包含 是否在運行隊列; 優(yōu)先級; 調度策略; 可以使用那些 CPU 等信息.
  • 運行統(tǒng)計信息, 包含用戶/內核態(tài)運行時間; 上/下文切換次數(shù); 啟動時間等;
  • 進程親緣關系
    • 擁有同一父進程的所有進程具有兄弟關系
    • 包含: 指向 parent; 指向 real_parent; 子進程雙向鏈表頭結點; 兄弟進程雙向鏈表頭結點
    • parent 指向的父進程接收進程結束信號
    • real_parent 和 parent 通常一樣; 但在 bash 中用 GDB 調試程序時, GDB 是 real_parent, bash 是 parent
  • 進程權限, 包含 real_cred 指針(誰能操作我); cred 指針(我能操作誰)
    • cred 結構體中標明多組用戶和用戶組 id
    • uid/gid(哪個用戶的進程啟動我)
    • euid/egid(按照哪個用戶審核權限, 操作消息隊列, 共享內存等)
    • fsuid/fsgid(文件操作時審核)
    • 這三組 id 一般一樣
    • 通過 chmod u+s program, 給程序設置 set-user-id 標識位, 運行時程序將進程 euid/fsuid 改為程序文件所有者 id
    • suid/sgid 可以用來保存 id, 進程可以通過 setuid 更改 uid
    • capability 機制, 以細粒度賦予普通用戶部分高權限 (capability.h 列出了權限)
      • cap_permitted 表示進程的權限
      • cap_effective 實際起作用的權限, cap_permitted 范圍可大于 cap_effective
      • cap_inheritable 若權限可被繼承, 在 exec 執(zhí)行時繼承的權限集合, 并加入 cap_permitted 中(但非 root 用戶不會保留 cap_inheritable 集合)
      • cap_bset 所有進程保留的權限(限制只用一次的功能)
      • cap_ambient exec 時, 并入 cap_permitted 和 cap_effective 中
  • 內存管理: mm_struct
  • 文件與文件系統(tǒng): 打開的文件, 文件系統(tǒng)相關數(shù)據(jù)結構
image.png
  • 用戶態(tài)/內核態(tài)切換執(zhí)行如何串起來
  • 用戶態(tài)函數(shù)棧; 通過 JMP + 參數(shù) + 返回地址 調用函數(shù)
    • 棧內存空間從高到低增長
    • 32位棧結構: 棧幀包含 前一個幀的 EBP + 局部變量 + N個參數(shù) + 返回地址
      • ESP: 棧頂指針; EBP: 椔肱ⅲ基址(棧幀最底部, 局部變量起始)
      • 返回值保存在 EAX 中
    • 64位棧結構: 結構類似
      • rax 保存返回結果; rsp 棧頂指針; rbp 椥盅基指針
      • 參數(shù)傳遞時, 前 6個放寄存器中(再由被調用函數(shù) push 進自己的棧, 用以尋址), 參數(shù)超過 6個壓入棧中
  • 內核棧結構:
    • Linux 為每個 task 分配了內核棧, 32位(8K), 64位(16K)
    • 棧結構: [預留8字節(jié) +] pt_regs + 內核棧 + 頭部 thread_info
    • thread_info 是 task_struct 的補充, 存儲于體系結構有關的內容
    • pt_regs 用以保存用戶運行上下文, 通過 push 寄存器到棧中保存
    • 通過 task_struct 找到內核棧
      • 直接由 task_struct 內的 stack 直接得到指向 thread_info 的指針
    • 通過內核棧找到 task_struct
      • 32位 直接由 thread_info 中的指針得到
      • 64位 每個 CPU 當前運行進程的 task_struct 的指針存放到 Per CPU 變量 current_task 中; 可調用 this_cpu_read_stable 進行讀取

進程調度

image.png

調度策略與調度類

  • 進程包括兩類: 實時進程(優(yōu)先級高),普通進程余寥;實時進程(0-99); 普通進程(100-139)
  • 兩種進程調度策略不同: task_struct->policy 指明采用哪種調度策略(有6種策略)
  • 實時調度策略, 高優(yōu)先級可搶占低優(yōu)先級進程
    • FIFO: 相同優(yōu)先級進程先來先得
    • RR: 輪流調度策略, 采用時間片輪流調度相同優(yōu)先級進程
    • Deadline: 在調度時, 選擇 deadline 最近的進程
  • 普通調度策略
    • normal: 普通進程
    • batch: 后臺進程, 可以降低優(yōu)先級
    • idle: 空閑時才運行
  • 調度類: task_struct 中 * sched_class 指向封裝了調度策略執(zhí)行邏輯的類(有5種)
    • stop: 優(yōu)先級最高. 將中斷其他所有進程, 且不能被打斷
    • dl: 實現(xiàn) deadline 調度策略
    • rt: RR 或 FIFO, 具體策略由 task_struct->policy 指定
    • fair: 普通進程調度
    • idle: 空閑進程調度
  • 普通進程的 fair 完全公平調度算法 CFS(Linux 實現(xiàn))
    • 記錄進程運行時間( vruntime 虛擬運行時間)
    • 優(yōu)先調度 vruntime 小的進程
    • 按照比例累計 vruntime, 使之考慮進優(yōu)先級關系
  • 調度隊列和調度實體
    • CFS 中需要對 vruntime 排序找最小, 不斷查詢更新, 因此利用紅黑樹實現(xiàn)調度隊列
    • task_struct 中有 實時, deadline 和 cfs 三個調度實體, cfs 調度實體即紅黑樹節(jié)點
    • 每個 CPU 都有 rq 結構體, 里面有 dl_rq, rt_rq 和 cfs_rq 三個調度隊列以及其他信息; 隊列描述該 CPU 所運行的所有進程
    • 先在 rt_rq 中找進程運行, 若沒有再到 cfs_rq 中找; cfs_rq 中 rb_root 指向紅黑樹根節(jié)點, rb_leftmost指向最左節(jié)點
  • 調度類如何工作
    • 調度類中有一個成員指向下一個調度類(按優(yōu)先級順序串起來)
    • 找下一個運行任務時, 按 stop-dl-rt-fair-idle 依次調用調度類, 不同調度類操作不同調度隊列

進程搶占

  • 搶占式調度
  • 兩種情況: 執(zhí)行太久, 需切換到另一進程; 另一個高優(yōu)先級進程被喚醒
    • 執(zhí)行太久: 由時鐘中斷觸發(fā)檢測, 中斷處理調用 scheduler_tick
      • 取當前進程 task_struct->task_tick_fair()->取 sched_entity cfs_rq 調用 entity_tick()
      • entity_tick() 調用 update_curr 更新當前進程 vruntime, 調用 check_preempt_tick 檢測是否需要被搶占
      • check_preempt_tick 中計算 ideal_runtime(一個調度周期中應該運行的實際時間), 若進程本次調度運行時間 > ideal_runtime, 則應該被搶占
      • 要被搶占, 則調用 resched_curr, 設置 TIF_NEED_RESCHED, 將其標記為應被搶占進程(因為要等待當前進程運行 __schedule)
    • 另一個高優(yōu)先級進程被喚醒: 當 I/O 完成, 進程被喚醒, 若優(yōu)先級高于當前進程則觸發(fā)搶占
      • try_to_wake_up()->ttwu_queue() 將喚醒任務加入隊列 調用 ttwu_do_activate 激活任務
      • 調用 tt_do_wakeup()->check_preempt_curr() 檢查是否應該搶占, 若需搶占則標記
  • 搶占時機: 讓進程調用 __schedule, 分為用戶態(tài)和內核態(tài)
    • 用戶態(tài)進程
      • 時機-1: 從系統(tǒng)調用中返回, 返回過程中會調用 exit_to_usermode_loop, 檢查 _TIF_NEED_RESCHED, 若打了標記, 則調用 schedule()
      • 時機-2: 從中斷中返回, 中斷返回分為返回用戶態(tài)和內核態(tài)(匯編代碼: arch/x86/entry/entry_64.S), 返回用戶態(tài)過程中會調用 exit_to_usermode_loop()->shcedule()
    • 內核態(tài)進程
      • 時機-1: 發(fā)生在 preempt_enable() 中, 內核態(tài)進程有的操作不能被中斷, 會調用 preempt_disable(), 在開啟時(調用 preempt_enable) 時是一個搶占時機, 會調用 preempt_count_dec_and_test(), 檢測 preempt_count 和標記, 若可搶占則最終調用 __schedule
      • 時機-2: 發(fā)生在中斷返回, 也會調用 __schedule
        image.png

進程創(chuàng)建

  • fork -> sys_call_table 轉換為 sys_fork()->_do_fork
  • 創(chuàng)建進程做兩件事: 復制初始化 task_struct; 喚醒新進程
  • 復制并初始化 task_struct, copy_process()
    • dup_task_struct: 分配 task_struct 結構體; 創(chuàng)建內核棧, 賦給* stack; 復制 task_struct, 設置 thread_info;
    • copy_creds: 分配 cred 結構體并復制, p->cred = p->real_cred = get_cred(new)
    • 初始化運行時統(tǒng)計量
    • sched_fork 調度相關結構體: 分配并初始化 sched_entity; state = TASK_NEW; 設置優(yōu)先級和調度類; task_fork_fair()->update_curr 更新當前進程運行統(tǒng)計量, 將當前進程 vruntime 賦給子進程, 通過 sysctl_sched_child_runs_first 設置是否讓子進程搶占, 若是則將其 sched_entity 放前頭, 并調用 resched_curr 做被搶占標記.
    • 初始化文件和文件系統(tǒng)變量
      • copy_files: 復制進程打開的文件信息, 用 files_struct 維護;
      • copy_fs: 復制進程目錄信息, 包括根目錄/根文件系統(tǒng); pwd 等, 用 fs_struct 維護
    • 初始化信號相關內容: 復制信號和處理函數(shù)
    • 復制內存空間: 分配并復制 mm_struct; 復制內存映射信息
    • 分配 pid
  • 喚醒新進程 wake_up_new_task()
    • state = TASK_RUNNING; activate 用調度類將當前子進程入隊列
    • 其中 enqueue_entiry 中會調用 update_curr 更新運行統(tǒng)計量, 再加入隊列
    • 調用 check_preempt_curr 看是否能搶占, 若 task_fork_fair 中已設置 sysctl_sched_child_runs_first, 直接返回, 否則進一步比較并調用 resched_curr 做搶占標記
    • 若父進程被標記會被搶占, 則系統(tǒng)調用 fork 返回過程會調度子進程
image.png

線程創(chuàng)建

  • 線程的創(chuàng)建
  • 線程是由內核態(tài)和用戶態(tài)合作完成的, pthread_create 是 Glibc 庫的一個函數(shù)
  • pthread_create 中
  1. 設置線程屬性參數(shù), 如線程棧大小
  2. 創(chuàng)建用戶態(tài)維護線程的結構, pthread
  3. 創(chuàng)建線程棧 allocate_stack
    • 取棧的大小, 在棧末尾加 guardsize
    • 在進程堆中創(chuàng)建線程棧(先嘗試調用 get_cached_stack 從緩存回收的線程棧中取用)
    • 若無緩存線程棧, 調用 __mmap 創(chuàng)建
    • 將 pthread 指向椓祛恚空間中
    • 計算 guard 內存位置, 并設置保護
    • 填充 pthread 內容, 其中 specific 存放屬于線程的全局變量
    • 線程棧放入 stack_used 鏈表中(另外 stack_cache 鏈表記錄回收緩存的線程棧)
  4. 設置運行函數(shù), 參數(shù)到 pthread 中
  5. 調用 create_thread 創(chuàng)建線程
    • 設置 clone_flags 標志位, 調用 __clone
    • clone 系統(tǒng)調用返回時, 應該要返回到新線程上下文中, 因此 __clone 將參數(shù)和指令位置壓入棧中, 返回時從該函數(shù)開始執(zhí)行
  6. 內核調用 __do_fork
    • 在 copy_process 復制 task_struct 過程中, 五大數(shù)據(jù)結構不復制, 直接引用進程的
    • 親緣關系設置: group_leader 和 tgid 是當前進程; real_parent 與當前進程一樣
    • 信號處理: 數(shù)據(jù)結構共享, 處理一樣
  7. 返回用戶態(tài), 先運行 start_thread 同樣函數(shù)
    • 在 start_thread 中調用用戶的函數(shù), 運行完釋放相關數(shù)據(jù)
    • 如果是最后一個線程直接退出
    • 或調用 __free_tcb 釋放 pthread 以及線程棧, 從 stack_used 移到 stack_cache 中
image.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宋舷,隨后出現(xiàn)的幾起案子绪撵,更是在濱河造成了極大的恐慌,老刑警劉巖肥缔,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲兢,死亡現(xiàn)場離奇詭異,居然都是意外死亡续膳,警方通過查閱死者的電腦和手機改艇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坟岔,“玉大人谒兄,你說我怎么就攤上這事∩绺叮” “怎么了承疲?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵邻耕,是天一觀的道長。 經(jīng)常有香客問我燕鸽,道長兄世,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任啊研,我火速辦了婚禮御滩,結果婚禮上,老公的妹妹穿的比我還像新娘党远。我一直安慰自己削解,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布沟娱。 她就那樣靜靜地躺著氛驮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪济似。 梳的紋絲不亂的頭發(fā)上矫废,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音砰蠢,去河邊找鬼磷脯。 笑死,一個胖子當著我的面吹牛娩脾,可吹牛的內容都是我干的。 我是一名探鬼主播打毛,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼柿赊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了幻枉?” 一聲冷哼從身側響起碰声,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熬甫,沒想到半個月后胰挑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡椿肩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年瞻颂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郑象。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡贡这,死狀恐怖,靈堂內的尸體忽然破棺而出厂榛,到底是詐尸還是另有隱情盖矫,我是刑警寧澤丽惭,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站辈双,受9級特大地震影響责掏,放射性物質發(fā)生泄漏。R本人自食惡果不足惜湃望,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一换衬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喜爷,春花似錦冗疮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至湃密,卻和暖如春诅挑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泛源。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工拔妥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人达箍。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓没龙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缎玫。 傳聞我的和親對象是個殘疾皇子硬纤,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容