目錄
一、馮諾依曼體系結構
二、操作系統(tǒng)
?1、概念
?2、設計OS的目的
三、進程
?1、基本概念
?2、描述進程-PCB
?3、組織進程
?4、查看進程和終止
?5、通過系統(tǒng)調用獲取進程標識符
?6、通過系統(tǒng)調用創(chuàng)建進程-fork
?7、進程狀態(tài)
?8、特殊進程
? ?8.1 僵尸進程
? ?8.2 孤兒進程
?
一、馮諾依曼體系結構
我們常見的計算機,如筆記本。我們不常見的計算機,如服務器,大部分都遵守馮諾依曼體系。
下面是馮諾依曼體系結構圖:
? 我們所認識的計算機,都是輸入設備、存儲器、運算器、控制器、輸出設備組成的。
- 輸入單元:包括鍵盤,鼠標,掃描儀,寫板,網卡,磁盤等;
- 中央處理器(CPU):含有運算器和控制器等;
- 輸出單元:顯示器,網卡,打印機等。
? 關于馮諾依曼,必須強調幾點:
- 這里的存儲器指的是內存;
- 不考慮緩存情況,這里的CPU能且只能對內存進行讀寫,不能訪問外設(輸入或輸出設備);
- 外設(輸入或輸出設備)要輸入或者輸出數據,也只能寫入內存或者從內存中讀??;
- 一句話,所有設備都只能直接和內存打交道。
? 我們的數據需要先從磁盤加載到內存中,然后由CPU讀取并進行計算,將計算的結果再次加載到內存中,最后再由內存寫入磁盤,通過輸出設備將數據交給我們。
? 為什么CPU為什么不能直接訪問外設呢?
? 因為輸入輸出設備稱之為外設,外設一般是很慢的,比如說磁盤,相對于內存,他的速度是非常慢的,但CPU的計算速度確是非常快的。就好比從磁盤的讀取速度很慢,但是CPU的計算速度卻很快,但是整體的速度還是以磁盤的讀取速度為主的,所以整體效率就以外設為主。
??對馮諾依曼的理解,不能停留在概念上,要深入到對軟件數據流理解上,請解釋,從你登錄上qq開始和某位朋友聊天開始,數據的流動過程。從你打開窗口,開始給他發(fā)消息,到他的到消息之后的數據流動過程。如果是在qq上發(fā) 送文件呢?
? ?首先從鍵盤上讀取信息然后加載到內存,再從內存將數據通過一系列操作發(fā)送到輸出設備上(網卡),然后通過一系列的網絡操作將數據發(fā)送到朋友的輸入設備上(網卡),朋友的電腦再從輸入設備中將數據讀到內存,然后通過輸出設備(顯示器)就可以將信息發(fā)送到朋友的電腦上。
二、操作系統(tǒng)
?1、概念
? 任何計算機系統(tǒng)都包含一個基本的程序集合,稱為操作系統(tǒng)(OS)?;\統(tǒng)的理解,操作系統(tǒng)包括:
- 內核(進程管理,內存管理,文件管理,驅動管理);
- 其他程序(例如函數庫,shell程序等等)。
??操作系統(tǒng)是一款進行軟硬件資源管理的軟件。為什么操作系統(tǒng)要對軟硬件進行管理呢?
? 因為操作系統(tǒng)對下要管理好軟硬件資源,對上需要給用戶提供良好(安全、穩(wěn)定、高效、功能豐富等)的執(zhí)行環(huán)境。
? 操作系統(tǒng)管理的本質:先描述,再組織。
- 描述:通過 struct 結構體對各種數據進行描述;
- 組織:通過 鏈表 等高效的數據結構對數據進行組織管理。
? 在計算機中,操作系統(tǒng)就相當于我們的管理者,而硬件驅動就相當于我們的執(zhí)行者,而軟件就是我們被管理者。
? 首先操作系統(tǒng)是不相信任何人的,正如我們是銀行的用戶,經常去銀行存錢,但銀行就信任我們嗎?為了避免用戶中有人惡意破壞,而對操作系統(tǒng)造成傷害, 所以操作系統(tǒng)并不是暴露自己的全部功能而是以系統(tǒng)調用來訪問操作系統(tǒng)。由于系統(tǒng)調用的使用成本可能較高,之后在此基礎上便有人進行二次的軟件開發(fā)而產生了?圖形化界面?和 shell 及工具集。
? ?系統(tǒng)調用與庫函數?
- 在開發(fā)角度,操作系統(tǒng)對外會表現為一個整體,但是會暴露自己的部分接口,供上層開發(fā)使用,這部分 由操作系統(tǒng)提供的接口,叫做系統(tǒng)調用;
- 系統(tǒng)調用在使用上,功能比較基礎,對用戶的要求相對也比較高,所以,有心的開發(fā)者可以對部分系統(tǒng) 調用進行適度封裝,從而形成庫,有了庫,就很有利于更上層用戶或者開發(fā)者進行二次開發(fā)。
?2、設計OS的目的
- 與硬件交互,管理所有的軟硬件資源;
- 為用戶程序(應用程序)提供一個良好的執(zhí)行環(huán)境。
? 計算機的體系的結構是層狀的,一般不可以跳過某個層
三、進程
?1、基本概念
- 課本概念:程序的一個執(zhí)行實例,正在執(zhí)行的程序等;
- 內核觀點:擔當分配系統(tǒng)資源(CPU時間,內存)的實體。
?我們打開任務管理器便會發(fā)現這些正在運行的可執(zhí)行文件都是一個個進程。
?2、描述進程-PCB
- 進程信息被放在一個叫做進程控制塊的數據結構中,可以理解為進程屬性的集合;
- 課本上稱之為PCB(process control block),Linux操作系統(tǒng)下的PCB是: task_struct
?task_struct-PCB的一種
- 在 Linux 中描述進程的結構體叫做 task_struct;
- task_struct 是 Linux 內核的一種數據結構,它會被裝載到 RAM(內存) 里并且包含著進程的信息。
? 操作系統(tǒng)在對我們的進程進行先描述,后組織的時候,會先將我們的程序的共有屬性創(chuàng)建一個結構體,然后對我們的每一個進程創(chuàng)建一個結構體對象,這就是先描述的過程。接下來我們的操作系統(tǒng)會使用特性的數據結構(比如鏈表)將我們的結構體對象組織起來,這就是后組織的過程。然后我們的操作系統(tǒng)對進程的管理就會轉換成對特定數據結構的管理。
? ?所以,進程 =?內核關于進程的相關數據結構+當前進程的代碼和數據。
?task_ struct內容分類
- 標示符: 描述本進程的唯一標示符,用來區(qū)別其他進程;
- 狀態(tài): 任務狀態(tài),退出代碼,退出信號等;
- 優(yōu)先級: 相對于其他進程的優(yōu)先級;
- 程序計數器: 程序中即將被執(zhí)行的下一條指令的地址;
- 內存指針: 包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針;
- 上下文數據: 進程執(zhí)行時處理器的寄存器中的數據[休學例子,要加圖CPU,寄存器];
- I/O狀態(tài)信息: 包括顯示的I/O請求,分配給進程的I/O設備和被進程使用的文件列表;
- 記賬信息: 可能包括處理器時間總和,使用的時鐘數總和,時間限制,記賬號等;
- 其他信息。
?3、組織進程
可以在內核源代碼里找到它。所有運行在系統(tǒng)里的進程都以task_struct鏈表的形式存在內核里。
?4、查看進程和終止
查看一個進程的基本信息,我們可以利用命令 ps? -axj? 列出當前系統(tǒng)所用進程信息;
先測試一段代碼:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("我是一個進程!\n");
sleep(1);
}
return 0;
}
在輸入查看進程的命令,如下所示:?
輸入 ps -axj | head -1 && ps -axj | grep "test",就可以獲得表頭及帶有 test 的進程。
? 這里我們還需要解釋一個概念那就是 PID 和 PPID 的概念,操作系統(tǒng)里指進程識別號,也就是進程表示符。操作系統(tǒng)里每打開一個程序都會創(chuàng)建一個進程 ID,即 PID 。?當然了,PPID 就是父進程的進程 ID 號。
? 殺死進程
有兩種方法:第一種是Ctrl + c 是強制結束進程,第二種是:用命令的形式,kill -9 PID,指定目標進程殺死。
? 在這里我推薦使用第二種方法。
?5、通過系統(tǒng)調用獲取進程標識符
- 進程id(PID);
- 父進程id(PPID).
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
using namespace std;
int main()
{
pid_t t = fork();
if (t == 0)
{
while (1)
{
cout << "我是一個子進程" << " pid:" << getpid() << " ppid:" << getppid() << endl;
sleep(1);
}
}
else if (t > 0)
{
while (1)
{
cout << "我是一個父進程" << " pid:" << getpid() << " ppid:" << getppid() << endl;
sleep(1);
}
}
return 0;
}
?6、通過系統(tǒng)調用創(chuàng)建進程-fork
- 運行 man fork 認識 fork;
- fork有兩個返回值;
- 父子進程代碼共享,數據各自開辟空間,私有一份(采用寫時拷貝);
- fork 之后通常要用 if 進行分流。
??可以看到?fork()?這個函數很特殊,成功創(chuàng)建子進程后居然有兩個返回值,給父進程返回子進程pid,給子進程返回 0,如果創(chuàng)建失敗那么就返回 -1。?
? 如何理解兩個返回值
當 fork() 要對值進行返回的時候,其實在函數的內部創(chuàng)建子進程的工作已經完成,此時已經有了兩個進程,兩個進程繼續(xù)執(zhí)行下面的語句,執(zhí)行完 fork() 之后自然都會有返回值,這樣在我們看來就好像有兩個返回值,實則我們在接收返回值時便已觸發(fā)了寫時拷貝(寫實拷貝就是當操作系統(tǒng)檢測到子進程有寫的操作的時候,操作系統(tǒng)就會給子進程分配相應的物理空間),看似相同的 ret 實則儲存在不同的空間。
?7、進程狀態(tài)
- R運行狀態(tài)(running): 并不意味著進程一定在運行中,它表明進程要么是在運行中要么在運行隊列里;
- S睡眠狀態(tài)(sleeping): 意味著進程在等待事件完成 (這里的睡眠有時候也叫做可中斷睡眠 (interruptible sleep));
- D磁盤休眠狀態(tài)(Disk sleep)有時候也叫不可中斷睡眠狀態(tài)(uninterruptible sleep),在這個狀態(tài)的 進程通常會等待IO的結束;
- T停止狀態(tài)(stopped): 可以通過發(fā)送 SIGSTOP 信號給進程來停止(T)進程。這個被暫停的進程可 以通過發(fā)送 SIGCONT 信號讓進程繼續(xù)運行;
- X死亡狀態(tài)(dead):這個狀態(tài)只是一個返回狀態(tài),你不會在任務列表里看到這個狀態(tài).
ps? -axj 指令是查看進程的信息
? (1)R運行狀態(tài)(running): 并不意味著進程一定在運行中,它表明進程要么是在運行中要么在運行隊列里;?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
while (1)
{
;
}
return 0;
}
運行時,查看進程的狀態(tài)是R+,說明是運行狀態(tài)。
?后面有個+號表示什么意思呢?
這里+表示該進程是前臺運行的,當我們使用ctrl+c的時候能夠終止掉該進程,不寫+表示的是后臺運行的,這時用ctrl+c是無法終止掉該程序的,要用命令殺掉進程來終止。
? (2)S睡眠狀態(tài)(sleeping): 意味著進程在等待事件完成 (這里的睡眠有時候也叫做可中斷睡眠 (interruptible sleep));
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int n = 0;
scanf("%d", &n);
return 0;
}
運行時,查看進程的狀態(tài)是S+,說明是睡眠狀態(tài)。
?(3)T停止狀態(tài)(stopped): 可以通過發(fā)送 SIGSTOP 信號給進程來停止(T)進程。這個被暫停的進程可以通過發(fā)送 SIGCONT 信號讓進程繼續(xù)運行;
kill -l :就能顯示所有的信號,而 SIGSTOP 信號對應 19 ,SIGCONT 信號對應 18。
當我們輸入kill -19 PID ,該進程就會處于停止狀態(tài)。
當我們輸入kill -18?PID ,該進程就會恢復狀態(tài)。
? (4)死亡狀態(tài):這個狀態(tài)只是一個返回狀態(tài),是一瞬間的,你可能不會在任務列表里看到這個狀態(tài),因為一個進程死亡后就會變成僵尸進程。
? (5)僵尸狀態(tài):進程死亡后的狀態(tài),一個進程死亡后,會處于僵尸狀態(tài)如果其父進程不回收,會一直占用資源,造成內存泄漏。
?8、特殊進程
? ?8.1 僵尸進程
- 僵尸狀態(tài)(Zombies)是一個比較特殊的狀態(tài)。當進程退出并且父進程(使用wait()系統(tǒng)調用) 沒有讀取到子進程退出的返回代碼時就會產生僵尸進程。
- 僵死進程會以終止狀態(tài)保持在進程表中,并且會一直在等待父進程讀取退出狀態(tài)代碼。
- 只要子進程退出,父進程還在運行,但父進程沒有讀取子進程狀態(tài),子進程進入Z狀態(tài)
下面演示一下僵尸進程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
//子進程
while (1)
{
printf("我是子進程,我的pid :%d,我的ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
else if (id > 0)
{
//父進程
while (1)
{
printf("我是父進程,我的pid :%d,我的ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
else
{
perror("fail");
exit(-1);
}
return 0;
}
運行時,當中途用kill -9 PID 殺死子進程,此時子進程就是一個僵尸進程。
? ?8.2 孤兒進程
- 父進程如果提前退出,那么子進程后退出,進入Z之后,那該如何處理呢?
- 父進程先退出,子進程就稱之為“孤兒進程”
- 孤兒進程被1號init進程領養(yǎng),當然要有init進程回收嘍。
?看下面這段代碼,就是父進程先運行3秒,然后就退出了,此時子進程就是孤兒進程。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if (id < 0)
{
perror("fail");
return 1;
}
else if (id == 0)
{
//子進程
printf("I am child, pid : %d\n", getpid());
sleep(10);
}
else
{
//父進程
printf("I am parent, pid: %d\n", getpid());
sleep(3);
exit(-1);
}
return 0;
}
運行結果對比:
?
?
本文要是有不足的地方,歡迎大家在下面評論,我會在第一時間更正。
文章來源:http://www.zghlxwxcb.cn/news/detail-495194.html
老鐵們,記著點贊加關注!!!???文章來源地址http://www.zghlxwxcb.cn/news/detail-495194.html
到了這里,關于【Linux】馮諾依曼體系結構 && 操作系統(tǒng) && 進程概念的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!