《Linux內核分析》第六週筆記 進程的描述和進程的建立

進程的描述和進程的建立node

1、進程的描述linux

  一、進程描述符task_struct數據結構(一)編程

  操做系統的三大功能:進程管理(核心)、內存管理、文件系統。數據結構

  進程控制塊PCB——task_struct(進程描述符):爲了管理進程,內核必須對每一個進程進行清晰的描述,進程描述符提供了內核所需瞭解的進程信息。dom

  • struct task_struct數據結構很龐大,共有約400行代碼

  • Linux進程的狀態與操做系統原理中的描述的進程狀態彷佛有所不一樣,好比就緒狀態和運行狀態都是TASK_RUNNING,爲何呢?

    操做系統原理中有三個狀態:就緒狀態、運行狀態。阻塞狀態。函數

    調用fork建立一個新進程的時候實際上的狀態是TASK_RUNNING(就緒但沒有在運行),當調度器選擇一個task時仍是切換到TASK_RUNNING,(爲何呢?當進程是TASK_RUNNING狀態時是可運行的,可是否運行仍是看是否得到CPU的控制權(有沒有在CPU上實際的執行))進程調用do_exit()停止執行,進入TASK_ZOMBIE(殭屍進程)this

    一個正在運行的進程在等待特定的事件或者是資源的時候會進入阻塞態,當阻塞的條件沒有了的時候,就進入就緒態。atom

  • 進程的標示pid

    pid及tpid用來標識進程的spa

  • 全部進程鏈表struct list_head tasks;
  • 內核的雙向循環鏈表的實現方法 - 一個更簡略的雙向循環鏈表
  • 程序建立的進程具備父子關係,在編程時每每須要引用這樣的父子關係。進程描述符中有幾個域用來表示這樣的關係
  • Linux爲每一個進程分配一個8KB大小的內存區域,用於存放該進程兩個不一樣的數據結構:Thread_info和進程的內核堆棧
  • 進程處於內核態時使用, 不一樣於用戶態堆棧,即PCB中指定了內核棧,那爲何PCB中沒有用戶態堆棧?用戶態堆棧是怎麼設定的?
  • 內核控制路徑所用的堆棧 不多,所以對棧和Thread_info 來講,8KB足夠了
  • struct thread_struct thread; //CPU-specific state of this task
  • 文件系統和文件描述符
  • 內存管理——進程的地址空間

  二、進程描述符task_struct數據結構(二)操作系統

閱讀理解task_struct數據結構

struct task_struct {
1236 volatile long state; /*state是運行狀態*/
1237 void *stack;/*指定了進程的內核堆棧 */
1238atomic_tusage;
1239 unsigned int flags; /* 標識符*/
1240 unsigned int ptrace;
1241
1242#ifdef CONFIG_SMP/*條件編譯多處理器用到*/
1243 struct llist_nodewake_entry;
1244 int on_cpu;
1245 struct task_struct *last_wakee;
1246 unsigned long wakee_flips;
1247 unsigned long wakee_flip_decay_ts;
1248
1249 int wake_cpu;
1250#endif
1251 int on_rq;/*運行隊列和進程調度相關程序*/
1252
1253 int prio, static_prio, normal_prio;
1254 unsigned int rt_priority;
1255 const struct sched_class *sched_class;
1256 struct sched_entityse;
1257 struct sched_rt_entityrt;
1258#ifdef CONFIG_CGROUP_SCHED
1259 struct task_group *sched_task_group;
1260#endif
1295 struct list_head tasks;/*進程鏈表*/

/*雙向鏈表*/
1296#ifdef CONFIG_SMP
1297 struct plist_nodepushable_tasks;
1298 struct rb_nodepushable_dl_tasks;
1299#endif
1300
1301 struct mm_struct *mm, *active_mm;/*進程管理進程的地址空間相關*/ 每一個進程有獨立的進程地址空間4G,32位x86。
1302#ifdef CONFIG_COMPAT_BRK
1303 unsigned brk_randomized:1;
1304#endif
1305 /* per-thread vma caching */
1306 u32 vmacache_seqnum;
1307 struct vm_area_struct *vmacache[VMACACHE_SIZE];
1308#if defined(SPLIT_RSS_COUNTING)
1309 struct task_rss_statrss_stat;
1310#endif
1330 pid_t pid;/*標識*/
1331pid_ttgid;
1337 /*
1338 * pointers to (original) parent process, youngest child, younger sibling,/*進程的父子關係*/
1339 * older sibling, respectively.  (p->father can be replaced with
1340 * p->real_parent->pid)
1341 */
1342 struct task_struct __rcu *real_parent; /* real parent process */
1343 struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
1344 /*
1345 * children/sibling forms the list of my natural children
1346 */
1347 struct list_head children; /* list of my children */
1348 struct list_head sibling; /* linkage in my parent's children list */
1349 struct task_struct *group_leader; /* threadgroup leader */



1411/*當前任務和CPU相關的狀態,在進程上下文切換的過程當中起着重要的做用 */
1412 struct thread_struct thread;
1413/* filesystem information */


1414 struct fs_struct *fs;/*文件系統相關的數據結構*/
1415/* open file information */
1416 struct files_struct *files;/*打開的文件描述符列表*/
1417/* namespaces */
1418 struct nsproxy *nsproxy;
1419/* signal handlers *//*與信號處理相關的工做*/

2、進程的建立

