wordpress輸入密碼訪問(wèn)湖南有實(shí)力seo優(yōu)化
什么是進(jìn)程
想要了解什么是進(jìn)程,或者說(shuō),為什么會(huì)有進(jìn)程這個(gè)概念,我們就需要去了解現(xiàn)代計(jì)算機(jī)的設(shè)計(jì)框架(馮·諾依曼體系):
計(jì)算機(jī)從設(shè)計(jì)之初就以執(zhí)行程序?yàn)楹诵娜蝿?wù),也就是運(yùn)算器從內(nèi)存中讀取,也只從內(nèi)存中讀取,輸入設(shè)備也只能輸入到內(nèi)存中。在早期計(jì)算機(jī)系統(tǒng)中,計(jì)算機(jī)一次只能運(yùn)行一個(gè)程序,所有資源都被這個(gè)程序獨(dú)占。隨著需求的增加,計(jì)算機(jī)需要能夠同時(shí)運(yùn)行多個(gè)任務(wù),比如一個(gè)任務(wù)處理文件讀寫,另一個(gè)任務(wù)計(jì)算數(shù)據(jù)。這時(shí),系統(tǒng)需要一種機(jī)制來(lái)管理多個(gè)程序的執(zhí)行,確保它們能夠公平、有效地利用計(jì)算機(jī)的資源,這就是進(jìn)程概念產(chǎn)生的原因。
進(jìn)程的定義
進(jìn)程是操作系統(tǒng)中執(zhí)行程序的一個(gè)實(shí)例,它包含了程序運(yùn)行所需的所有信息和資源。簡(jiǎn)單來(lái)說(shuō),進(jìn)程就是操作系統(tǒng)為運(yùn)行中的程序提供的一個(gè)執(zhí)行環(huán)境,包括程序代碼、數(shù)據(jù)、打開的文件、內(nèi)存空間和處理器時(shí)間等。一個(gè)程序在操作系統(tǒng)中可能運(yùn)行多個(gè)實(shí)例,每個(gè)實(shí)例就是一個(gè)獨(dú)立的進(jìn)程。
每個(gè)進(jìn)程通常包含以下幾個(gè)部分:
- 可執(zhí)行程序代碼:程序的二進(jìn)制指令。
- 進(jìn)程的內(nèi)存空間:包括堆、棧、數(shù)據(jù)段等。
- 進(jìn)程狀態(tài)信息:CPU寄存器、程序計(jì)數(shù)器等,記錄當(dāng)前進(jìn)程執(zhí)行的具體狀態(tài)。
- 調(diào)度信息:操作系統(tǒng)需要的信息,用來(lái)決定何時(shí)執(zhí)行該進(jìn)程。
這些也就是計(jì)算機(jī)或者說(shuō)是操作系統(tǒng)用來(lái)描述每一個(gè)需要執(zhí)行的程序的方法,在linux里,描述程序的結(jié)構(gòu)體叫做 task_struct? 也叫做PCB? (Process Control Block,進(jìn)程控制塊)PCB是一個(gè)概念,task_struct則是在linux里PCB的具體表現(xiàn)。
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;int lock_depth; /* Lock depth */int prio, static_prio;struct list_head run_list;prio_array_t *array;
}
這是在??linux-2.6.11.1 截取的task_struct 片段,通過(guò)這個(gè)片段,我們可以看到操作系統(tǒng)用來(lái)描述進(jìn)程信息的部分屬性。
state
表示進(jìn)程的當(dāng)前狀態(tài)(可運(yùn)行、不可運(yùn)行等)。usage
是引用計(jì)數(shù),記錄進(jìn)程被使用的次數(shù)。prio
和static_prio
是進(jìn)程的優(yōu)先級(jí),用于調(diào)度。run_list
用于將進(jìn)程掛入運(yùn)行隊(duì)列,array
管理進(jìn)程的優(yōu)先級(jí)隊(duì)列。
struct list_head {struct list_head *next, *prev;
};
?而這里鏈接進(jìn)程進(jìn)入運(yùn)行隊(duì)列的結(jié)構(gòu)就是這個(gè)結(jié)構(gòu)體,
通過(guò)上篇博客(不同結(jié)構(gòu)體之間的鏈接-CSDN博客)的字節(jié)運(yùn)算從而獲得每一個(gè)結(jié)構(gòu)體的成員。
為什么需要進(jìn)程?
引入進(jìn)程的核心原因在于多任務(wù)處理和資源管理。現(xiàn)代計(jì)算機(jī)需要同時(shí)運(yùn)行多個(gè)任務(wù),而進(jìn)程是管理這些任務(wù)的一種機(jī)制。進(jìn)程可以:
- 隔離任務(wù):每個(gè)進(jìn)程運(yùn)行在自己的內(nèi)存空間中,避免相互影響。這種隔離性提高了系統(tǒng)的安全性和穩(wěn)定性。
- 資源管理:操作系統(tǒng)通過(guò)進(jìn)程來(lái)分配 CPU、內(nèi)存和I/O設(shè)備等資源,確保每個(gè)進(jìn)程都能公平地使用系統(tǒng)資源。
- 并發(fā)執(zhí)行:通過(guò)多任務(wù)調(diào)度,操作系統(tǒng)可以在一個(gè) CPU 上快速切換進(jìn)程,使得多個(gè)進(jìn)程看似同時(shí)執(zhí)行,提升系統(tǒng)的利用效率。
進(jìn)程的出現(xiàn),使得操作系統(tǒng)能夠高效管理多個(gè)任務(wù),并為每個(gè)任務(wù)提供獨(dú)立、受保護(hù)的執(zhí)行環(huán)境。
查看進(jìn)程?
在window操作系統(tǒng)中,由于圖形化的原因,我們可以通過(guò)任務(wù)管理器更加方便直接的看到當(dāng)前計(jì)算機(jī)正在運(yùn)行的進(jìn)程,同時(shí)我們還可以看到他的基本信息,那么在Linux系統(tǒng)中,我們?cè)撊绾尾榭催M(jìn)程呢 ??
我們可以直接在 /proc/ 文件夾里查看進(jìn)程
這些藍(lán)色的數(shù)字就是進(jìn)程的PID (唯一標(biāo)識(shí)符) ,如果我們想要查進(jìn)程信息,同樣可以進(jìn)入文件夾內(nèi)查看
大多數(shù)進(jìn)程信息同樣可以使用top和ps這些用戶級(jí)工具來(lái)獲取
我們可以寫一個(gè)一直循環(huán)的進(jìn)程用來(lái)使用ps查詢
#include <stdio.h>
#include <unistd.h>int main()
{while (1){sleep(5); // 暫停 5 秒printf("mypid: %d\n"); }return 0;
}
為什么有兩個(gè)呢,因?yàn)樽鳛椴樵內(nèi)蝿?wù)的 grep 也是一個(gè)進(jìn)程?,同樣,我們也可以使用系統(tǒng)接口,在程序內(nèi)獲取進(jìn)程PID 和父進(jìn)程的PID (PPID)?
每一次我們執(zhí)行程序,進(jìn)程PID都是改變的?
top命令大多數(shù)情況下都是用在進(jìn)程優(yōu)先級(jí)上,我們?cè)趦?yōu)先級(jí)那里在回顧top的使用
?創(chuàng)建進(jìn)程
在系統(tǒng)給出的系統(tǒng)調(diào)用接口中,也有創(chuàng)建子進(jìn)程的相關(guān)函數(shù),那就是 fork() 。
?fork()
函數(shù)是 Unix 和 Linux 系統(tǒng)中用于創(chuàng)建新進(jìn)程的系統(tǒng)調(diào)用。它是進(jìn)程控制的一部分,用于實(shí)現(xiàn)多任務(wù)處理。fork()
在調(diào)用時(shí)會(huì)創(chuàng)建一個(gè)新的進(jìn)程,這個(gè)新進(jìn)程是調(diào)用進(jìn)程的一個(gè)副本,稱為子進(jìn)程
- 創(chuàng)建新進(jìn)程:
fork()
會(huì)復(fù)制調(diào)用進(jìn)程的所有屬性,創(chuàng)建一個(gè)新的進(jìn)程。 - 返回值:
- 在父進(jìn)程中,
fork()
返回新創(chuàng)建的子進(jìn)程的 PID。 - 在子進(jìn)程中,
fork()
返回0
。 - 如果
fork()
失敗,它會(huì)返回-1
,并設(shè)置errno
以指示錯(cuò)誤原因。
- 在父進(jìn)程中,
?我們通常使用 if 來(lái)分流父進(jìn)程與子進(jìn)程
#include<stdio.h>
#include<unistd.h> int main()
{ pid_t id = fork(); if(id==0) //子進(jìn)程 { int i = 5; while(i--) { printf("我是子進(jìn)程 pid : %d, ppid : %d\n",getpid(),getppid()); sleep(1); } } else if(id>0) { int i=7; while(i--) { printf("我是父進(jìn)程 pid: %d,ppid: %d\n",getpid(),getpid()); sleep(1); } } else { perror("進(jìn)程創(chuàng)建失敗\n"); } return 0;
}
?fork()函數(shù)
?上述代碼,我們可以看出來(lái),我們創(chuàng)建了一個(gè)子進(jìn)程,但是,在以往我們學(xué)習(xí)C語(yǔ)言的經(jīng)驗(yàn)里,一個(gè)程序里,一個(gè)值怎么進(jìn)入兩個(gè)完全不同的if判斷呢,那我們最直觀的想法就是,父進(jìn)程跟子進(jìn)程使用的數(shù)據(jù)并不是用一個(gè)甚至并沒(méi)有在一起。
在fork()官訪問(wèn)當(dāng)中,我們可以看到
內(nèi)存空間的獨(dú)立性
-
進(jìn)程復(fù)制:
- 當(dāng)調(diào)用
fork()
時(shí),操作系統(tǒng)會(huì)創(chuàng)建一個(gè)新的進(jìn)程(子進(jìn)程),這個(gè)子進(jìn)程是父進(jìn)程的一個(gè)副本。在內(nèi)存中,這意味著子進(jìn)程會(huì)有一份與父進(jìn)程相同的內(nèi)存內(nèi)容,包括代碼、數(shù)據(jù)、堆棧等。
- 當(dāng)調(diào)用
-
獨(dú)立的內(nèi)存空間:
- 雖然父進(jìn)程和子進(jìn)程在
fork()
時(shí)內(nèi)存內(nèi)容是相同的,但它們的內(nèi)存空間是獨(dú)立的。每個(gè)進(jìn)程在其自己的地址空間中操作,因此一個(gè)進(jìn)程的內(nèi)存更改不會(huì)直接影響到另一個(gè)進(jìn)程的內(nèi)存。
- 雖然父進(jìn)程和子進(jìn)程在
-
寫時(shí)復(fù)制(Copy-on-Write):
- 在現(xiàn)代操作系統(tǒng)中,
fork()
采用了一種優(yōu)化機(jī)制叫做寫時(shí)復(fù)制(Copy-on-Write)。最初,父進(jìn)程和子進(jìn)程共享相同的物理內(nèi)存頁(yè),只有當(dāng)其中一個(gè)進(jìn)程試圖修改這些內(nèi)存頁(yè)時(shí),操作系統(tǒng)才會(huì)復(fù)制這些頁(yè),確保每個(gè)進(jìn)程擁有自己的副本。這種機(jī)制減少了fork()
操作的開銷。
- 在現(xiàn)代操作系統(tǒng)中,
子進(jìn)程與父進(jìn)程的分流
當(dāng)我們了解 fork()函數(shù)后,了解了fork()函數(shù)之后的代碼是子進(jìn)程與父進(jìn)程共有的,當(dāng)子進(jìn)程要修改某一個(gè)變量的值時(shí),系統(tǒng)才會(huì)給子進(jìn)程拷貝一份這個(gè)值,并開辟空間給其進(jìn)行修改,那我們是不是可以通過(guò)取地址? id 的地址來(lái)獲得子進(jìn)程存放數(shù)據(jù)的地址呢?
#include<stdio.h>
#include<unistd.h> int main()
{ pid_t id = fork(); if(id==0) //子進(jìn)程 { printf("%p\n",&id); } else if(id>0) { printf("%p\n",&id); } else { perror("進(jìn)程創(chuàng)建失敗\n"); } return 0;
}
我們驚奇的發(fā)現(xiàn),兩個(gè)id的地址竟然是一樣的,那我們之前的拷貝的id在哪里,同一個(gè)地址給出的id值竟然會(huì)不一樣 ! 其實(shí)這是操作系統(tǒng)包含內(nèi)存的設(shè)計(jì),如果讓我們的程序簡(jiǎn)簡(jiǎn)單單就可以訪問(wèn)到物理地址,那么操作系統(tǒng)很容易就崩潰了。對(duì)與每一個(gè)進(jìn)程,或者說(shuō)是每一個(gè)程序,操作系統(tǒng)都平等分配一個(gè)虛擬地址空間,通過(guò)這個(gè)虛擬地址空間跟頁(yè)表映射到真實(shí)的物理地址,那么也就是說(shuō),這兩個(gè)id不同的是物理地址。
?
當(dāng)fork函數(shù)返回時(shí),對(duì)id進(jìn)行了寫時(shí)拷貝,子程序的頁(yè)表重新映射到操作系統(tǒng)存放的新id的物理地址(簡(jiǎn)圖)
當(dāng)子進(jìn)程被創(chuàng)建時(shí),PCB等內(nèi)容被填充,父子進(jìn)程指向相同的代碼,同時(shí),父子進(jìn)程都有了屬于自己的task_struct,可以被操作系統(tǒng)調(diào)度并執(zhí)行了,同時(shí)由于,直接拷貝導(dǎo)致消耗過(guò)大,也就有了,修改數(shù)據(jù)時(shí)分配空間同時(shí)讓子進(jìn)程的頁(yè)表指向該空間從而獲得不同的數(shù)據(jù)。頁(yè)表的設(shè)計(jì),將虛擬地址跟物理地址連接到一起并且讓內(nèi)存單獨(dú)維護(hù)內(nèi)存,進(jìn)程單獨(dú)維護(hù)進(jìn)程,保護(hù)數(shù)據(jù)的同時(shí),解耦兩部分。?
ps: Linux 源碼下載??Index of /pub/linux/kernel/