務(wù)川網(wǎng)站建設(shè)最新國際新聞大事件
文章目錄
- 1. 馮諾依曼體系結(jié)構(gòu)
- 2. 操作系統(tǒng)
- 3. 進(jìn)程
- 進(jìn)程的基本概念
- 查看進(jìn)程和殺死進(jìn)程
- 父進(jìn)程和子進(jìn)程
- 通過系統(tǒng)調(diào)用創(chuàng)建子進(jìn)程
1. 馮諾依曼體系結(jié)構(gòu)
馮·諾依曼結(jié)構(gòu)也稱普林斯頓結(jié)構(gòu),是一種將程序指令存儲器和數(shù)據(jù)存儲器合并在一起的存儲器結(jié)構(gòu)。數(shù)學(xué)家馮·諾依曼提出了計(jì)算機(jī)制造的三個(gè)基本原則,即采用二進(jìn)制邏輯、程序存儲執(zhí)行以及計(jì)算機(jī)由五個(gè)部分組成(運(yùn)算器、控制器、存儲器、輸入設(shè)備、輸出設(shè)備
),這套理論被稱為馮·諾依曼體系結(jié)構(gòu)。
這里我們來介紹一下組成馮諾依曼體系結(jié)構(gòu)的五個(gè)部分:
輸入設(shè)備:鍵盤、攝像頭、網(wǎng)卡、磁盤等
輸出設(shè)備:顯示器、磁盤、網(wǎng)卡、聲卡音響等
存儲器:內(nèi)存(掉電易失)
運(yùn)算器和控制器:CPU
關(guān)于馮諾依曼,我們必須強(qiáng)調(diào)幾點(diǎn):
- 這里的存儲器指的是內(nèi)存
- 不考慮緩存情況,這里的CPU能且只能對內(nèi)存進(jìn)行讀寫,不能訪問外設(shè)(輸入或輸出設(shè)備)
- 外設(shè)(輸入或輸出設(shè)備)要輸入或者輸出數(shù)據(jù),也只能寫入內(nèi)存或者從內(nèi)存中讀取。
我們知道,我們的數(shù)據(jù)需要先從磁盤加載到內(nèi)存中,然后由CPU讀取并進(jìn)行計(jì)算,將計(jì)算的結(jié)果再次加載到內(nèi)存中,最后再由內(nèi)存寫入磁盤,通過輸出設(shè)備將數(shù)據(jù)交給我們。那么,為什么CPU為什么不能直接訪問外設(shè)呢?
其實(shí)原因也很簡單,輸入輸出設(shè)備稱之為外圍設(shè)備,外設(shè)一般是很慢的,比如說磁盤,相對于內(nèi)存,他的速度是非常慢的,但CPU的計(jì)算速度確是非??斓摹>秃帽葟拇疟P的讀取速度很慢,但是CPU的計(jì)算速度卻很快,但是整體的速度還是以磁盤的讀取速度為主的,所以整體效率就以外設(shè)為主。
所以在這里得出了兩個(gè)結(jié)論:
- 在數(shù)據(jù)層面上,一般CPU不和外設(shè)直接溝通,而是直接和內(nèi)存打交道。
- 在數(shù)據(jù)層面上,外設(shè)指揮和內(nèi)存打交道。
所以說,程序的運(yùn)行必須加載到內(nèi)存中,因?yàn)镃PU想要執(zhí)行我們的程序,訪問我們的數(shù)據(jù),就必須從內(nèi)存中讀取,這是馮諾依曼體系結(jié)構(gòu)規(guī)定的。
💕 那么,在硬件層面,單機(jī)和跨主機(jī)之間的數(shù)據(jù)流是如何流向的呢?
對馮諾依曼的理解,不能停留在概念上,要深入到對軟件數(shù)據(jù)流理解上,那么,當(dāng)你從登錄上qq開始和某位朋友聊天開始,整個(gè)信息是如何在體系結(jié)構(gòu)中流動的?
當(dāng)我和我的朋友同時(shí)打開QQ時(shí),我們的QQ其實(shí)已經(jīng)被加載到內(nèi)存中了,然后當(dāng)我們在對話框中輸入消息時(shí),我們的數(shù)據(jù)已經(jīng)被輸入到內(nèi)存中了,下面我們的數(shù)據(jù)在內(nèi)存中被加密計(jì)算,計(jì)算完之后將數(shù)據(jù)返回到內(nèi)存,然后顯示到輸出設(shè)備,也就是我們的顯示器和網(wǎng)卡上,這時(shí)我們就可以在顯示器上看到我們所發(fā)送的消息了。
緊接著,我們的數(shù)據(jù)還會通過網(wǎng)卡輸入到對方的電腦上,對方通過網(wǎng)卡接收到數(shù)據(jù)后,加載到內(nèi)存中,然后通過CPU將數(shù)據(jù)進(jìn)行解密操作并寫回內(nèi)存,最后通過輸出設(shè)備顯示到對方的顯示器上。
2. 操作系統(tǒng)
操作系統(tǒng)是一款進(jìn)行軟硬件資源管理的軟件
,在這里我們先來解釋一下,為什么操作系統(tǒng)要對軟硬件進(jìn)行管理呢?
其實(shí)是因?yàn)?#xff0c;操作系統(tǒng)對下要管理好軟硬件資源,對上需要給用戶提供良好(安全、穩(wěn)定、高效、功能豐富等)的執(zhí)行環(huán)境。
那么我們應(yīng)該如何理解操作系統(tǒng)對硬件做管理呢?其實(shí),管理的本質(zhì)就是對數(shù)據(jù)進(jìn)行管理,管理的方法是:先描述、后組織。
💖 管理的本質(zhì)是對數(shù)據(jù)進(jìn)行管理
對于這一點(diǎn),我們應(yīng)該如何理解呢?我們可以以學(xué)校為例:假設(shè)學(xué)校只有校長、輔導(dǎo)員和學(xué)生。那么在這里,校長就是我們的管理者,同時(shí)也是做決策的人,而輔導(dǎo)員呢就是執(zhí)行者,最后輔導(dǎo)員把決策交給我們學(xué)生,學(xué)生才是真正的被管理者。
因?yàn)楣芾淼谋举|(zhì)就是數(shù)據(jù)進(jìn)行管理,但在這里我們需要考慮的是,管理者是如何拿到被管理者的數(shù)據(jù)的呢?就好比說,校長是如何拿到我們學(xué)生的數(shù)據(jù)的呢?其實(shí)也很簡單,通過輔導(dǎo)員拿到的了。而校長只要管理好我們學(xué)生的這些數(shù)據(jù),就能將學(xué)生管理起來了,所以管理的本質(zhì)就是對數(shù)據(jù)做管理。
💕 管理的方法是先描述、后組織
這里我們可以想一下,學(xué)校的學(xué)生那么多,校長是如何管理的呢?這里我們提出了一種方法,叫做 ‘先描述、后組織’,
先描述
,就好比先將將被管理者的數(shù)據(jù)抽象成一個(gè)結(jié)構(gòu)體(類),后組織
,就是使用各種數(shù)據(jù)結(jié)構(gòu)將數(shù)據(jù)管理起來,這里我們可以先將學(xué)生的各種信息定義一個(gè)結(jié)構(gòu)體,然后,將學(xué)生們通過鏈表鏈接起來,那么現(xiàn)在對學(xué)生數(shù)據(jù)做管理就變成了,對鏈表的管理。
當(dāng)然了,對應(yīng)到我們的計(jì)算機(jī)中,操作系統(tǒng)
就相當(dāng)于我們的管理者,而硬件驅(qū)動
就相當(dāng)于我們的執(zhí)行者,而硬件(軟件)
就是我們被管理者。
當(dāng)然了,我們的操作系統(tǒng)也是不相信任何人的,為了保護(hù)操作系統(tǒng)不受到任何的侵害,但是又必須又必須給上層用戶提供服務(wù),在用戶和操作系統(tǒng)之間有一層系統(tǒng)調(diào)用接口,但由于系統(tǒng)調(diào)用接口的使用成本很高,所以相關(guān)人員就在系統(tǒng)調(diào)用上面進(jìn)行了二次軟件開發(fā),例如:圖形化界面、shell和工具集等。
系統(tǒng)調(diào)用和庫函數(shù):
- 在開發(fā)角度,操作系統(tǒng)對外會表現(xiàn)為一個(gè)整體,但是會暴露自己的部分接口,供上層開發(fā)使用,這部分由操作系統(tǒng)提供的接口,叫做系統(tǒng)調(diào)用。
- 系統(tǒng)調(diào)用在使用上,功能比較基礎(chǔ),對用戶的要求相對也比較高,所以,有心的開發(fā)者可以對部分系統(tǒng)調(diào)用進(jìn)行適度封裝,從而形成庫,有了庫,就很有利于更上層用戶或者開發(fā)者進(jìn)行二次開發(fā)。
計(jì)算機(jī)的體系結(jié)構(gòu)圖
3. 進(jìn)程
進(jìn)程的基本概念
進(jìn)程
在課本中的描述一般為:程序的一個(gè)執(zhí)行實(shí)例,正在執(zhí)行的程序,或者是一個(gè)程序運(yùn)行起來(程序被加載到內(nèi)存)就是進(jìn)程,進(jìn)程的內(nèi)核觀點(diǎn)是擔(dān)當(dāng)分配系統(tǒng)資源(CPU時(shí)間,內(nèi)存)的實(shí)體。但是這些概念都很膚淺,我們應(yīng)該如何去描述一個(gè)進(jìn)程呢?
💕 描述進(jìn)程
- 進(jìn)程信息被放在一個(gè)叫做進(jìn)程控制塊的數(shù)據(jù)結(jié)構(gòu)中,可以理解為進(jìn)程屬性的集合。
- 課本上稱之為PCB(process control block), Linux操作系統(tǒng)下的PCB是:
task_struct
當(dāng)我們把多個(gè)程序加載到內(nèi)存中時(shí),操作系統(tǒng)會對這些程序進(jìn)行先描述,后組織。那么操作系統(tǒng)是如何對我們的程序進(jìn)行描述和組織的呢?
我們將寫好的代碼編譯鏈接形成可執(zhí)行程序存放在磁盤上,然后我們運(yùn)行這個(gè)程序時(shí),需要先將這個(gè)程序加載到內(nèi)存中,然后通過CPU進(jìn)行運(yùn)算。當(dāng)我們的程序加載到內(nèi)存中時(shí),操作系統(tǒng)會對我們的程序進(jìn)行管理,對程序的管理方法我們在上面也提及到了:先描述,在組織
。
操作系統(tǒng)在對我們的進(jìn)程進(jìn)行先描述,后組織的時(shí)候,會先將我們的程序的共有屬性創(chuàng)建一個(gè)結(jié)構(gòu)體,然后對我們的每一個(gè)進(jìn)程創(chuàng)建一個(gè)結(jié)構(gòu)體對象,這就是先描述
的過程。接下來我們的操作系統(tǒng)會使用特性的數(shù)據(jù)結(jié)構(gòu)(比如鏈表)將我們的結(jié)構(gòu)體對象組織起來,這就是后組織
的過程。然后我們的操作系統(tǒng)對進(jìn)程的管理就會轉(zhuǎn)換成對特定數(shù)據(jù)結(jié)構(gòu)的管理。當(dāng)然了,這個(gè)描述和組織進(jìn)程的東西就被稱為進(jìn)程控制塊(PCB)。
所以,在這里我們也引出了進(jìn)程真正的概念:進(jìn)程=內(nèi)核關(guān)于進(jìn)程的相關(guān)數(shù)據(jù)結(jié)構(gòu)+當(dāng)前進(jìn)程的代碼和數(shù)據(jù)。
進(jìn)程控制塊(PCB) 是我們操作系統(tǒng)用來描述進(jìn)程的工具,他包含了進(jìn)程屬性的集合。在Linux中描述進(jìn)程的結(jié)構(gòu)體叫做task_struct。他是Linux內(nèi)核的一種數(shù)據(jù)結(jié)構(gòu),它會被裝載到
RAM(內(nèi)存)
里并且包含著進(jìn)程的信息。
下面我們看一下task_ struct內(nèi)容分類:
- 標(biāo)示符: 描述本進(jìn)程的唯一標(biāo)示符,用來區(qū)別其他進(jìn)程。
- 狀態(tài): 任務(wù)狀態(tài),退出代碼,退出信號等。
- 優(yōu)先級: 相對于其他進(jìn)程的優(yōu)先級。
- 程序計(jì)數(shù)器: 程序中即將被執(zhí)行的下一條指令的地址。
- 內(nèi)存指針: 包括程序代碼和進(jìn)程相關(guān)數(shù)據(jù)的指針,還有和其他進(jìn)程共享的內(nèi)存塊的指針
- 上下文數(shù)據(jù): 進(jìn)程執(zhí)行時(shí)處理器的寄存器中的數(shù)據(jù)[休學(xué)例子,要加圖CPU,寄存器]。
- I/ O狀態(tài)信息: 包括顯示的I/O請求,分配給進(jìn)程的I/ O設(shè)備和被進(jìn)程使用的文件列表。
- 記賬信息: 可能包括處理器時(shí)間總和,使用的時(shí)鐘數(shù)總和,時(shí)間限制,記賬號等。
- 其他信息
查看進(jìn)程和殺死進(jìn)程
💕 查看進(jìn)程
再查看進(jìn)程之前我們需要先創(chuàng)建寫一段普通的C語言代碼:
查看進(jìn)程有兩種方式,這里我們先來講解第一種:
ps ajx | head -1 && ps ajx | grep '進(jìn)程名'
當(dāng)然了我們需要先將我們的程序運(yùn)行起來:
這里我們還可以看到在我們的myproc進(jìn)程的下面還有一個(gè)grep進(jìn)程,這是因?yàn)間rep指令也是一個(gè)進(jìn)程,進(jìn)程在調(diào)度的時(shí)候是具有動態(tài)屬性的。這里我們還需要解釋一個(gè)概念那就是PID
和PPID
的概念,操作系統(tǒng)里指進(jìn)程識別號,也就是進(jìn)程標(biāo)識符
。操作系統(tǒng)里每打開一個(gè)程序都會創(chuàng)建一個(gè)進(jìn)程ID,即PID。 當(dāng)然了,PPID就是父進(jìn)程的進(jìn)程ID號。
查看進(jìn)程的第二種方式:通過查看進(jìn)程目錄 ls /proc
來查看進(jìn)程
當(dāng)然了我們還可以直接到==/proc/進(jìn)程pid==目錄下去查看進(jìn)程對應(yīng)的可執(zhí)行程序。
💕 殺死進(jìn)程
我們要結(jié)束我們的進(jìn)程可以有兩種方法:直接按ctrl+c結(jié)束進(jìn)程或者殺死進(jìn)程。殺死進(jìn)程需要的命令是:kill -9 進(jìn)程標(biāo)識符
父進(jìn)程和子進(jìn)程
上面我們已經(jīng)介紹了父進(jìn)程和子進(jìn)程有各自的進(jìn)程ID,但是究竟什么是父進(jìn)程呢?什么又是子進(jìn)程呢?其實(shí),我們平常所寫的進(jìn)程都是由bash
這個(gè)父進(jìn)程來創(chuàng)建的,bash其實(shí)就是shell外殼,shell為了防止自身奔潰,一幫會通過派生子進(jìn)程的方式去執(zhí)行我們的指令。
我們可以通過getpid
和getppid
函數(shù)來獲取當(dāng)前進(jìn)程的ID和當(dāng)前進(jìn)程父進(jìn)程的ID。下面我們來看一下這兩個(gè)函數(shù):
這里我們修改一下我們的程序,看一下父進(jìn)程和子進(jìn)程的ID:
如果我們把我們的程序終止掉,然后再次運(yùn)行的時(shí)候,就會發(fā)現(xiàn)子進(jìn)程的ID一直在變,而父進(jìn)程的ID卻不變。那么我們現(xiàn)在來看一下這個(gè)父進(jìn)程究竟是什么?
這是我們就可以恍然大悟了,原來該父進(jìn)程就是bash,bash通過創(chuàng)建子進(jìn)程的方式來運(yùn)行我們的程序,那么shell是如何創(chuàng)建子進(jìn)程的呢?下面就讓我們來認(rèn)識一下如何創(chuàng)建子進(jìn)程。
通過系統(tǒng)調(diào)用創(chuàng)建子進(jìn)程
我們先通過 man fork 來認(rèn)識一下fork
函數(shù):
fork函數(shù)的返回值是fork函數(shù)的重點(diǎn),如果子進(jìn)程創(chuàng)建成功,fork函數(shù)給父進(jìn)程返回子進(jìn)程的id,給子進(jìn)程返回0,如果子進(jìn)程創(chuàng)建失敗將會給父進(jìn)程返回-1。
這里我們先修改一下我們的代碼:
但是這個(gè)代碼為什么可以既執(zhí)行if里面的語句,又可以執(zhí)行if else里面的代碼呢?
結(jié)論:
- fork之后,執(zhí)行流會變成2個(gè)執(zhí)行流。
- fork之后,誰先運(yùn)行由調(diào)度器決定。
- fork之后的代碼共享,通常通過
if
和elseif
來進(jìn)行執(zhí)行流分流。
下面我們再來看一個(gè)更離譜的事情,我們將代碼中的內(nèi)容做出如下修改:
以上問題我們都可以歸結(jié)為三點(diǎn):
- fork做了什么?
- fork是如何看待代碼和數(shù)據(jù)的?
- fork如何理解兩個(gè)返回值問題?
💕 fork做了什么?
子進(jìn)程會創(chuàng)建一份獨(dú)立的pcb結(jié)構(gòu),但子進(jìn)程的pcb的大部分屬性都是以父進(jìn)程為模板進(jìn)行拷貝的,當(dāng)然了對于PID和PPID這些值是不能拷貝的。
💕 fork是如何看待代碼和數(shù)據(jù)的?
進(jìn)程在運(yùn)行的時(shí)候,是具有獨(dú)立性的,當(dāng)然父子進(jìn)程再運(yùn)行的時(shí)候也是具有獨(dú)立性的,如果我們父子進(jìn)程同時(shí)運(yùn)行但我們把其中任意一個(gè)進(jìn)程殺死后,并不會影響另一個(gè)進(jìn)程。當(dāng)然如果父子進(jìn)程中有相同的變量,但我們修改父進(jìn)程或子進(jìn)程中的任意一個(gè)數(shù)據(jù)時(shí),操作系統(tǒng)會在當(dāng)前進(jìn)程中觸發(fā)
寫時(shí)拷貝
。
💕 fork是如何理解兩個(gè)返回值問題的?
當(dāng)我們的函數(shù)執(zhí)行return功能之前其實(shí)函數(shù)中的主體部分就已經(jīng)完成了,但是return也是一條語句,在這之前子進(jìn)程已經(jīng)被創(chuàng)建,甚至可能已經(jīng)被調(diào)度,我們在執(zhí)行return時(shí),父進(jìn)程可以被調(diào)度執(zhí)行一次,而子進(jìn)程也有它的執(zhí)行流,所以return也會被調(diào)度執(zhí)行一次。所以一條return就被執(zhí)行了兩次,好像讓我們看到了兩個(gè)返回值。