進程是對邏輯的抽象,我們從作業系統的書籍中對進程有了很多的認識,但是對進程的實現可能不太了解,這篇文章試著解釋一下關於進程實現的大致原理。
進程的實現,其實和我們平常寫程式碼的時候一樣,例如我們要表示一個東西,我們會定義一個資料結構。進程也不例外。所以進程的本質就是一個資料結構,他保存了一系列的資料。作業系統以數組或鍊錶的形式和全部的進程管理起來。進程可以說分為兩種
1 系統初始化時第一個進程,
2 除了第一個進程外的其他進程,他們都是由fork或fork execute系統呼叫創建出來的。
我們先來看看進程的結構體都有什麼資訊。
當系統建立一個行程之後,會設定cs:ip暫存器的值,如果是fork,則ip就是fork函數後面的語句的ip位址。如果是execute則ip位址由編譯器指定。不管怎樣,當行程開始執行的時候,cpu就會解析cs:ip拿到一條指令去執行。那麼cs:ip是如何被解析的呢?
執行進程的時候,tss選擇子(GDT索引)被載入到tss暫存器,然後把tss裡的上下文也載入到對應的暫存器,例如cr3,ldt選擇子。根據tss資訊中的ldt索引首先從GDT找到進程ldt結構體資料的首地址,然後根據當前段的屬性,例如代碼段,則從cs中取得選擇子,系統從ldt表中取得進程線性空間的首地址、限長、權限等資訊。用線性位址的首位址加上ip中的偏移,得到線性位址,然後再透過頁目錄和頁表得到實體位址,物理位址還沒有分配則進行缺頁異常等處理。
進程的掛起、阻斷、多進程。這些概念我們平常聽得比較多,現在我們來看看他是實現是怎樣的。進程的掛起,或者說阻塞分為兩種。
1 主動掛起。透過sleep讓進程間歇性掛起。 sleep的原理之前有分析過,就不再分析。大概的原理
就是設定一個定時器,到期後喚醒進程。
修改行程為掛起狀態,等待喚醒。
2 被動掛起。
被動掛起的場景比較多,主要是行程申請一個資源,但是資源沒有滿足條件,則行程被作業系統掛起。比如我們讀一個管道的時候。管道沒有資料可讀,則進程被掛起。插入到管道的等待隊列。
<code>// 当前进程挂载到睡眠队列p中,p指向队列头指针的地址<br>void sleep_on(struct task_struct **p)<br>{<br> struct task_struct *tmp;<br><br> if (!p)<br> return;<br> if (current == &(init_task.task))<br> panic("task[0] trying to sleep");<br> /*<br> *p为第一个睡眠节点的地址,即tmp指向第一个睡眠节点<br> 头指针指向当前进程,这个版本的实现没有采用真正链表的形式,<br> 他通过每个进程在栈中的临时变量形成一个链表,每个睡眠的进程,<br> 在栈里有一个变量指向后面一个睡眠节点,然后把链表的头指针指向当前进程,<br> 然后切换到其他进程执行,当被wake_up唤醒的时候,wake_up会唤醒链表的第一个<br> 睡眠节点,因为第一个节点里保存了后面一个节点的地址,所以他唤醒后面一个节点,<br> 后面一个节点以此类推,从而把整个链表的节点唤醒,这里的实现类似nginx的filter,<br> 即每个模块保存后面一个节点的地址,然后把全局指针指向自己。<br> */<br> tmp = *p;<br> *p = current;<br> // 不可中断睡眠只能通过wake_up唤醒,即使有信号也无法唤醒<br> current->state = TASK_UNINTERRUPTIBLE;<br> // 进程调度<br> schedule();<br> // 唤醒后面一个节点<br> if (tmp)<br> tmp->state=0;<br>}<br><br>// 唤醒队列中的第一个节点,并清空链表,因为第一个节点会向后唤醒其他节点<br>void wake_up(struct task_struct **p)<br>{<br> if (p && *p) {<br> (**p).state=0;<br> *p=NULL;<br> }<br>}</code>
我們發現,進程的實現,跟我們平時寫程式碼差不多,就是定義資料結構,然後實作操作資料結構的演算法。當然,因為涉及硬體底層,作業系統的實作比我們的程式碼複雜得多。
以上是意思?請提供更多上下文。的詳細內容。更多資訊請關注PHP中文網其他相關文章!