正文開始前給大家推薦個網(wǎng)站,前些天發(fā)現(xiàn)了一個巨牛的 人工智能學(xué)習(xí)網(wǎng)站, 通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。 點擊跳轉(zhuǎn)到網(wǎng)站。
命令行參數(shù)
什么是命令行參數(shù)?
我們平時寫的代碼中寫寫到的主函數(shù)main函數(shù)是可以有參數(shù)的。
我們可以看一下這段代碼的運行結(jié)果。
我們可以看到我們輸入的字符串被一空格為分隔符分成了若干個字串,并且argc就是字串的個數(shù),argv中放的都是被分割出來的字串。而argv數(shù)組最后一個原始是存放的空的,有了這個東西,我們的程序就可以支持各種各樣的命令行級別的指令選項的設(shè)置,也是為了讓我們的一個程序?qū)Σ煌倪x項做出不同的動作。
環(huán)境變量
環(huán)境變量(environment variables)一般是指在操作系統(tǒng)中用來指定操作系統(tǒng)運行環(huán)境的一些參數(shù)如:我們在編寫C/C++代碼的時候,在鏈接的時候,從來不知道我們的所鏈接的動態(tài)靜態(tài)庫在哪里,但是照樣可以鏈接成功,生成可執(zhí)行程序,原因就是有相關(guān)環(huán)境變量幫助編譯器進行查找。環(huán)境變量通常具有某些特殊用途,還有在系統(tǒng)當中通常具有全局特性。
常見的環(huán)境變量
我們在執(zhí)行自己的可執(zhí)行程序但是都需要加一個./,不然運行不要,系統(tǒng)找不到我們的可執(zhí)行程序,但是在執(zhí)行系統(tǒng)的命令是卻不需要這樣做,直接輸入程序名稱就可以運行,這是為什么?為什么系統(tǒng)的命令直接就可以執(zhí)行我們的程序就必須要加一個./,我們直到不管是什么程序,系統(tǒng)要執(zhí)行,就必須找到它,我們的執(zhí)行不了而系統(tǒng)的可以執(zhí)行只有一個原因,就是系統(tǒng)中一定存在默認的搜索路徑。
這個默認的搜索路徑就放在一個環(huán)境變量PATH中。Linux中可以使用$環(huán)境變量來查看環(huán)境變量中的內(nèi)容。
所以PATH環(huán)境變量就是存放系統(tǒng)默認的搜索路徑。只要我們將我們自己的程序的路徑加到這個默認路徑中,我們的進程也可以直接運行。
除了PATH環(huán)境變量外還有HOME,PWD等等很多環(huán)境變量。
其中HOME就是我們的家目錄,PWD就是我們當前所處的目錄。
這也就可以解釋為為什么我們普通用戶登錄后是在/home/xxx,而root是在/root下的原因了。
系統(tǒng)中會存在大量的環(huán)境變量,每一個環(huán)境變量都有它自己特殊的用途,用來完成特定的系統(tǒng)功能。
在Linux中查看所有的環(huán)境變量命令是env。
我們也可以創(chuàng)建環(huán)境變量,在Linux中直接在命令行中使用 環(huán)境變量名=什么 就可以創(chuàng)建一個變量,不過這種變量不是環(huán)境變量叫做本地變量,我們需要用export導(dǎo)出一下才是環(huán)境變量,而且每一次我們重新登錄服務(wù)器時,環(huán)境變量都會被更新,不會保留之前的操作,也就是說,就算我們修改了系統(tǒng)的環(huán)境變量也沒關(guān)系,只需要重新登錄一下云服務(wù)器就好了,那么為什么這樣呢?
我們的命令行啟動的進程都是shell/bash的子進程,子進程的命令行參數(shù)和環(huán)境變量都是父進程給我們傳遞的,而我們更改的是命令行內(nèi)部的環(huán)境變量信息,而我們每一次重新登錄都會給我們都會給我們形成新的bash解釋器并且新的bash解釋器自動讀取新的環(huán)境變量表信息,所以我們可以大膽的推測,父進程的環(huán)境變量是以腳本配置的文件存在的。
而在我們目前的系統(tǒng)中,我們每一次登錄我們的bash都會讀取 .bash_profile文件,文件中的內(nèi)容為我們形成一張環(huán)境變量表信息。
這個文件一般是存在我們的家目錄中的。
我們在外面可以很好的獲取環(huán)境變量,但是編碼如何獲取環(huán)境變量呢?
- 我們可以通過函數(shù)getenv來獲取環(huán)境變量
- main函數(shù)是可以有第三個參數(shù)的,第三個參數(shù)就是環(huán)境變量表
這個環(huán)境變量表最后也是以null結(jié)尾的。
- C語言是有一個全局變量指向環(huán)境變量表的
我們可以直接使用envrion來獲取環(huán)境變量。
環(huán)境變量通常具有全局屬性,可以被子進程繼承下去。
如果我們把PATH清空,我們會發(fā)現(xiàn)大部分命令都跑不了,但是還有一部分命令是可以跑的。
我們知道Linux是又C/C++寫的,Linux中大部分命令都是子進程,但是也有一部分是Shell的一個函數(shù)。
所以Linux的命令就分為兩種:
- 常規(guī)命令,Shell使用fork讓子進程去執(zhí)行的
- 內(nèi)建命令,Shell命令行的一個函數(shù)
本地變量VS環(huán)境變量
本地變量只在bash內(nèi)部有效,不會被子進程繼承下去,而環(huán)境變量通過讓所有子進程繼承的方式實現(xiàn)自身的全局屬性。
進程地址空間
對于我們C/C++程序員來說,內(nèi)存空間一般分為如下幾個區(qū)域:
其中堆和棧中間有一塊很大的區(qū)域,堆是向上增長的,而棧是向下增長的,堆棧相對而生。我們創(chuàng)建的變量都是指向低地址的起始位置,變量的本質(zhì)就是起始地址+偏移量的訪問形式。那么這個東西就是我們平時在系統(tǒng)層面說的內(nèi)存嗎?我們可以先來看一個代碼
#include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 int val = 10;
5 int main()
6 {
7 pid_t id = fork();
8
9 if(id == 0)
10 {
11 //子進程
12 int cnt = 5;
13 while(1)
14 {
15 if(cnt--==0)
16 {
17 val = 5;
18 }
19 printf("i am child, i am pid %d, val = %d, &val = %p\n",getpid(),val,&val);
20 sleep(1);
21 }
22 }
23 else{
24 while(1)
25 {
26 printf("i am parent, i am pid %d, val = %d, &val = %p\n",getpid(),val,&val);
27 sleep(1);
28 }
29 }
30 return 0;
31 }
我們可以看到在5秒過后,父進程和子進程的val地址一樣,但是里面的內(nèi)容確不一樣,那么就只有一個原因,就是他們一定不是物理內(nèi)存。我們平時寫代碼用到的地址都是虛擬地址/線性地址。所以說上面的那個圖不是內(nèi)存,是進程地址空間!
在進程地址空間和物理內(nèi)存中間存在一個叫頁表的東西,而頁表就是虛擬地址對應(yīng)映射的物理地址,也就是說,我們每一個運行起來的進程都會有一個進程地址空間,并且都要在系統(tǒng)層面有自己的頁表映射結(jié)構(gòu)。
當我們的父進程創(chuàng)建子進程是,把它的進程地址空間和頁表都給子進程拷貝一份,然后他們遵循寫時拷貝的原則,當需要修改時,由于進程間是存在獨立性的,所以子進程修改不能影響父進程,所以它就要重新再物理內(nèi)存中開一塊空間,然后修改自己的頁表映射,所以才會存在同一塊地址但是里面的東西是不一樣的,本質(zhì)就是他們映射的物理內(nèi)存是不一樣的。
地址空間每一個進程都有,所以O(shè)S也要對地址空間進行管理,所以地址空間就是一個內(nèi)核的數(shù)據(jù)結(jié)構(gòu)對象,就是一個內(nèi)核結(jié)構(gòu)體,里面需要對各個區(qū)域進行劃分,我們每一個進程都可以使用00000000~FFFFFFFF這么大的內(nèi)存,但是每個進程都不可能使用這么多的內(nèi)存,因為就算你能用的了OS也不會允許,所以每一地址空間的內(nèi)核數(shù)據(jù)結(jié)構(gòu)中都是一些XXX_start,XXX_end的一些字段,表示一個區(qū)域的范圍,如果某個需要增加,就修改字段就可以了。在Linux中這個虛擬地址空間就是叫做 struct mm_struct{}.
頁表結(jié)構(gòu)除了有虛擬地址->物理地址的映射外,在頁表的后面還會存在兩個字段,一個是該區(qū)域的訪問權(quán)限字段,還有一個是映射的物理地址是否進行了空間分配&&是否存在內(nèi)容,我們知道對于代碼段和字符常量去的數(shù)據(jù)是不可以修改的,所以有人惡意修改的話,OS在頁表層面就把它阻止了,所以我們平時對字符常量沒法修改,本質(zhì)還是它在頁表映射中訪問權(quán)限為只讀,而第二個字段就是如果我們進程在阻塞的時候,OS可能會把我們的代碼和數(shù)據(jù)暫時移除內(nèi)存,所以我們在訪問前要先看數(shù)據(jù)是否在內(nèi)存中,如果不在內(nèi)存中,OS就需要為我們開辟物理內(nèi)存,把數(shù)據(jù)拷貝進來,然后進行頁表映射,然后才能正常的執(zhí)行我們的代碼,該過程也叫做缺頁中斷。
CPU中是存在一個寄存器CR3存的是頁表的地址(物理地址).
為什么存在進程地址空間呢?
- 讓進程以統(tǒng)一的視角看待內(nèi)存,所以任意一個進程,可以通過地址空間+頁表將亂序的內(nèi)存數(shù)據(jù)變?yōu)橛行?,分門別類規(guī)劃好。
- 存在虛擬地址空間和頁表,可以很好的進行訪問內(nèi)存的安全檢查。
- 通過頁表,進程管理只管頁表左邊的一部分,不管右邊的,右邊的由操作系統(tǒng)的內(nèi)存管理來完成,所以可以很好的將進程管理和內(nèi)存管理解耦。而且通過頁表可以讓相同地址的內(nèi)容映射到不同的物理地址中,實現(xiàn)進程的獨立性。
寫時拷貝文章來源:http://www.zghlxwxcb.cn/news/detail-802151.html
我們知道創(chuàng)建子進程后,子進程和父進程數(shù)據(jù)和代碼都是共享的,當有一方要修改時要發(fā)生寫時拷貝,那么這個寫時拷貝OS是什么時候做的呢?
其實在我們創(chuàng)建子進程時,代碼和數(shù)據(jù)父進程和子進程都是共享的,并且他們在頁表中的權(quán)限也被設(shè)置了為只讀,所以當OS要進行寫入時,會對它進程檢查,如果是數(shù)據(jù)區(qū)是可以寫入的,那么就寫時拷貝,如果是代碼區(qū),本身就是只讀的,就報錯。所以當頁表因為權(quán)限問題出錯時,有可能是真的錯了,也有可能不是出錯,而是觸發(fā)寫時拷貝機制。文章來源地址http://www.zghlxwxcb.cn/news/detail-802151.html
到了這里,關(guān)于命令行參數(shù)環(huán)境變量和進程空間地址的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!