ninesunqian
??
[
資料
] [
站內(nèi)短信
] [
Blog
]
|
2樓
發(fā)表于 2008-9-28 09:05?
|
進(jìn)程創(chuàng)建: clone, fork, vfork系統(tǒng)調(diào)用
-
??clone系統(tǒng)調(diào)用
-
? ?參數(shù):
-
? ? 執(zhí)行函數(shù)(fn), 參數(shù)(arg)
-
? ? flags|死亡時(shí)給父進(jìn)程發(fā)的信號(hào) (clone_flags): 以下介紹clone_flags
-
? ???資源共享
-
? ?? ?段,頁,打開文件共享:
-
? ?? ? 頁表(不是頁, CLONE_VM),
-
? ?? ? 打開文件(clone_files),
-
? ?? ? 建一個(gè)新tls段(clone_settls)
-
? ?? ?路徑和權(quán)限設(shè)置:
-
? ?? ? clone_fs: 共享根目錄, 當(dāng)前目錄, 創(chuàng)建文件初始權(quán)限.
-
? ?? ? clone_newns: 新的根路徑, 自己的視野看文件系統(tǒng)
-
? ?? ?線程通信
-
? ?? ? clone_sighand: 信號(hào)處理action, 阻塞和懸掛的信號(hào)
-
? ?? ? clone_sysvsem: 共享undoable信號(hào)量操作
-
? ???進(jìn)程關(guān)系
-
? ?? ?同父: clone_parent 創(chuàng)建進(jìn)程與新進(jìn)程是兄弟 (同父), 新進(jìn)程不是創(chuàng)建進(jìn)程的子進(jìn)程
-
? ?? ? 為了方便期間, 以下討論暫時(shí)不考慮這一因素(它很容易實(shí)現(xiàn)), 認(rèn)為創(chuàng)建進(jìn)程就是父進(jìn)程
-
? ?? ?同一個(gè)線程組: clone_thread. 屬于同一個(gè)進(jìn)程(線程組)
-
? ?? ?都被trace: clone_ptrace
-
? ?? ?子進(jìn)程不被trace: clone_untrace (內(nèi)核設(shè)置, 覆蓋clone_ptrace)
-
? ???返回tid
-
? ?? ?向父進(jìn)程返回tid: clone_parent_settid
-
? ?? ?向子進(jìn)程返回tid: clone_child_settid
-
? ???子進(jìn)程的狀態(tài):
-
? ?? ?子進(jìn)程開始就stop: clone_stopped
-
? ???進(jìn)程死亡或exec通知:
-
? ?? ?啟動(dòng)內(nèi)核機(jī)制: 如果子進(jìn)程死亡或exec, 它自己空間內(nèi)的tid(*ctid)清零, 并喚醒等待子進(jìn)程死亡的進(jìn)程.
-
? ? 賦給子進(jìn)程的資源
-
? ???子進(jìn)程的棧(父進(jìn)程alloc的內(nèi)存地址)
-
? ???線程局部倉庫段(tls)
-
? ? 返回子進(jìn)程tid的地址
-
? ???父進(jìn)程用戶空間內(nèi)的地址
-
? ???子進(jìn)程用戶空間的地址
-
??clone, fork, vfork實(shí)現(xiàn)方式
-
??大致相同:
-
? ? 系統(tǒng)調(diào)用服務(wù)例程sys_clone, sys_fork, sys_vfork三者最終都是調(diào)用do_fork函數(shù)完成.
-
? ? do_fork的參數(shù)與clone系統(tǒng)調(diào)用的參數(shù)類似, 不過多了一個(gè)regs(內(nèi)核棧保存的用戶模式寄存器). 實(shí)際上其他的參數(shù)也都是用regs取的
-
? ?區(qū)別在于:
-
? ? clone:
-
? ???clone的API外衣, 把fn, arg壓入用戶棧中, 然后引發(fā)系統(tǒng)調(diào)用. 返回用戶模式后下一條指令就是fn.
-
? ???sysclone: parent_tidptr, child_tidptr都傳到了 do_fork的參數(shù)中
-
? ???sysclone: 檢查是否有新的棧, 如果沒有就用父進(jìn)程的棧 (開始地址就是regs.esp)
-
? ? fork, vfork:
-
? ???服務(wù)例程就是直接調(diào)用do_fork, 不過參數(shù)稍加修改
-
? ???clone_flags:
-
? ?? ?sys_fork: SIGCHLD|0;
-
? ?? ?sys_vfork: SIGCHLD| (clone_vfork | clone_vm)
-
? ???用戶棧: 都是父進(jìn)程的棧.
-
? ???parent_tidptr, child_ctidptr都是NULL.
-
具體實(shí)現(xiàn)函數(shù)do_fork() (內(nèi)核函數(shù))的工作流程:
-
??分配PID, 確定子進(jìn)程到底是否traced.
-
? ?分配空閑的PID
-
? ?確定clone_ptrace位. (確定子進(jìn)程到底要不要被trace, 而不是參數(shù)所說的希望被trace)
-
? ? 設(shè)置該位: 參數(shù)已設(shè)該位, 且創(chuàng)建線程被trace中
-
? ? 清除該位: 父進(jìn)程沒有被trace, 或 clone_untrace已經(jīng)設(shè)置.
-
??復(fù)制進(jìn)程描述符(copy_process)
-
? ?檢查clone_flags是否兼容, 是否安全
-
? ? clone_newns 與 clone_fs 互斥
-
? ? clone_sighand 是 clone_thread 的必要條件: 線程必須共享信號(hào)處理
-
? ? clone_vm 是 clone_sighand 的必要條件 : 共享信號(hào)處理, 首先要共享信號(hào)處理的代碼(在進(jìn)程頁面里)
-
? ? 附加的安全檢查: security_task_create(clone_flags)
-
? ?復(fù)制進(jìn)程描述符
-
? ? 在父進(jìn)程的thread_info里保存浮點(diǎn)寄存器: __unlazy_fpu()
-
? ? 分配新的進(jìn)程pd(alloc_task_struct), 并拷貝父進(jìn)程pd
-
? ? 分配新的thread_info(alloc_thread_info), 并拷貝父進(jìn)程的thread_info.
-
? ? 新的thread_info和新分配的pd 相互引, 新pd的引用計(jì)數(shù)設(shè)為2 (表示:新pd有用, 且不是僵尸進(jìn)程)
-
? ?相關(guān)計(jì)數(shù)加1: (此處先相關(guān)計(jì)數(shù)檢查, 都通過后再都加1)
-
? ? 檢查并增加: 用戶擁有進(jìn)程數(shù), 系統(tǒng)總共進(jìn)程數(shù).
-
? ???一般來說, 所有進(jìn)程的thread_info總和, 不超過物理內(nèi)存的1/8
-
? ? 新進(jìn)程的可執(zhí)行格式的引用計(jì)數(shù)(FIXME: pd里標(biāo)有可執(zhí)行個(gè)數(shù)嗎)
-
? ? 系統(tǒng)執(zhí)行fork總數(shù).
-
? ?進(jìn)程pd的關(guān)鍵域的設(shè)置(順序與源碼可能不一致):
-
? ? 進(jìn)程關(guān)系
-
? ???設(shè)置父子關(guān)系 (parent, real_parent, 考慮被trace的情況)
-
? ???設(shè)置新pd的PID
-
? ???設(shè)置tgid, 線程組長的pd(pd->group_leader). (根據(jù)是不是線程組長, 即clone_thread位是否為0)
-
? ???加入PID哈希表(pid, tgid, 如果是進(jìn)程組長加入pgid和sid表),(調(diào)attach_pid())
-
? ???拷貝tid到父進(jìn)程的用戶空間(parent_tidptr)
-
? ? 拷貝資源(如果clone_flags沒標(biāo)明共享):
-
? ???文件,目錄,內(nèi)存:copy_files, copy_mm, copy_namespace,
-
? ???進(jìn)程通信: copy_signal, copy_sighand, copy_semundo
-
? ? 設(shè)置子進(jìn)程的內(nèi)核棧(thread_info), 內(nèi)核態(tài)相關(guān)寄存器(thread_struct, 不知道這個(gè)結(jié)構(gòu)的具體用處): copy_thread()
-
? ???子進(jìn)程的thread_struct:
-
? ?? ?esp, esp0 - 內(nèi)核棧頂, 內(nèi)核棧底
-
? ?? ?eip - ret_from_fork()的地址 (用戶態(tài)切到內(nèi)核態(tài)的第一條指令)
-
? ?? ?I/O許可位圖 - 如果父進(jìn)程有, 就拷貝一份過來
-
? ?? ?TLS - 如果用戶空間提供了TLS段, 拷貝過來
-
? ???設(shè)置子進(jìn)程的內(nèi)核棧:
-
? ?? ?child_regs.esp = 傳入的棧地址參數(shù);
-
? ?? ?child_regs.eax = 0, 給用戶態(tài)的返回值是0
-
? ?? ?清除thread_info中的, TIF_SYSCALL_TRACE位, 防止運(yùn)行ret_from_fork時(shí), 系統(tǒng)通知調(diào)試進(jìn)程
-
? ?? ?設(shè)置子進(jìn)程的thread_info的cpuid
-
? ? 設(shè)置調(diào)度信息(sched_fork())
-
? ???設(shè)置task_running狀態(tài),
-
? ???初始化調(diào)度參數(shù)(時(shí)間片),
-
? ???子進(jìn)程禁止內(nèi)核搶占(thread_info.preempt_cout = 1)
-
? ? 其他:
-
? ???如果沒有被trace,pd->ptrace = 0;
-
? ???設(shè)置pd->exit_signal:
-
? ?? ?有clone_thread位: 設(shè)為參數(shù)clone_flags中的退出信號(hào)
-
? ?? ?沒有clone_thread位: 設(shè)為-1 (表示進(jìn)程終止時(shí), 該LWP不給父進(jìn)程發(fā)信號(hào))
-
? ???pd->flags: 清除PF_SUPERPRIV , 設(shè)置PF_FORKNOEXEC
-
? ???大內(nèi)核鎖 pd->lock_depth = -1
-
? ???exec次數(shù): pd->did_exec = 0
-
? ???拷貝child_tidptr到pd->set_child_tid. 以備子進(jìn)程開始執(zhí)行時(shí), 把tid放到自己內(nèi)存空間的child_tidptr
-
? ?返回pd
-
??設(shè)置父子進(jìn)程的運(yùn)行狀態(tài), 調(diào)度信息
-
? ?設(shè)置子進(jìn)程的狀態(tài).
-
? ? 掛信號(hào): 如果創(chuàng)建出來的是停止(clone_stopped)或被trace(pd->ptrace里有PT_PTRACE位)的進(jìn)程, 懸掛一個(gè)SIGSTOP信號(hào).
-
? ???只有debugger發(fā)出SIGCONT信號(hào)后, 才能進(jìn)入運(yùn)行狀態(tài)
-
? ? 設(shè)狀態(tài),入列隊(duì):如果有clone_stopped位, 子進(jìn)程設(shè)為stopped狀態(tài); 否則調(diào)用wake_up_new_task(), 把子進(jìn)程加入就緒列隊(duì):
-
? ???調(diào)整父進(jìn)程和子進(jìn)程的調(diào)度參數(shù) (主要是時(shí)間片)
-
? ???如果父子在同一CPU上運(yùn)行, 且頁表不同享, 子進(jìn)程在插在父進(jìn)程前
-
? ?? ?子進(jìn)程很可能exec, 不與父進(jìn)程共享頁. 這樣防止父進(jìn)程無用的copy on write.
-
? ???如果不同CPU上運(yùn)行, 或者共享頁表, 子進(jìn)程放在列隊(duì)最后
-
? ?如果父進(jìn)程處于被調(diào)試狀態(tài), 程通知調(diào)試器
-
? ? 當(dāng)前進(jìn)程給debugger進(jìn)程發(fā)信號(hào), 告知自己創(chuàng)建了子進(jìn)程; 并停止自己(進(jìn)入traced狀態(tài)), 使debugger運(yùn)行.
-
? ???子進(jìn)程的pid保存在current->ptrace_message中, 供debugger用
-
? ???調(diào)試器發(fā)信號(hào), 使父進(jìn)程繼續(xù)后, 再進(jìn)行下一步; 否則父進(jìn)程一直處于traced狀態(tài)
-
? ?設(shè)置父進(jìn)程狀態(tài)
-
? ? 如果有clone_vfork, 把自己放到一個(gè)等待列隊(duì).
-
? ???內(nèi)核處理完系統(tǒng)調(diào)用后, 會(huì)執(zhí)行調(diào)度, 這樣就阻塞父進(jìn)程了.
-
? ???直到子進(jìn)程釋放了它的內(nèi)存地址空間, 即子進(jìn)程終止或exec新程序, 用信號(hào)喚醒父進(jìn)程.
-
??返回子進(jìn)程的pid.
-
??子進(jìn)程被調(diào)度后,執(zhí)行pd.thread.eip(ret_from_fork). 調(diào)用關(guān)系(=>): ret_from_fork=>schedule_tail=>finish_task_switch.
-
? ?schedule_tail的另一件事就是: 把pid保存到地址pd->set_child_tid (創(chuàng)建進(jìn)程使的parent_tidptr)
-
? ?finish_task_switch的動(dòng)作是: 裝載內(nèi)核棧保存的寄存器(regs->eax為0),返回到用戶態(tài)。系統(tǒng)調(diào)用返回值就是eax(0)
-
內(nèi)核線程:
-
??只運(yùn)行于kernel模式,只能訪問大于3G的空間。而普通進(jìn)程在內(nèi)核模式時(shí),能訪問整個(gè)4G空間
-
??創(chuàng)建方法, 類似于clone
-
? ?準(zhǔn)備返回地址fn: 構(gòu)造一個(gè)regs. 里面有fn, args, __KERNEL_CS等. regs->eip是匯編函數(shù)kernel_thread_helper
-
? ?do_fork (flags|CLONE_VM|clone_untraced, 0, ®s, 0, NULL, NULL)
-
? ? 創(chuàng)建線程, 與父進(jìn)程共享頁. 用上步構(gòu)造的regs初始化新程的內(nèi)核棧
-
? ?新線程被調(diào)度后. 由ret_from_fork, 用regs恢復(fù)寄存器, 開始執(zhí)行kernel_thread_helper
-
? ?kernel_thread_helper: 把a(bǔ)rgs壓入棧, call fn(args, fn都寄存器中)
-
??典型的內(nèi)核線程:
-
? ?進(jìn)程0: 所有進(jìn)程的祖先
-
? ? 編譯時(shí)存在.
-
? ???pd, 內(nèi)核棧: init_task, init_thread_union
-
? ???資源: init_mm, init_files, init_fs.??信號(hào): init_signals, init_sighand
-
? ???頁表: swapper_gd_dir
-
? ? 功能
-
? ???初始化系統(tǒng)數(shù)據(jù),
-
? ?? ?多CPU系統(tǒng)中, 開始時(shí)BIOS禁用其他CPU.
-
? ?? ?初始化系統(tǒng)數(shù)據(jù)后, 進(jìn)程0拷貝自己到其他CPU的調(diào)度列隊(duì)上, 啟動(dòng)其他CPU, 所有的PID都是0.
-
? ???使能中斷
-
? ???創(chuàng)建內(nèi)核線程1, (函數(shù)是init)
-
? ???進(jìn)入idle
-
? ?進(jìn)程1:
-
? ? init函數(shù) exec可執(zhí)行文件init, 使內(nèi)核線程變成了普通進(jìn)程.
-
? ? 管理其他進(jìn)程, 稱為托孤進(jìn)程
-
? ?其他內(nèi)核線程:
-
? ? 執(zhí)行工作列隊(duì):
-
? ???ksoftirqd: 執(zhí)行 softlets
-
? ???kblockd: 執(zhí)行工作列隊(duì) kblockd_workqueue, 定期激活塊設(shè)備驅(qū)動(dòng)
-
? ???keventd (又叫events): 處理工作列隊(duì) keventd_wq
-
? ? 管理資源:
-
? ???kapmd: 電源管理
-
? ???kswapd: 交換進(jìn)程, 用于回收內(nèi)存資源
-
? ???pdflush: flush臟的磁盤緩存
進(jìn)程銷毀
-
進(jìn)程終止
-
??系統(tǒng)調(diào)用
-
? ?整個(gè)進(jìn)程終止: exit_group(), 由do_group_exit處理系統(tǒng)調(diào)用. c函數(shù) exit()也是用的這系統(tǒng)調(diào)用
-
? ?某個(gè)線程終止: _exit(), 由do_exit處理. C函數(shù)中用到此系統(tǒng)調(diào)用的API: pthread_exit
-
??do_group_exit流程: (整個(gè)組內(nèi)至少有一個(gè)線程調(diào)用它, 用于整組協(xié)調(diào))
-
? ?檢查線程組的退出過程是否啟動(dòng): 檢查signal_group_exit(線程組內(nèi)的公共數(shù)據(jù))是否非零. 如果沒有啟動(dòng), 執(zhí)行一下操作來啟動(dòng)退出過程:
-
? ? 設(shè)置啟動(dòng)標(biāo)志signal_group_exit.
-
? ? 存儲(chǔ)終止碼(exit_group的參數(shù)), 在current->signal->group_exit_cold
-
? ? 向其他線程發(fā)SIG_KILL信號(hào), (它們收到信號(hào)后, 調(diào)do_exit())
-
? ?調(diào)用do_exit, 使本線程退出
-
??do_exit流程:
-
? ?設(shè)置線程的終止標(biāo)志, 退出碼
-
? ? 設(shè)置PF_EXITING, 標(biāo)明要被終止
-
? ? 設(shè)置pd->exit_code
-
? ???系統(tǒng)調(diào)用參數(shù)
-
? ???或是內(nèi)核提供的錯(cuò)誤碼, 表示異常終止
-
? ?釋放資源:
-
? ? 刪除該進(jìn)程的定時(shí)器
-
? ? 去除對(duì)資源的引用:
-
? ???exit_mm, __exit_files;
-
? ???__exit_fs(root路徑,工作路徑, 創(chuàng)建文件權(quán)限), exit_namespace(掛載的文件系統(tǒng)的視野);
-
? ???exit_thread(thread_struct), exit_sem,
-
? ?如果這個(gè)線程的函數(shù)實(shí)現(xiàn)了一種可執(zhí)行格式, 可執(zhí)行格式數(shù)的引用計(jì)數(shù)--; FIXME: 還沒看到這塊兒, 湊合翻譯的不一定對(duì)
-
? ?改變父子關(guān)系, 并向父進(jìn)程發(fā)信號(hào), 改變自己的狀態(tài)(exit_notify)
-
? ? 托付終止線程創(chuàng)建的子進(jìn)程:
-
? ???如果終止線程還有同組線程: 終止線程創(chuàng)建的子進(jìn)程, 作為與同組線程的子進(jìn)程.
-
? ???否則: 終止線程創(chuàng)建的子進(jìn)程, 作為孤兒進(jìn)程, 由init進(jìn)程托管
-
? ? 向父進(jìn)程發(fā)信號(hào)
-
? ???exit_signal有意義 && 最后線程 :??發(fā)exit_signal
-
? ???否則:
-
? ?? ?被trace : 發(fā)SIGCHLD
-
? ?? ?沒被trace : 不發(fā)信號(hào)
-
? ? 僵尸自己或直接死亡,??并設(shè)置PF_DEAD位
-
? ???exit_signal沒意義 && 沒被trace : 直接死亡 (這種情況沒有發(fā)信號(hào))
-
? ?? ?變成EXIT_DEAD狀態(tài),
-
? ?? ?release_task() (后面介紹). pd引用計(jì)數(shù)變?yōu)?, 不會(huì)馬上釋放
-
? ???否則: 僵尸
-
? ?? ?exit_signal有意義 || 被trace : 僵尸
-
? ???整理"僵尸"與"發(fā)臨僵尸信號(hào)"的關(guān)系:
-
? ?? ?將發(fā)信號(hào)的條件中"最后線程"去掉, 可簡化為(exit_signal有意義)||(被trace) == (發(fā)信號(hào))
-
? ?? ?可得出后: (發(fā)信號(hào)) == (僵尸)
-
? ?? ?又可推出: (沒有trace && exit_signal有意義 && 不是最后進(jìn)程) == (僵尸了,但沒法信號(hào)) , 這種情況在移除死進(jìn)程時(shí), 會(huì)給其父進(jìn)程發(fā)信號(hào) (FIXME: 待驗(yàn)證)
-
? ?調(diào)度. 調(diào)度函數(shù)會(huì)忽略僵尸進(jìn)程, 但會(huì)減少僵尸進(jìn)程的pd的使用計(jì)數(shù); 會(huì)檢查PF_DEAD位, 把它變成exit_dead狀態(tài)
|
|