<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何描述一個進程?

          共 7401字,需瀏覽 15分鐘

           ·

          2021-05-25 13:26

          本文來源:

          號主老吳,從事嵌入式軟件開發(fā)多年,積極擁抱開源社區(qū)和熱衷技術(shù)分享。分享技術(shù)的同時,也分享自己對生活和人生的思考和見解,歡迎關(guān)注!

          一、關(guān)于寫文章

          1. 許多知識,書上或者網(wǎng)絡(luò)上都有,就算這兩個地方都沒有,代碼里也會有答案。但有時恰恰是 資料太多,反而讓人難以檢索出有用的信息

          2. 面對同樣的資料,每個人的理解能力,思維方式,學習能力,學習時間是不同,所以學習的效果也是不一樣的。有人愿意將自己認真思考的結(jié)果分享出來,是非常值得鼓勵的。即便你是學霸,找到優(yōu)秀的人和他們的文章,會大大節(jié)省自己在陌生領(lǐng)域的學習時間。

          3. 每個人的思維方式和寫作能力是有高低之分的,同樣的內(nèi)容,不同的描述,讀者會有不同的收獲。我希望寫一些有價值的文章,通過公開寫作來不斷地提高自己學習能力,并且尋找出更好的學習方法。

          4. 學得越多,記憶的難度越大。不及時輸出的話,內(nèi)化效果極差,學習價值會不斷地進行貶值

          5. 寫文章首先是給自己看,其次才是給別人看,不要害怕寫錯,寫錯了要虛心接受批評。

          二、Linux 內(nèi)核如何描述一個進程?

          目的:

          • 初步了解進程描述符 task_struct。

          目錄:

          1. Linux 的進程
          2. Linux 的進程描述符
            • task_struct
            • 內(nèi)核如何找到 task_struct
            • task_struct 的分配和初始化
          3. 實驗:打印 task_struct / thread_info / kernel mode stack

          環(huán)境:

          • Linux-4.14 + ARMv7

          1. Linux 的進程

          進程的術(shù)語是 process,是 Linux 最基礎(chǔ)的抽象,另一個基礎(chǔ)抽象是文件。

          最簡單的理解,進程就是執(zhí)行中 (executing, 不等于running) 的程序。

          更準確一點的理解,進程包括執(zhí)行中的程序以及相關(guān)的資源 (包括cpu狀態(tài)、打開的文件、掛起的信號、tty、內(nèi)存地址空間等)。

          一種簡潔的說法:進程 = n*執(zhí)行流 + 資源,n>=1


          Linux 進程的特點:

          • 通過系統(tǒng)調(diào)用 fork() 創(chuàng)建進程,fork() 會復制現(xiàn)有進程來創(chuàng)建一個全新的進程。

          • 內(nèi)核里,并不嚴格區(qū)分進程和線程

          • 從內(nèi)核的角度看,調(diào)度單位是線程 (即執(zhí)行流)。可以把線程看做是進程里的一條執(zhí)行流,1個進程里可以有1個或者多個線程。

          • 內(nèi)核里,常把進程稱為 task 或者 thread,這樣描述更準確,因為許多進程就只有1條執(zhí)行流。

          • 內(nèi)核通過輕量級進程 (lightweight process) 來支持多線程。1個輕量級進程就對應(yīng)1個線程,輕量級進程之間可以共享打開的文件、地址空間等資源。

          2. Linux 的進程描述符

          2.1 task_struct

          內(nèi)核里,通過 task_struct 結(jié)構(gòu)體來描述一個進程,稱為進程描述符 (process descriptor),它保存著支撐一個進程正常運行的所有信息。

          每一個進程,即便是輕量級進程(即線程),都有1個 task_struct。

          sched.h (include\linux)

          struct task_struct {
              struct thread_info thread_info;
              volatile long state;
              void *stack;

              [...]
              struct mm_struct *mm;

              [...]
              pid_t pid;

              [...]
              struct task_struct *parent;

              [...]
              char comm[TASK_COMM_LEN];

              [...]
           struct files_struct *files;

           [...]
           struct signal_struct *signal;
          }

          這是一個龐大的結(jié)構(gòu)體,不僅有許多進程相關(guān)的基礎(chǔ)字段,還有許多指向其他數(shù)據(jù)結(jié)構(gòu)的指針。

          它包含的字段能完整地描述一個正在執(zhí)行的程序,包括 cpu 狀態(tài)、打開的文件、地址空間、掛起的信號、進程狀態(tài)等。

          點擊查看大圖

          作為初學者,先簡單地了解部分字段就好:

          • struct thread_info thread_info: 進程底層信息,平臺相關(guān),下面會詳細描述。

          • long state: 進程當前的狀態(tài),下面是幾個比較重要的進程狀態(tài)以及它們之間的轉(zhuǎn)換流程。

          點擊查看大圖
          • void *stack: 指向進程內(nèi)核棧,下面會解釋。

          • struct mm_struct *mm: 與進程地址空間相關(guān)的信息都保存在一個叫內(nèi)存描述符 (memory descriptor) 的結(jié)構(gòu)體 (mm_struct) 中。

          點擊查看大圖
          • pid_t pid: 進程標識符,本質(zhì)就是一個數(shù)字,是用戶空間引用進程的唯一標識。

          • struct task_struct *parent: 父進程的 task_struct。

          • char comm[TASK_COMM_LEN]: 進程的名稱。

          • struct files_struct *files: 打開的文件表。

          • struct signal_struct *signal: 信號處理相關(guān)。

          其他字段,等到有需要的時候再回過頭來學習。

          2.2 當發(fā)生系統(tǒng)調(diào)用或者進程切換時,內(nèi)核如何找到 task_struct ?

          對于 ARM 架構(gòu),答案是:通過內(nèi)核棧 (kernel mode stack)。

          為什么要有內(nèi)核棧?

          • 因為內(nèi)核是可重入的,在內(nèi)核中會有多條與不同進程相關(guān)聯(lián)的執(zhí)行路徑。因此不同的進程處于內(nèi)核態(tài)時,都需要有自己私有的進程內(nèi)核棧 (process kernel stack)。

          當進程從用戶態(tài)切換到內(nèi)核態(tài)時,所使用的棧會從用戶棧切換到內(nèi)核棧

          • 至于是如何切換的,關(guān)鍵詞是系統(tǒng)調(diào)用,這不是本文關(guān)注的重點,先放一邊,學習內(nèi)核要懂得恰當?shù)臅r候忽略細節(jié)。

          當發(fā)生進程切換時,也會切換到目標進程的內(nèi)核棧

          • 同上,關(guān)鍵詞是硬件上下文切換 (hardware context switch),忽略具體實現(xiàn)。

          無論何時,只要進程處于內(nèi)核態(tài),就會有內(nèi)核棧可以使用,否則系統(tǒng)就離崩潰不遠了。

          ARM 架構(gòu)的內(nèi)核棧和 task_struct 的關(guān)系如下:

          點擊查看大圖

          內(nèi)核棧的長度是 THREAD_SIZE,對于 ARM 架構(gòu),一般是 2 個頁框的大小,即 8KB。

          內(nèi)核將一個較小的數(shù)據(jù)結(jié)構(gòu) thread_info 放在內(nèi)核棧的底部,它負責將內(nèi)核棧和 task_struct 串聯(lián)起來。thread_info 是平臺相關(guān)的,在 ARM 架構(gòu)中的定義如下:

          // thread_info.h (arch\arm\include\asm)

          struct thread_info {
           unsigned long flags;  /* low level flags */
           int preempt_count; /* 0 => preemptable, <0 => bug */
           mm_segment_t addr_limit; /* address limit */
           struct task_struct *task;  /* main task structure */
              [...]
           struct cpu_context_save cpu_context; /* cpu context */
           [...]
          };

          thread_info 保存了一個進程能被調(diào)度執(zhí)行的最底層信息(low level task data),例如struct cpu_context_save cpu_context 會在進程切換時用來保存/恢復寄存器上下文。

          內(nèi)核通過內(nèi)核棧的棧指針可以快速地拿到 thread_info:

          // thread_info.h (include\linux)

          static inline struct thread_info *current_thread_info(void)
          {
              // current_stack_pointer 是當前進程內(nèi)核棧的棧指針
           return (struct thread_info *)
            (current_stack_pointer & ~(THREAD_SIZE - 1));
          }

          然后通過 thread_info 找到 task_struct:

          // current.h (include\asm-generic)

          #define current (current_thread_info()->task)

          內(nèi)核里通過 current 宏可以獲得當前進程的 task_struct。

          2.3 task_struct 的分配和初始化

          當上層應(yīng)用使用 fork() 創(chuàng)建進程時,內(nèi)核會新建一個 task_struct。

          進程的創(chuàng)建是個復雜的工作,可以延伸出無數(shù)的細節(jié)。這里我們只是簡單地了解一下 task_struct 的分配和部分初始化的流程。

          fork() 在內(nèi)核里的核心流程

          點擊查看大圖

          dup_task_struct() 做了什么?

          點擊查看大圖

          至于設(shè)置內(nèi)核棧里做了什么,涉及到了進程的創(chuàng)建與切換,不在本文的關(guān)注范圍內(nèi),以后再研究了。

          3. 實驗:打印 task_struct / thread_info / kernel mode stack

          實驗?zāi)康模?/strong>

          • 梳理 task_struct / thread_info / kernel mode stack 的關(guān)系。

          實驗代碼:

          #include <linux/init.h>
          #include <linux/module.h>
          #include <linux/sched.h>

          static void print_task_info(struct task_struct *task)
          {
              printk(KERN_NOTICE "%10s %5d task_struct (%p) / stack(%p~%p) / thread_info->task (%p)",
                  task->comm, 
                  task->pid,
                  task,
                  task->stack,
                  ((unsigned long *)task->stack) + THREAD_SIZE,
                  task_thread_info(task)->task);
          }

          static int __init task_init(void)
          {
              struct task_struct *task = current;

              printk(KERN_INFO "task module init\n");

              print_task_info(task);
              do {
                  task = task->parent;
                  print_task_info(task);
              } while (task->pid != 0);

              return 0;
          }
          module_init(task_init);

          static void __exit task_exit(void)
          {
              printk(KERN_INFO "task module exit\n ");
          }
          module_exit(task_exit);

          運行效果:

          task module init
              insmod  3123 task_struct (edb42580) / stack(ed46c000~ed474000) / thread_info->task (edb42580)
                bash  2393 task_struct (eda13e80) / stack(c9dda000~c9de2000) / thread_info->task (eda13e80)
                sshd  2255 task_struct (ee5c9f40) / stack(c9d2e000~c9d36000) / thread_info->task (ee5c9f40)
                sshd   543 task_struct (ef15f080) / stack(ee554000~ee55c000) / thread_info->task (ef15f080)
             systemd     1 task_struct (ef058000) / stack(ef04c000~ef054000) / thread_info->task (ef058000)

          在程序里,我們通過 task_struct 找到 stack,然后通過 stack 找到 thread_info,最后又通過 thread_info->task 找到 task_struct。

          4. 相關(guān)參考

          • Linux 內(nèi)核設(shè)計與實現(xiàn) / 第 3.1 章節(jié)

          • 深入理解 Linux 內(nèi)核 / 3

          • Linux 內(nèi)核深度解析 / 2.5.1

          • 深入Linux 內(nèi)核架構(gòu) / 2.3

          三、思考技術(shù),也思考人生

          要學習技術(shù),更要學習如何生活

          你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。


          瀏覽 87
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼激情网 | 国产又爽又黄无码无遮挡在线观看 | 九九九九影视 | 日日夜夜久久视频久久视频 | 全免费A级毛片免费视频播放 |