太原網(wǎng)站建設與維護做網(wǎng)站的平臺
?
?
文章目錄
- 馮諾依曼體系結(jié)構(gòu)
- 一:操作系統(tǒng)
- 二:? 進程
- 總結(jié)
?
馮諾依曼體系結(jié)構(gòu)
我們常見的計算機,如筆記本。我們不常見的計算機,如服務器,大部分都遵守馮諾依曼體系。
馮諾依曼體系如下圖:
那么輸入設備有哪些呢?如鍵盤,鼠標,話筒,攝像頭,磁盤等。
存儲器就是我們經(jīng)常說的內(nèi)存了。
輸出設備有網(wǎng)卡,聲卡,音響等。
cpu含有運算器和控制器等。
那么為什么馮諾依曼要有內(nèi)存呢?直接讓輸入設備連接cpu到輸出設備不是更方便嗎,大家看如果是下面這個體系怎么樣?
首先這樣的體系是沒問題的,可以直接讓輸入設備直接和cpu溝通,但問題在于cpu的運行速度非???#xff0c;而輸入設備和輸出設備都屬于外設,外設一般都會比較慢,就比如磁盤和內(nèi)存的比較,磁盤如果將數(shù)據(jù)存入會一直存在,而內(nèi)存是一種掉電易失的介質(zhì)如果你使用的電腦臺式電腦突然停電關(guān)機了那么內(nèi)存里的數(shù)據(jù)就沒有了,但是相對于磁盤,內(nèi)存的速度比較快。馮諾依曼體系之所以讓外設與內(nèi)存溝通是因為要防止木桶原理,cpu的算力太快了是外設遠遠比不上的,而有內(nèi)存的存在就可以大大的緩解木桶原理帶來的效率慢的問題。因為有了內(nèi)存的存在,我們可以對數(shù)據(jù)進行預加載,CPU以后在進行數(shù)據(jù)計算的時候,根本就不需要訪問外設了,而只要直接伸手向內(nèi)存要就可以了。
結(jié)論一:在數(shù)據(jù)層面,一般CPU不和外設直接溝通,而是直接只和內(nèi)存溝通。這也就回答了為什么我們的程序必須先被加載到內(nèi)存中,因為這是體系結(jié)構(gòu)決定的。
那么在硬件層面,單機和跨主機之間數(shù)據(jù)是如何流向的呢?看下圖:
舉個例子:比如我們用qq給別人發(fā)送消息,先從鍵盤輸入要發(fā)送的消息,然后將數(shù)據(jù)經(jīng)過內(nèi)存被CPU運行后再回到內(nèi)存然后將數(shù)據(jù)交給輸出設備,這個時候給別人發(fā)消息的那個人的屏幕會先顯示出發(fā)送的消息,然后經(jīng)過網(wǎng)絡等到接收消息的人的輸入設備然后經(jīng)過內(nèi)存,cpu然后顯示到對方的屏幕上。?
?
一、操作系統(tǒng)
操作系統(tǒng)是進行軟硬件資源管理軟件的。任何一個計算機系統(tǒng)都包含一個基本的程序集合,稱為操作系統(tǒng)(OS),籠統(tǒng)的理解,操作系統(tǒng)包括:內(nèi)核(進程管理,內(nèi)存管理,文件管理,驅(qū)動管理),其他程序(例如函數(shù)庫,shell程序等等)
那么如何理解操作系統(tǒng)對硬件做管理呢?我們舉個例子:就比如學生,是誰在管理學生呢,當然是校長在管理學生了,校長主要是決策,輔導員主要是去執(zhí)行校長做出的決策,而學生是參與執(zhí)行的對象,在校長管理期間,校長只需要向各級領導發(fā)放指令管理學生,而不會直接和學生溝通管理。所以我們得出以下結(jié)論:1.管理者和被管理者其實是不需要直接溝通的。
2.管理的本質(zhì):對被管理的對象的數(shù)據(jù)進行管理
3.用什么管理呢?數(shù)據(jù)結(jié)構(gòu)。
管理的本質(zhì)就是先描述,在組織。就像各個不同的程序,先將這些程序描述為一個個的結(jié)構(gòu)體,然后以鏈表的形式進行對單個節(jié)點的修改也就是管理。
而像我們剛剛舉得學生的例子中,校長就是操作系統(tǒng),輔導員就是硬件驅(qū)動,學生就是硬件或軟件。
那么操作系統(tǒng)為什么對軟硬件資源進行管理呢?操作系統(tǒng)對下通過管理好軟硬件資源(手段),對上給用戶提供良好(安全,穩(wěn)定,高效,功能豐富)的執(zhí)行環(huán)境(目的)。
那么操作系統(tǒng)是如何給用戶提供良好的執(zhí)行環(huán)境的呢?操作系統(tǒng)通過暴露出一些系統(tǒng)接口供用戶操作使用,這里就好比銀行一樣,銀行為人們服務是窗口式服務,像金庫等一些重要的地方是不會對用戶進行開放的,而開放的地方也會用窗口封裝起來避免遭到破壞,所以操作系統(tǒng)是不會相信任何一個用戶的,它只是暴露出一些可以供用戶使用的接口去使用,同時還要保證操作系統(tǒng)內(nèi)部的安全,所以這些接口是經(jīng)過一層又一層的封裝,而這些由操作系統(tǒng)提供的接口又被稱為系統(tǒng)調(diào)用。
系統(tǒng)調(diào)用在使用上,功能比較基礎,對用戶的要求相對也比較高,所以有心的開發(fā)者可以對部分系統(tǒng)調(diào)用進行適度封裝,從而形成庫,有了庫,就很利于更上層用戶或者開發(fā)者進行二次開發(fā)。
二、進程
我們現(xiàn)在大多數(shù)人都接觸過電腦,那么我們以windows為例,windows中的進程是什么樣的呢,大家看下圖:
?因為windows是圖形化操作的所以這些進程看起來非常生動形象。那么這些進程該如何去理解呢?
首先,我們以前的任何啟動并運行程序的行為,都是由操作系統(tǒng)幫助我們將程序轉(zhuǎn)化為進程完成特定的任務。
如下圖:
首先我們要運行一個程序,這個程序保存在磁盤中,當雙擊運行就把這個程序的代碼和數(shù)據(jù)加載到內(nèi)存中,每加載一個程序的代碼和數(shù)據(jù)操作系統(tǒng)都會將這個程序的代碼和程序用pcb/task_struct保存起來,并且將這些程序的代碼和數(shù)據(jù)連接起來像鏈表一樣進行控制,用戶想要優(yōu)先使用哪個軟件就將這個軟件的pcb/task_struct加載到CPU中進行運行,所以對于進程的管理就轉(zhuǎn)化為了對pcb/task_struck進行鏈表的增刪查改。進程不是內(nèi)存中的代碼和數(shù)據(jù),進程是代碼和數(shù)據(jù)加該進程在內(nèi)核中加載的pcb/task_struct合起來。
結(jié)論:進程 =?內(nèi)核關(guān)于進程的相關(guān)數(shù)據(jù)結(jié)構(gòu) +?當前進程的代碼和數(shù)據(jù)
描述進程-PCB:進程信息被放在一個叫做進程控制塊的數(shù)據(jù)結(jié)構(gòu)中,可以理解為進程屬性的集合。課本上稱之為PCB,linux操作系統(tǒng)下的PCB是task_struck
為什么要有pcb呢?我們前面說過操作系統(tǒng)管理的本質(zhì)是先描述在組織,而進程的描述就是靠pcb。
task_struck內(nèi)容分類
標識符:描述本進程的唯一標識符,用來區(qū)別其他進程。
狀態(tài):任務狀態(tài),退出代碼,退出信號等
優(yōu)先級:相對于其他進程的優(yōu)先級
程序計數(shù)器:程序中即將被執(zhí)行的下一條指令的地址
內(nèi)存指針:包括程序代碼和進程相關(guān)數(shù)據(jù)的指針,還有和其他進程共享的內(nèi)存塊的指針。
上下文數(shù)據(jù):進程執(zhí)行時處理器的寄存器中的數(shù)據(jù)
I/O狀態(tài)信息:包括顯示的I\O請求,分配給進程的I\O設備和被進程使用的文件列表。
記賬信息:可能包括處理器的時間總和,使用的時鐘數(shù)總和,時間限制,記賬號等
其他信息。
下面我們在linux系統(tǒng)下演示進程的存在:
首先創(chuàng)建一個.c文件用來寫一個死循環(huán)程序,這樣方便我們看到進程:
然后我們創(chuàng)建一個并寫出一個一個簡單的Makefile文件:
?文件中冒號的左邊依賴于冒號的右邊,也就是說沒有process依賴于myprocess.c,gcc - o中的$@符號是指冒號左邊的文件,$^是指冒號右邊的文件。
?然后我們寫了一個簡單的死循環(huán)代碼,現(xiàn)在開始運行。
現(xiàn)在這個可執(zhí)行程序已經(jīng)跑起來了,根據(jù)我們上面所講的,先將代碼和數(shù)據(jù)加載到內(nèi)存,然后變成一個task_struct,這就變成一個進程了,那么怎么看到這個進程呢?先復制一個會話,在新會話中用ps axj命令可以查看所有的進程,然后我們利用管道來過濾只想看我的進程。
?然后我們再利用管道head -1查看第一行
然后我們利用邏輯與拿到我們進程的信息:
?這個時候我們就拿到了進程的屬性,最后一行的grep是什么呢?這是因為我們利用grep去過濾進程,而grep本身也是一個進程,所以進程中可以看到grep,那么如果我們不想看到grep該怎么操作呢?只需要在剛剛的命令下多加一個管道grep -v grep就過濾掉grep這個進程了。
?我們在開一個會話運行這個死循環(huán)程序,然后看看他們的進程屬性。
這個時候我們就能看到有兩個myprocess可執(zhí)行程序他們兩個很明顯是兩個不同的進程,任何一個進程都有自己的pid,除了上面這種查看進程的方式,我們還有在系統(tǒng)根目錄查看進程的方式:
?proc就是進程的縮寫,圖中紅色就是剛剛我們所查看的進程在根目錄下顯示的情況
?我們利用訪問文件的方式去查看新增的進程的屬性:
接下來我們將進程用ctrl+c終止了。
?當我們將程序結(jié)束后就看不到相對應的進程了。
剛剛我們看到了進程對應的pid,那么我們能不能在寫程序的時候就獲取到pid呢?答案是可以的,我們可以用getpid()函數(shù)來獲取pid。
?因為getpid()這個函數(shù)所需要包含的頭文件為#include <unistd.h>? 和 #include <sys/types.h>所以我們多加了這個頭文件,那么我們來運行一下。注意:修改.c文件后切記make clean一下然后再重新make生成可執(zhí)行程序。
?可以看到我們獲取到了pid,現(xiàn)在去驗證一個是否正確。
?很明顯是正確的,之前我們就看到了pid旁邊的ppid那么ppid是什么呢?ppid其實是這個進程的父進程,每個進程都會有子進程和父進程,下面我們以同樣的方式獲取一下父進程的pid:
?進過驗證我們也能發(fā)現(xiàn)是正確的。然后我們再重新運行一下程序:
這個時候我們發(fā)現(xiàn)子進程變了但是父進程還是剛剛的15611這是什么原因呢?那么我們就去查查這個父進程是什么:
?通過檢查我們發(fā)現(xiàn)父進程竟然是bash。bash是命令行解釋器,bash本質(zhì)上也是一個進程,因為bash有獨立的pid。接下來我們得出結(jié)論:
1.命令行啟動的所有的程序,最終都會變成進程,而該進程對應的父進程都是bash。
剛剛我們采用的都是ctrl + c的方式結(jié)束程序,現(xiàn)在我們用命令的方式去結(jié)束,kill -9 +pid就能殺掉一個進程。
?我們可以看到確實將剛剛運行的程序結(jié)束了,那么如果殺掉了bash會怎么樣呢?
如果我們直接殺掉了bash,那么命令行編輯器就會崩潰我們就無法繼續(xù)輸入命令只能先關(guān)閉xshell重新連接才可以。
接下來我們演示一下如何創(chuàng)建一個子進程:
創(chuàng)建子進程我們需要使用fork函數(shù),先來看一下fork函數(shù)的使用規(guī)則:
?我們可以看到fork的作用是創(chuàng)建一個子進程,頭文件是#include <unistd.h>。
fork的返回值是給父進程返回子進程pid,給子進程返回對應的0值
我們先講剛剛myprocess.c里的代碼先批量化注釋掉,如何批量化注釋呢?首先在命令模式下按ctrl v?左下角出現(xiàn)V,然后用HJKL四個方向鍵選出要注釋的區(qū)域,然后切換為大寫,再輸入i然后輸入//,然后ESC退出即可。
那么如何取消注釋呢?直接輸入方式切換為小寫,然后u一下。當然我們也可以繼續(xù)使用批量化取消注釋的方法,先在命令模式下ctrl?v,然后L選擇區(qū)域,然后直接輸入d就能取消注釋了。
?首先上圖中我們用fork創(chuàng)建了一個子進程,本來這個程序應該只有一個進程由于fork的存在從兩行打印變成3行打印,并且創(chuàng)建出來的子進程的pid和ppid都與A完全一樣。因為fork的返回值是給父進程返回子進程的pid,所以通過上圖我們可以看到ret是22431這是子進程的pid,22431的父進程就是22430了。而給子進程返回0值也可以看到22431這個子進程的ret確實為0。那么為什么一個函數(shù)會有兩個返回值呢?下面我們用一個程序來解釋為什么:
注意:1.fork之后,執(zhí)行流會變成兩個執(zhí)行流
2.fork之后,誰先運行由調(diào)度器決定
3.fork之后,fork之后的代碼共享,通常我們通過if和else if來執(zhí)行分流。
?再說返回值的問題先來說一下fork做了什么,如下圖:
?我們很清楚程序運行后會將代碼和數(shù)據(jù)加載到內(nèi)存中,然后操作系統(tǒng)會將這些描述為task_struct結(jié)構(gòu)體,父進程有自己的PCB和代碼和數(shù)據(jù),而當我們創(chuàng)建子進程的時候,會在內(nèi)核當中再創(chuàng)建一個進程所對應的PCB,與父進程不同的是操作系統(tǒng)會修改子進程的一些數(shù)據(jù)比如pid和ppid,但是這兩個PCB都指向一份代碼和數(shù)據(jù)。而進程在運行的時候是具有獨立性的,父子進程也同樣具有獨立性,比如下面我們將一個進程殺掉那么另外的進程還會繼續(xù)運行:
?
從上圖中我們可以看到當我們殺死子進程時父進程還會繼續(xù)運行,這就證明了進程具有獨立性。
剛剛我們說過父子進程指向同一份代碼和數(shù)據(jù),那么為什么子進程都結(jié)束了父進程的代碼和數(shù)據(jù)不受影響呢?因為對于代碼來說代碼是只讀的,而數(shù)據(jù)中當有一個執(zhí)行流嘗試修改數(shù)據(jù)的時候,OS會自動給我們當前進程觸發(fā)寫時拷貝。
當我們函數(shù)內(nèi)部準備執(zhí)行return的時候,我們的主體功能已經(jīng)完成,所以執(zhí)行return的時候由于return也是一個語句,父子進程都會執(zhí)行這個語句所以才會出現(xiàn)兩個返回值,而由于寫時拷貝所以能接收到兩個返回值。
總結(jié)
以上只是linux中關(guān)于進程的一小部分,下一篇將持續(xù)更新linux進程的概念
要去了解進程應該先了解馮諾依曼體系結(jié)構(gòu)以及操作系統(tǒng),這樣我們在學習進程的時候有一些看不懂的東西才能弄明白,同時進程這部分概念較多所以需要大家理解概念后嘗試去通過代碼證明概念的正確性,這樣才能深入的理解進程。
?