  一、進程的建立概覽及fork一個進程的用戶態代碼

  進程描述符是整個系統管理中挈領性的東西。

  瞭解進程是如何建立的?進程之間如何調度切換的?

  

一號進程的建立:複製了0號進程的pcb,而後根據1號進程的須要修改,再加載一個init執行程序 。

fork一個子進程的代碼

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(int argc, char * argv[])
  5. {
  6.     int pid;
  7.     /* fork another process */
  8.     pid = fork();/*在用戶態用於建立一個子進程的系統調用*/
  9.     if (pid < 0) /*出錯處理*/
  10.     
  11.         /* error occurred */
  12.         fprintf(stderr,"Fork Failed!");
  13.         exit(-1);
  14.     
  15.     else if (pid == 0) /*與else共同執行*/
  16.     {
  17.         /* child process */
  18.         printf("This is Child Process!\n");
  19.     
  20.     else 
  21.     
  22.         /* parent process  */
  23.         printf("This is Parent Process!\n");
  24.         /* parent will wait for the child to complete*/
  25.         wait(NULL);
  26.         printf("Child Complete!\n");
  27.     }
  28. }

  fork系統調用在子進程和父進程各返回一次。子進程中pid的返回值是:0。父進程中pid的返回值是:子進程的ID。

  fork以後兩個進程。

  二、理解進程建立過程複雜代碼的方法

   在進程調度的過程當中,調度到一個未調度的新進程,執行的起點是咱們設定的my process的ip。
  建立一個新進程就是複製當前進程的信息來實現的。
  一個父進程建立一個子進程,有一個地方複製子進程的pcb,修改複製出來的pcb.
  要給新進程分配一個新的內核堆棧.

  回顧:系統調用的進程建立過程

  

  Linux中建立進程一共有三個函數:

  • fork,建立子進程
  • vfork,與fork相似,可是父子進程共享地址空間,並且子進程先於父進程運行。
  • clone,主要用於建立線程

  這裏值得注意的是,Linux中得線程是經過模擬進程實現的,較新的內核使用的線程庫通常都是NPTL。

  三、瀏覽進程建立過程相關的關鍵代碼

   

  四、建立的新進程是從哪裏開始執行的

  fork,vfork,clone均可以建立新進程,他們都是經過調用do_fork來實現的。

ip指向ret_from_fork

fork()系統調用產生的子進程在系統調用處理過程當中從ret_from_fork開始執行。

只複製了部份內核堆棧

  五、使用gdb跟蹤建立新進程的過程

 爲了減小對以後實驗的影響,刪除test_fork.c以及test.c,編譯內核:

gdb調試,設斷點:

執行fork,發現fork函數停在了父進程中。

特別關注新進程是從哪裏開始執行的?爲何從哪裏能順利執行下去?即執行起點與內核堆棧如何保證一致。

  • ret_ from_ fork決定了新進程的第一條指令地址。
  • 子進程從ret_ from_ fork處開始執行。
  • 由於在ret_ from_ fork以前,也就是在copy_ thread()函數中* childregs = * current_ pt_ regs();該句將父進程的regs參數賦值到子進程的內核堆棧。
  • * childregs的類型爲pt_ regs,裏面存放了SAVE_ ALL中壓入棧的參數,所以在以後的RESTORE ALL中能順利執行下去。

 3、總結

  fork()函數建立新進程是經過下列一系列函數實現的:fork() -> sys_clone() -> do_fork() -> dup_task_struct() -> copy_process() -> copy_thread() -> ret_from_fork()。操做系統的三大功能:進程管理(核心)、內存管理、文件系統。進程控制塊PCB——task_struct(進程描述符):爲了管理進程,內核必須對每一個進程進行清晰的描述,進程描述符提供了內核所需瞭解的進程信息。Linux經過複製父進程來建立一個新進程,fork系統調用在子進程和父進程各返回一次。子進程中pid的返回值是:0。父進程中pid的返回值是:子進程的ID。

相關文章
相關標籤/搜索