文件和進(jìn)程的故事,。 成為更好的軟件工程師 Unix很美,。 請(qǐng)?jiān)试S我為您畫一些快樂(lè)的小樹(shù),。 我不會(huì)解釋很多命令,,這很無(wú)聊,,網(wǎng)絡(luò)上已經(jīng)有上百萬(wàn)的教程可以做到這一點(diǎn)。 我將讓您能夠?qū)ο到y(tǒng)進(jìn)行推理,。 您想做的每件奇特的事都只需一個(gè)Google搜索即可,。 但是,理解為什么解決方案可以實(shí)現(xiàn)您想要的功能并不相同,。這就是給您真正的力量,,而您不用害怕的力量。[1] 并且,,因?yàn)樗喉?,所以它必須是真?shí)的。 假設(shè)您是從頭開(kāi)始,,我將只放足夠的命令供我們使用,。 我們將探索概念,在外殼中實(shí)踐中查看它們,,然后尖叫:'我明白了,!'。 在此過(guò)程中,,我們還將弄清楚shell到底是什么,。 但是,我們必須首先進(jìn)入創(chuàng)造者的思想:探索Unix的哲學(xué),。 現(xiàn)在,,我們可以假設(shè)Linux是Unix。 如果您想知道為什么情況并非如此,,則可以跳到底部再返回,。 我們將一勞永逸地結(jié)束Unix與Linux的混淆。 哲學(xué)讓我們從核心開(kāi)始-Unix背后的哲學(xué),。 · 編寫可以做一件事并且做得很好的程序,。 · 編寫程序以協(xié)同工作,。 (沒(méi)有額外的輸出,,不要堅(jiān)持使用交互式輸入,。) · 編寫程序來(lái)處理文本流,因?yàn)檫@是一個(gè)通用接口,。 Unix也接受了越差越好的哲學(xué),。 這種想法是有力的。 在更高的層次上,,我們?cè)诤瘮?shù)式編程中看到了很多東西:構(gòu)建只關(guān)注一件事的原子函數(shù),,無(wú)需額外的輸出,然后將它們組合在一起以完成復(fù)雜的事情,。 組成中的所有功能都是純函數(shù),。 沒(méi)有全局變量可以跟蹤。 也許是直接的結(jié)果,,Unix的設(shè)計(jì)側(cè)重于兩個(gè)主要組件:進(jìn)程和文件,。 Unix中的所有內(nèi)容都是進(jìn)程或文件。 沒(méi)有其他的,。 進(jìn)程和文件之間存在周期性的依存關(guān)系-如果我們開(kāi)始深入解釋其中一個(gè),,我們將需要另一個(gè)來(lái)支持它。 為了打破這個(gè)循環(huán),,我們將先簡(jiǎn)要概述每個(gè)內(nèi)容,,然后再深入研究。我們將重復(fù)幾次以達(dá)到最低要求,。 進(jìn)程您正在運(yùn)行的瀏覽器是一個(gè)進(jìn)程,。 如果您將其打開(kāi),那么終端也是如此,。[2] 如果沒(méi)有,,現(xiàn)在是打開(kāi)它的好時(shí)機(jī)。 如果您使用Windows,,那么Docker也可以很好地工作,。 如果您使用的是Mac,請(qǐng)使用終端-這是Unix環(huán)境,。 用更抽象的術(shù)語(yǔ)來(lái)說(shuō),,流程是運(yùn)行中的代碼實(shí)例。 操作系統(tǒng)將資源分配給進(jìn)程(例如內(nèi)存),,然后將一些元數(shù)據(jù)附加到進(jìn)程(例如誰(shuí)是所有者),,然后運(yùn)行代碼。 作為資源的一部分,,操作系統(tǒng)還為每個(gè)進(jìn)程提供了三個(gè)打開(kāi)文件:stdin,,stdout和stderr。 文件不是進(jìn)程的所有內(nèi)容都是文件,。 是的,,這意味著您的打印機(jī),掃描儀,,終端屏幕以及任何過(guò)程的代碼,! 它們都是文件。 如果這聽(tīng)起來(lái)令人困惑,,請(qǐng)繼續(xù)閱讀,。 我們將清除此問(wèn)題,。 文件系統(tǒng)中的文件就是文件—一串字節(jié)串在一起,以創(chuàng)建有意義的東西,。 您的照片是文件,。 您的目錄也是文件! 它們只包含當(dāng)前目錄中存在的文件/目錄的列表,,就像樹(shù)一樣,。 這樣做的好處是我可以'打開(kāi)'目錄文件來(lái)查看內(nèi)容! 例如: $ vim .' ==================================================================== ' Netrw Directory Listing (netrw v162) ' /Users/Neil/examples ' Sorted by size ' Quick Help: <F1>:help -:go up dir D:delete R:rename s:sort-by x:special ' =================================================================== ../ ./ git-example/ unix-git/ unix-file-system-example/ 我用Vim打開(kāi)了一個(gè)名為..的文件,,聽(tīng)起來(lái)很熟悉嗎,? Unix如何存儲(chǔ)當(dāng)前目錄。 如您所見(jiàn),,它包含當(dāng)前目錄中的文件/目錄列表,。 文件只是數(shù)據(jù)流。 文件和文件系統(tǒng)有了一切的想法,,文件就是文件,,數(shù)據(jù)就成了數(shù)據(jù)流,我們可以探索事情如何進(jìn)一步發(fā)展,。 在Unix系統(tǒng)上,,用于獲取輸入和寫入輸出的流是預(yù)定義的。 這正是標(biāo)準(zhǔn)輸入stdin,,標(biāo)準(zhǔn)輸出stdout和標(biāo)準(zhǔn)錯(cuò)誤stderr的用途,。 · stdin是輸入數(shù)據(jù)源。 · stdout是輸出數(shù)據(jù)源,。 · stderr是標(biāo)準(zhǔn)錯(cuò)誤輸出源,。 在shell [3]中,stdin是來(lái)自鍵盤的輸入數(shù)據(jù),,而stdout和stderr都是屏幕,。[4] 現(xiàn)在,我們可以將這些流重定向到其他地方,,而我們的程序不必知道,! 無(wú)論輸入來(lái)自何處(鍵盤或文本文件),對(duì)于任何正在運(yùn)行的進(jìn)程,,輸入均來(lái)自標(biāo)準(zhǔn)輸入,。 同樣,對(duì)于stdout和stderr,。 當(dāng)我們討論與這些流一起使用的流程時(shí),,我們將詳細(xì)討論這一點(diǎn)。 索引節(jié)點(diǎn)要安裝文件系統(tǒng),您需要一種結(jié)構(gòu)來(lái)管理文件系統(tǒng),。 文件中不僅要處理數(shù)據(jù),,還涉及有關(guān)數(shù)據(jù)本身的信息,稱為元數(shù)據(jù),。 這包括數(shù)據(jù)的存儲(chǔ)位置,,擁有者和查看者。 這就是inode-文件元數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),。 每個(gè)文件都有一個(gè)唯一的索引節(jié)點(diǎn)號(hào)。 當(dāng)它存在時(shí),,它將成為文件的唯一標(biāo)識(shí)符,。 $ ls -li 在第一欄中看到那些數(shù)字? 這些是inode,![5] 索引節(jié)點(diǎn)存儲(chǔ)所有元數(shù)據(jù),。 stat對(duì)于查看此元數(shù)據(jù)也很有用。 $ stat -LF . A和B是用戶名和組名,。 Unix是一個(gè)多用戶系統(tǒng),。 Unix就是這樣-用戶和組是文件的屬性。 用戶屬性設(shè)置為X的文件表示X擁有該文件,。 這就是使用Unix的用戶,。 224是文件大小,或文件中的字節(jié)數(shù),。 2018年10月28日7:15:48是最后修改的日期,。[6] 我從哪里獲得所有這些信息? 男子ls,。 現(xiàn)在來(lái)看看我遺漏的有趣的數(shù)字和字符:drwxrwxrwx和7,。 文件權(quán)限每個(gè)文件都具有與其關(guān)聯(lián)的權(quán)限。 還記得與文件關(guān)聯(lián)的用戶和組嗎,? 每個(gè)文件都存儲(chǔ)誰(shuí)擁有該文件以及該文件屬于哪個(gè)組,。 同樣,每個(gè)用戶也都有一個(gè)用戶名和組,。 進(jìn)入-rwxrwxrwx字符串:這是所有者,,組和其他人的權(quán)限。 · r用于讀,。 · w是寫,。 · x用于執(zhí)行。 對(duì)于目錄,,這意味著可以搜索,。 您只需要三個(gè)位來(lái)代表每個(gè)用戶,組和其他用戶的權(quán)限。 您會(huì)注意到該字符串有10個(gè)字符,。 第一個(gè)是特殊的輸入類型字符,,用于區(qū)分目錄,符號(hào)鏈接,,字符流(stdin)和其他一些字符,。 我想知道更多。 $ stat -LF 如果您想更改權(quán)限怎么辦,? 說(shuō),,我不要任何人搜索我的個(gè)人文件夾(哎呀)。 Unix的創(chuàng)建者對(duì)此進(jìn)行了思考,。 有一個(gè)名為chmod的實(shí)用程序,,可以修改文件的權(quán)限。 在后端,,您現(xiàn)在知道chmod正在與文件的inode交互,。 由于我們需要三個(gè)位來(lái)表示每個(gè)權(quán)限,因此我們可以將其轉(zhuǎn)換為整數(shù)并將其傳遞給chmod,。 例如:chmod 721,。 表示rwx-w — x表示所有者的所有權(quán)限,對(duì)該組的寫權(quán)限以及對(duì)其他人的執(zhí)行權(quán)限,。 我更喜歡詳細(xì)形式: $ chmod u+rwx . # enable user for rwx 您在這里做的完全一樣,。 要為所有人設(shè)置權(quán)限,chmod a + x 更加簡(jiǎn)單,! 您也可以使用-代替+刪除權(quán)限,。 為了限制對(duì)我個(gè)人文件夾的訪問(wèn),我要做:chmod og-x none-interesting-here /,。 您還可以限制對(duì)自己的訪問(wèn),,從而刪除自己的所有讀取,寫入和執(zhí)行權(quán)限,。 如果文件元數(shù)據(jù)存儲(chǔ)在文件本身中,,則您將無(wú)法再次更改權(quán)限(因?yàn)槟鸁o(wú)法寫入文件)。 這就是inode很酷的另一個(gè)原因:它們總是可以由文件所有者和root修改,,因此您可以恢復(fù)權(quán)限,。 嘗試這樣做。 文件連結(jié)有沒(méi)有想過(guò)為什么將千兆字節(jié)文件從一個(gè)目錄移動(dòng)到另一個(gè)目錄會(huì)很快,,而復(fù)制相同文件卻要花一些時(shí)間呢,? 你能猜出現(xiàn)在為什么嗎? 這是因?yàn)楫?dāng)我們進(jìn)行MV時(shí),,我們將移動(dòng)目錄結(jié)構(gòu),,而不是實(shí)際的文件數(shù)據(jù),。 索引節(jié)點(diǎn)是文件系統(tǒng)上非常有用的抽象。 我們可以采取其他行動(dòng),。 我們可以將文件從一個(gè)位置鏈接到另一位置,,或使兩個(gè)文件名指向同一文件。 指向同一文件的兩個(gè)文件名是硬鏈接,。 將它們視為文件的別名,。 您已經(jīng)看到了兩個(gè)硬鏈接:。 和..是到系統(tǒng)中當(dāng)前目錄和父目錄的硬鏈接,。 從一個(gè)地方到另一個(gè)地方的鏈接是符號(hào)鏈接,。 符號(hào)鏈接是與原始文件分開(kāi)的新文件,該文件鏈接到原始文件,。 當(dāng)您要修復(fù)需要在新環(huán)境中運(yùn)行的腳本或制作文件副本,,以滿足希望該文件位于另一個(gè)位置的新程序的安裝要求時(shí),這些選項(xiàng)很有用,。 $ ls -litotal 025280489 -rw-r--r-- 1 neil X 0 8 Dec 08:48 a$ man ln # to check syntax for hard links$ ln a x # create x as a hard link to a$ ls -litotal 025280489 -rw-r--r-- 2 neil X 0 8 Dec 08:48 a25280489 -rw-r--r-- 2 neil X 0 8 Dec 08:48 x# Notice both files have the same inode number.# Modifying x or a is the same thing - both files get modified together.$ ln -s a y # create symbolic link to a$ ls -litotal 025280489 -rw-r--r-- 2 neil X 0 8 Dec 08:48 a25280489 -rw-r--r-- 2 neil X 0 8 Dec 08:48 x25280699 lrwxr-xr-x 1 neil X 1 8 Dec 08:54 y -> a# y is a symbolic link, a new small file - see size is 1. $ cat y # shows that y (a) is empty$ echo lsd >> y$ cat ylsd$ cat a # modifying y modifies alsd$ cat x # a is xlsd 我已經(jīng)在評(píng)論中解釋了上面的情況。 現(xiàn)在,,如果刪除y指向的文件,,該怎么辦?
這是一個(gè)懸掛的符號(hào)鏈接,。 沒(méi)用的,。 讀寫許可權(quán)后的數(shù)字,即執(zhí)行stat -LF時(shí)的7,。 是文件的硬鏈接數(shù),。 當(dāng)我創(chuàng)建x時(shí),數(shù)字增加到兩個(gè),。 當(dāng)我刪除a時(shí),,數(shù)字回落到1。 我們也可以確認(rèn),。 和..確實(shí)是硬鏈接,。 你能怎么想? $ ls -ail25280488 drwxr-xr-x 7 neil X 224 9 Dec 20:19 . 1289985 drwxr-xr-x+ 83 neil X 2656 10 Dec 08:13 ..25390377 drwxr-xr-x 5 neil X 160 9 Dec 19:13 sample_dir$ cd sample_dir$ ls -ail25390377 drwxr-xr-x 5 neil X 160 9 Dec 19:13 .25280488 drwxr-xr-x 7 neil X 224 9 Dec 20:19 ..25390378 -rw-r--r-- 1 neil X 0 9 Dec 19:13 a 】' 檢查索引節(jié)點(diǎn)號(hào),。 ..在sample_dir中是25280488,,與..相同 在父目錄中。 另外,,父目錄中的sample_dir是25390377,,與相同。 在sample_dir中,。 文件結(jié)構(gòu)它可以幫助我想象文件系統(tǒng)像樹(shù)形數(shù)據(jù)結(jié)構(gòu)(實(shí)際上就是這樣),。 每個(gè)節(jié)點(diǎn)(索引節(jié)點(diǎn))都有一個(gè)指向其父節(jié)點(diǎn),自身及其所有子節(jié)點(diǎn)的指針。 這形成目錄結(jié)構(gòu),。 /根目錄的父目錄是什么,? 您已有足夠的知識(shí)來(lái)回答這個(gè)問(wèn)題。 我做的第一件事是vim /看看是否有一個(gè)父指針,。 是的 然后,,我做了ls -ail來(lái)查看父級(jí)的索引節(jié)點(diǎn)。 它指向,。,,即/。 綜上所述,, · 文件系統(tǒng)是使用inode和目錄文件構(gòu)建的,。 · 用戶是文件和進(jìn)程的屬性。 此信息存儲(chǔ)在inode中,。 · 索引節(jié)點(diǎn)在文件系統(tǒng)中是唯一的,。 · 可以掛載多個(gè)文件系統(tǒng)并將其抽象為一個(gè)邏輯樹(shù)。 進(jìn)程首先,,讓我們擺脫這些定義,。 關(guān)于進(jìn)程,要記住三個(gè)組件: · 程序文件:代碼和數(shù)據(jù),。 · 過(guò)程映像:這將存儲(chǔ)堆棧,,當(dāng)前定義的變量,數(shù)據(jù),,地址空間等,。 在運(yùn)行時(shí),操作系統(tǒng)完全知道如何使用此映像重新創(chuàng)建過(guò)程,。 · 進(jìn)程:內(nèi)存中正在運(yùn)行的程序,。 進(jìn)程開(kāi)始運(yùn)行時(shí),它將從父進(jìn)程繼承用戶ID和組ID,。 此信息控制對(duì)該進(jìn)程的訪問(wèn)級(jí)別,。 注意:訪問(wèn)控制對(duì)于安全系統(tǒng)至關(guān)重要。 這就是為什么在生產(chǎn)環(huán)境中運(yùn)行裸Docker容器可能會(huì)出現(xiàn)這樣的問(wèn)題的原因之一:它需要以root身份運(yùn)行,,這意味著可能會(huì)發(fā)生不好的事情,。 我們可以使用setuid或setgid來(lái)使進(jìn)程繼承文件所有者權(quán)限。 setuid允許進(jìn)程繼承相關(guān)文件的用戶ID,。 例如,,要在Linux上更改密碼(請(qǐng)參閱Mac的此鏈接)-我們需要修改文件/ etc / passwd。 但是,,在檢查權(quán)限時(shí),,我們看到只有root有權(quán)訪問(wèn)此文件,。[7]
因此,當(dāng)我們調(diào)用幫助更改密碼的實(shí)用程序/ usr / bin / passwd時(shí),,它將繼承我們的用戶ID,,該用戶ID將導(dǎo)致對(duì)/ etc / passwd的訪問(wèn)被拒絕。 這是setuid有用的地方-它允許我們以root身份啟動(dòng)usr / bin / passwd,。
執(zhí)行權(quán)限中的s而不是x表示此過(guò)程將以root用戶身份運(yùn)行,。 要設(shè)置和刪除該位,我們可以再次使用chmod,。
我是在Docker上完成的,,因此我真正的文件系統(tǒng)是安全的。 屬性就像文件系統(tǒng)上的所有文件都具有唯一的索引節(jié)點(diǎn)一樣,,進(jìn)程也具有稱為進(jìn)程ID或pid的唯一標(biāo)識(shí)符,。 就像所有文件都具有指向其父目錄的鏈接一樣,每個(gè)進(jìn)程都具有指向生成它的父進(jìn)程的鏈接,。 就像文件系統(tǒng)根的存在方式(/)一樣,,有一個(gè)特殊的根父進(jìn)程稱為init。 它通常具有pid 1,。 與父目錄本身為(/)的文件系統(tǒng)的根目錄不同,,init的ppid為0,這通常意味著它沒(méi)有父目錄,。 pid 0對(duì)應(yīng)于內(nèi)核調(diào)度程序,它不是用戶進(jìn)程,。 生命周期在Unix中,,有一個(gè)常見(jiàn)的模式說(shuō)明進(jìn)程如何工作。 通過(guò)克隆現(xiàn)有的父進(jìn)程(fork()),,可以創(chuàng)建一個(gè)新的子進(jìn)程,。 這個(gè)新的子進(jìn)程調(diào)用(exec())將子進(jìn)程中運(yùn)行的父進(jìn)程替換為該子進(jìn)程想要運(yùn)行的進(jìn)程。 接下來(lái),,子進(jìn)程調(diào)用exit()終止自身,。 它只會(huì)傳遞退出代碼。 0表示成功,,其他所有都是錯(cuò)誤代碼,。 父進(jìn)程需要調(diào)用wait()系統(tǒng)調(diào)用才能訪問(wèn)此退出代碼。 對(duì)于產(chǎn)生的每個(gè)過(guò)程都重復(fù)此循環(huán),。 這里有些事情可能會(huì)出錯(cuò),。 如果父母不致電wait()怎么辦? 這會(huì)導(dǎo)致僵尸進(jìn)程,,這是資源泄漏,,因?yàn)椴僮飨到y(tǒng)無(wú)法在父進(jìn)程消耗退出代碼之前清理進(jìn)程,。 如果父母在子進(jìn)程之前去世怎么辦? 這將導(dǎo)致一個(gè)孤立的過(guò)程(我保證我不會(huì)對(duì)此進(jìn)行彌補(bǔ)),。 初始化進(jìn)程(特殊的根父進(jìn)程)采用了孤立進(jìn)程,,然后等待子進(jìn)程完成。 在計(jì)算機(jī)世界的自然秩序中,,孩子死于父母之前,。 父母如何從孩子那里獲得更多信息? 它不能通過(guò)退出代碼,,因?yàn)檫@是進(jìn)程唯一可以返回到父級(jí)的東西,。 流程與常規(guī)函數(shù)不同,在常規(guī)函數(shù)中,,您只需將響應(yīng)返回給調(diào)用函數(shù)即可,。 但是,還有其他方法可以進(jìn)行進(jìn)程間通信 我們將通過(guò)一個(gè)示例來(lái)詳細(xì)介紹事物的工作方式,。 在此之前,,我們需要更多信息。 文件重定向還記得操作系統(tǒng)如何為每個(gè)正在運(yùn)行的進(jìn)程提供三個(gè)打開(kāi)的文件嗎,? 我們有權(quán)將這些文件重定向到我們想要的任何文件,。 重定向stdout,2>重定向stderr,,和<重定向stdin,。 例如,someBinary 2>&1將stderr重定向到stdout,。 0,、1和2分別是stdin,stdout和stderr文件的簡(jiǎn)寫,。 注意:./someBinary 2> 1不會(huì)像您期望的那樣工作,,因?yàn)檎Z(yǔ)法是file-descriptor> file。 2> 1表示stderr將被重定向到名為1的文件,。&運(yùn)算符提供文件中的文件描述符,。 文件重定向發(fā)生在命令運(yùn)行之前。 操作系統(tǒng)打開(kāi)新文件(通過(guò)>)時(shí),,它已經(jīng)刪除了這些文件中的所有內(nèi)容,。 因此,無(wú)法對(duì)res.txt> res.txt進(jìn)行排序,。
提示:通過(guò)在外殼程序中設(shè)置noclobber選項(xiàng),,可以確保沒(méi)有任何重定向破壞現(xiàn)有文件。
但是,,它將與>>一起使用,,因?yàn)樵谶@種情況下,,您將附加到文件中。
閱讀有關(guān)重定向的更多信息,。 Unix中的層我們可以將Unix視為洋蔥,。 核心是硬件-主板,CPU和許多我不太了解的晶體管,。 一層是內(nèi)核,。 內(nèi)核內(nèi)核是負(fù)責(zé)與文件系統(tǒng)和設(shè)備進(jìn)行交互的核心。 它還處理流程調(diào)度,,任務(wù)執(zhí)行,,內(nèi)存管理和訪問(wèn)控制。 內(nèi)核公開(kāi)了API調(diào)用,,以構(gòu)建任何可以利用的東西,。 最受歡迎的是exec(),fork()和wait(),。 Unix實(shí)用程序另一層是Unix實(shí)用程序,。 這些是超級(jí)有用的進(jìn)程,可以幫助我們與內(nèi)核進(jìn)行交互,。 他們通過(guò)內(nèi)核提供的exec()和fork()之類的系統(tǒng)調(diào)用來(lái)執(zhí)行此操作,。 您可能已經(jīng)聽(tīng)說(shuō)過(guò)許多實(shí)用程序。 您可能使用了最著名的一種:Shell,。 其他包括:python,,gcc,vi,,sh,,ls,cp,,mv,,cat,,awk,。 您可以從shell調(diào)用其中的大多數(shù)。 bash,,zsh,,ksh只是shell的不同變體。 他們做同樣的事情,。 人們發(fā)現(xiàn)令人望而生畏的另一個(gè)實(shí)用工具是文本編輯器Vim,。 Vim值得擁有自己的職位,這就是我在這里創(chuàng)建的 有趣的事實(shí):Shell之所以稱為Shell,,是因?yàn)樗莾?nèi)核之外最近的一層,。 它在保護(hù)性…外殼中覆蓋了內(nèi)核,。 Shell如何工作還記得shell是一個(gè)過(guò)程嗎? 這意味著操作系統(tǒng)啟動(dòng)時(shí)會(huì)提供三個(gè)文件供其使用:stdin,,stdout和stderr,。 從終端運(yùn)行時(shí),stdin連接到鍵盤輸入,。 您編寫的內(nèi)容將傳遞到終端中,。 這是通過(guò)稱為電話打字機(jī)或tty的文件發(fā)生的。 stdout和stderr也連接到tty,,這就是為什么您運(yùn)行的任何命令的輸出和錯(cuò)誤都顯示在終端上的原因,。 您打開(kāi)的每個(gè)終端都會(huì)通過(guò)tty分配一個(gè)新文件,以使來(lái)自一個(gè)終端的命令不會(huì)破壞另一個(gè)終端,。 您可以通過(guò)tty命令找到終端連接到的文件,。
現(xiàn)在,您可以做一些時(shí)髦的事情:由于Shell從該文件讀取,,因此您也可以讓另一個(gè)Shell寫入該文件,,或者將Shell一起破壞。 我們?cè)囋嚢伞?(還記得如何從上面的過(guò)程部分重定向文件嗎,?) 打開(kāi)第二個(gè)終端,。 輸入:
注意在第一個(gè)終端中發(fā)生的事情。 嘗試回顯ls,,這次是列出文件的命令,。 為什么第一個(gè)終端不運(yùn)行命令? 它不會(huì)運(yùn)行該命令,,因?yàn)閷懭虢K端的流是第二終端的標(biāo)準(zhǔn)輸出,,而不是第一終端的stdin流。 請(qǐng)記住,,只有通過(guò)stdin輸入的輸入才作為輸入傳遞到shell,。 其他所有內(nèi)容僅顯示在屏幕上。 在這種情況下,,即使碰巧是同一文件,,也無(wú)需擔(dān)心該過(guò)程。 上面內(nèi)容的自然擴(kuò)展是,,當(dāng)您重定向標(biāo)準(zhǔn)輸入時(shí),,命令應(yīng)該運(yùn)行。 聽(tīng)起來(lái)很合理,,請(qǐng)嘗試一下,。 警告:執(zhí)行此操作的一種方法是bash </ dev / ttys001。 這不太好用,,因?yàn)楝F(xiàn)在有兩個(gè)進(jìn)程希望從該文件輸入信息,。 這是一個(gè)未定義的狀態(tài),,但是在我的Mac上,一個(gè)字符轉(zhuǎn)到一個(gè)終端,,另一個(gè)字符轉(zhuǎn)到第二個(gè)終端,,然后繼續(xù)。 這很有趣,,因?yàn)橐顺鲂峦鈿?,我必須鍵入eexxiitt。 然后我丟了兩個(gè)炮彈,。 $ echo ls > ls.txt # write 'ls' to a file$ cat ls.txt # check what's in filels$ bash < ls.txt ApplicationsMusicDocumentsDownloads 有一種更好的方法可以做到這一點(diǎn),,我們將在稍后介紹。 這里有些微妙的事情,。 這個(gè)新的bash流程(我們從現(xiàn)有的bash流程開(kāi)始)如何知道在哪里輸出內(nèi)容,? 我們從不指定輸出流,僅指定輸入流,。 發(fā)生這種情況是因?yàn)檫M(jìn)程從其父進(jìn)程繼承,。 每次在終端上編寫命令時(shí),shell都會(huì)創(chuàng)建一個(gè)重復(fù)進(jìn)程(通過(guò)fork()),。 從man2叉: 子進(jìn)程具有其父描述符的副本,。 這些描述符引用相同的基礎(chǔ)對(duì)象,例如,,子對(duì)象和父對(duì)象之間共享文件對(duì)象中的文件指針,,以便子進(jìn)程中的描述符上的lseek(2)可以影響后續(xù)的讀寫操作。 父母,。 Shell還使用此描述符復(fù)制為新創(chuàng)建的進(jìn)程建立標(biāo)準(zhǔn)輸入和輸出,,以及建立管道。 分叉后,,這個(gè)新的子進(jìn)程將從父級(jí)繼承文件描述符,,然后調(diào)用exec(execve())執(zhí)行命令。 這將替換過(guò)程映像,。 來(lái)自man 3 exec: 本手冊(cè)頁(yè)中描述的功能是功能execve(2)的前端,。 來(lái)自man 2 execve [8]: 在調(diào)用過(guò)程映像中打開(kāi)的文件描述符在新過(guò)程映像中保持打開(kāi)狀態(tài),但已設(shè)置close-on-exec標(biāo)志的文件描述符除外 因此,,我們的文件描述符與原始bash進(jìn)程相同,,除非我們通過(guò)重定向?qū)ζ溥M(jìn)行更改,。 在執(zhí)行此子進(jìn)程時(shí),,父進(jìn)程等待子進(jìn)程完成。 發(fā)生這種情況時(shí),,控制權(quán)將返回給父進(jìn)程,。 請(qǐng)記住,,子進(jìn)程不是bash,而是代替bash的進(jìn)程,。 使用ls,,該過(guò)程將文件列表輸出到stdout后立即返回。 注意:并非Shell上的所有命令都會(huì)產(chǎn)生fork和exec,。 那些不被稱為內(nèi)置命令的命令,。 有些是出于必要內(nèi)置的。 由于子進(jìn)程無(wú)法將信息傳遞回父母,,因此其他進(jìn)程可以使事情更快,。 例如,設(shè)置環(huán)境變量在子Shell中不起作用,,它無(wú)法將值傳遞回父Shell,。 您可以在此處找到列表。 這是我喜歡的示范,。 您是否曾經(jīng)想過(guò),,當(dāng)某事正在運(yùn)行并將東西輸出到終端時(shí),您可以編寫下一條命令并在現(xiàn)有過(guò)程完成后立即使它們起作用,,這有多奇怪,?
只是被阻止的進(jìn)程,輸入流仍在接受數(shù)據(jù),。 由于我們要讀取/寫入的文件是相同的(tty),,因此我們可以看到鍵入的內(nèi)容以及睡眠的時(shí)間10; 返回時(shí),,shell為ls創(chuàng)建另一個(gè)進(jìn)程,,再次等待,然后對(duì)cat b.txt相同,,然后再次對(duì)brrr,。 我用了10點(diǎn)睡眠; 進(jìn)行演示,,因?yàn)槠渌畎l(fā)生得太快,,我無(wú)法在控件返回到父bash進(jìn)程之前鍵入任何內(nèi)容。 現(xiàn)在是嘗試exec內(nèi)置命令的好時(shí)機(jī)(它將替換當(dāng)前進(jìn)程,,因此它將終止您的shell會(huì)話),。
echo也是內(nèi)置命令。 如果您想用C自己實(shí)現(xiàn)Shell,,我建議您使用以下資源,。 管道掌握了外殼如何工作的知識(shí)后,我們便可以冒險(xiǎn)進(jìn)入管道的世界:|。 它把兩個(gè)過(guò)程聯(lián)系在一起,,并且它的工作方式很有趣,。 還記得我們開(kāi)始時(shí)的哲學(xué)嗎? 做一件事,,并做好,。 現(xiàn)在,我們所有的公用程序都可以正常工作,,我們?nèi)绾问顾鼈円黄鸸ぷ鳎?/p> 這是管道|進(jìn)入管道的地方,。它表示對(duì)pipe()的系統(tǒng)調(diào)用,它所做的只是重定向進(jìn)程的stdin和stdout,。 由于事情的設(shè)計(jì)是如此出色,,因此原本就很復(fù)雜的功能可以簡(jiǎn)化為這一點(diǎn)。 每當(dāng)您使用管道或終端上的任何東西時(shí),,只要想象一下輸入和輸出文件的設(shè)置方式,,就不會(huì)有問(wèn)題。[9] 讓我們從一種更好的方法將輸入直接定向到bash開(kāi)始,,而不是像以前那樣使用臨時(shí)文件(ls.txt),。
該圖有點(diǎn)簡(jiǎn)化了管道的重定向。 您知道外殼現(xiàn)在是如何工作的,,因此您知道頂部bash分叉了另一個(gè)與tty連接的bash,,后者生成了ls的輸出。 您還知道頂部bash是從底部bash派生的,,這就是為什么它繼承了底部bash的文件描述符的原因,。 您還知道較低的bash并沒(méi)有進(jìn)行新的處理,因?yàn)閑cho是內(nèi)置命令,。 讓我們以一個(gè)更復(fù)雜的示例結(jié)束本節(jié):
該管道找出當(dāng)前目錄中最大的文件并輸出其大小,。 這樣做可能是一種更優(yōu)雅的方法,僅需一次Google搜索即可,,但這很好地舉例說(shuō)明了,。 誰(shuí)知道這已經(jīng)內(nèi)置到ls中了? 注意,,stderr是如何始終直接路由到tty的嗎,? 如果要重定向stderr而不是stdout到管道怎么辦? 您可以在管道之前切換流,。
關(guān)于PATH的一切局部變量是可以在shell中創(chuàng)建的變量,。 它們位于外殼的本地,因此不會(huì)傳遞給子級(jí),。 (請(qǐng)記住,,每個(gè)非內(nèi)置命令都在沒(méi)有這些局部變量的新外殼中,。) 環(huán)境變量(環(huán)境變量)類似于全局變量。 他們被傳遞給孩子,。 但是,子進(jìn)程中對(duì)環(huán)境變量的更改無(wú)法傳遞給父進(jìn)程,。 請(qǐng)記住,,除了退出代碼,孩子與父母之間沒(méi)有任何聯(lián)系,。 試試這個(gè):從bash調(diào)用bash,。 第一個(gè)bash正在等待第二個(gè)bash退出,而第二個(gè)bash正在等待第三個(gè)bash,。 當(dāng)您調(diào)用exec時(shí),,退出會(huì)自動(dòng)發(fā)生。 如果不是,,則您要自己鍵入exit,,以將退出代碼發(fā)送給父級(jí)。 退出兩次,,即可恢復(fù)原始狀態(tài),。 現(xiàn)在,進(jìn)行思想實(shí)驗(yàn):當(dāng)將ls輸入到shell中時(shí)會(huì)發(fā)生什么,? 您知道與tty一起發(fā)生的fork(),,exec()和wait()循環(huán)。 但是,,即使在這種情況發(fā)生之前,,ls只是另一個(gè)實(shí)用程序功能,對(duì)嗎,? 這意味著某個(gè)地方有一個(gè)程序文件,,其中包含執(zhí)行fork()和其他所有功能的C代碼。 該二進(jìn)制文件不在當(dāng)前目錄中(您可以使用ls -a檢查),。 對(duì)我來(lái)說(shuō),,如果這些文件在我的當(dāng)前目錄中,我可以通過(guò)在外殼中鍵入它們的名稱來(lái)執(zhí)行它們,。 它們是可執(zhí)行文件,。 ls程序文件在哪里? 還記得文件系統(tǒng)樹(shù)是如何分層的嗎,? 這是瘋狂的命令,。 所有基本級(jí)別目錄都具有特定功能。 例如,,所有Unix實(shí)用程序和一些其他程序都進(jìn)入/ bin目錄,。 Bin代表二進(jìn)制文件,。 如果您想了解更多信息,則有一百萬(wàn)本教程,。 這對(duì)我們來(lái)說(shuō)已經(jīng)足夠了,。 我住在/ bin。 因此,,您可以這樣做:
與運(yùn)行l(wèi)s相同,。 但是,shell如何知道要在bin中查找ls,? 這就是神奇的環(huán)境變量PATH出現(xiàn)的地方,。讓我們首先來(lái)看它。
并且,,要查看所有環(huán)境變量,,我們可以執(zhí)行以下操作:
PATH是用冒號(hào)分隔的目錄列表。 當(dāng)shell看到?jīng)]有絕對(duì)路徑的命令時(shí),,它將在$ PATH環(huán)境變量中查找,,依次進(jìn)入每個(gè)目錄,并嘗試在其中查找文件,。 它執(zhí)行找到的第一個(gè)文件,。 注意/ bin在PATH中的方式,這就是ls起作用的原因,。 如果我從PATH中刪除所有內(nèi)容,,會(huì)發(fā)生什么? 沒(méi)有絕對(duì)的路徑,,任何事情都不應(yīng)該工作,。
上面的語(yǔ)法僅用于設(shè)置一個(gè)命令的環(huán)境變量。 舊的PATH值仍然存在于外殼中,。
注意:由于echo是內(nèi)置的shell,,因此無(wú)法執(zhí)行PATH ='''echo $ PATH。 但是,,如果您使用PATH =''啟動(dòng)了一個(gè)新的shell進(jìn)程,,然后執(zhí)行了echo操作,那么它將起作用,。 PATH) ()是新子shell的語(yǔ)法,。 我知道,我首先不解釋很多信息,,但這全是語(yǔ)法級(jí)別,,僅需一次Google搜索。 從正面來(lái)看,,它可以確保該博客文章不會(huì)變成一本書,。 您是否聽(tīng)說(shuō)過(guò)./是運(yùn)行所創(chuàng)建文件的方式,? 您為什么不能像bash一樣運(yùn)行它們? 現(xiàn)在你知道了,。 當(dāng)您執(zhí)行./時(shí),,這就是您要執(zhí)行的文件的確切路徑。 bash有效,,因?yàn)樗挥赑ATH上,。 因此,有意義的是,,如果當(dāng)前目錄始終位于PATH上,,則腳本將按名稱運(yùn)行,。 讓我們嘗試一下,。 $ vim script.sh# echo 'I can work from everywhere?'$ chmod a+x script.sh $ lsscript.sh$ script.sh-bash: script.sh: command not found # not on PATH$ ./script.sh # path to file is definedI can work from everywhere? 現(xiàn)在,將其添加到當(dāng)前PATH中,。 然后只運(yùn)行script.sh,。
警告:不正確的做法是將當(dāng)前目錄包含在PATH中。 有幾個(gè)問(wèn)題,。 您永遠(yuǎn)不能確保任何命令的執(zhí)行都按預(yù)期進(jìn)行,。 如果您有一個(gè)名為ls的二進(jìn)制文件,它是當(dāng)前目錄中的一種病毒(可從Internet下載),,但您打算執(zhí)行/ bin / ls怎么辦,? 閱讀更多 下次當(dāng)您看到'沒(méi)有這樣的文件或目錄'錯(cuò)誤時(shí),當(dāng)您知道該文件存在(也許您剛剛安裝了該文件)時(shí),,便知道問(wèn)題出在哪里,。 路徑被破壞! 它安裝在PATH以外的位置,,因此只能從安裝它的位置進(jìn)行調(diào)用,。 要解決此問(wèn)題,您現(xiàn)在知道可以將該目錄添加到PATH,,也可以通過(guò)其絕對(duì)路徑調(diào)用該文件,。 有趣的事實(shí):Python在搜索導(dǎo)入時(shí)具有類似的結(jié)構(gòu),該結(jié)構(gòu)使用PYTHONPATH env變量,。 編寫Shell腳本這篇文章的長(zhǎng)度已經(jīng)超出了我的預(yù)期,。 此外,使用Shell進(jìn)行編程已經(jīng)在網(wǎng)上廣泛討論了,。 但是為了完整起見(jiàn),,這里是手冊(cè)的鏈接和不錯(cuò)的教程。 包管理假設(shè)您編寫了一個(gè)新工具,。 它在您的計(jì)算機(jī)上確實(shí)運(yùn)行良好,,現(xiàn)在您想將其出售給其他用戶,。 等等,我的意思是,,本著開(kāi)源的精神,,您想將其提供給他人使用。 您還希望節(jié)省他們的PATH麻煩,。 更妙的是,,您希望將文件安裝在正確的位置:二進(jìn)制文件進(jìn)入/ usr / bin /(它已經(jīng)在PATH中),而依賴項(xiàng)則位于主二進(jìn)制文件可以找到它的地方,。 包管理器正好解決了這個(gè)問(wèn)題,。 他們沒(méi)有讓您頭疼,而是使事情正常進(jìn)行,。 我知道三個(gè)主要的軟件包管理器:dpkg,,rpm和自制軟件。 它們每個(gè)都在不同的Linux發(fā)行版上工作(如果您不確定這是什么意思,,請(qǐng)參見(jiàn)下一節(jié)),。 但是,就像分發(fā)的數(shù)量一樣,,它們中有數(shù)百種在野外,。 dpkg是Debian軟件包管理器,但是您可能已經(jīng)聽(tīng)說(shuō)過(guò)在它之上構(gòu)建的用于管理軟件包的非常有用的工具:apt,。 每次使用apt install來(lái)安裝新軟件包時(shí),,您都在利用此軟件包管理器的功能,以確保一切都在需要的地方進(jìn)行,。 在開(kāi)發(fā)方面,,這意味著確保要?jiǎng)?chuàng)建的工具與程序包管理器兼容。 例如,,以下是在C和Python中執(zhí)行此操作的方法,。 rpm是Red Hat軟件包管理器,它在頂部還有一個(gè)有用的工具:yum,,它也處理依賴項(xiàng),。 homebrew是macOS上的軟件包管理器,每次醞釀安裝某些軟件時(shí)都在使用它,。 它們使生活變得輕松,。 它們是如此方便,以至于編程語(yǔ)言也有自己的軟件包管理器,! 例如,,pip是流行的Python工具安裝程序。 有用于Ruby的捆綁器,,用于Swift / iOS的可可和其他幾種捆綁器,。 Unix的簡(jiǎn)要?dú)v史Unix是第一個(gè)允許多個(gè)用戶使用它的操作系統(tǒng),,每個(gè)用戶可以同時(shí)運(yùn)行多個(gè)程序。 現(xiàn)在這聽(tīng)起來(lái)似乎很瑣碎,,因?yàn)閹缀趺總€(gè)操作系統(tǒng)都具有此功能,,但是它首次問(wèn)世時(shí)是革命性的。 在大型主機(jī)上租賃時(shí)間的日子已經(jīng)過(guò)去,。 您可以讓程序在后臺(tái)運(yùn)行,,而其他人來(lái)做。 顧名思義,,Unix是一個(gè)多用戶多任務(wù)操作系統(tǒng),。 這是專有的,AT&T是唯一可以出售它的公司,。 (貝爾實(shí)驗(yàn)室在1970年代開(kāi)發(fā)了它),。 他們選擇了許可模型,并很快提出了一個(gè)規(guī)范,,稱為'單一UNIX規(guī)范',。 這些是一組準(zhǔn)則,,遵循它們的任何系統(tǒng)都可以被認(rèn)證為Unix系統(tǒng),。 大約在同一時(shí)間,一些人對(duì)Unix的專有性不滿意,,并提出了另一個(gè)名為L(zhǎng)inux的開(kāi)源操作系統(tǒng)內(nèi)核,。 這些系統(tǒng)受到Unix哲學(xué)的啟發(fā)并實(shí)現(xiàn)了可移植性,因此遵循POSIX標(biāo)準(zhǔn),,該標(biāo)準(zhǔn)是Unix的一個(gè)子集,。 這些系統(tǒng)因此也被稱為類Unix。[10] 事情變得有些混亂,。 Linux是基于Linux內(nèi)核的一系列操作系統(tǒng),。 沒(méi)有稱為L(zhǎng)inux的單一操作系統(tǒng)。 相反,,我們所擁有的是Debian,,Ubuntu,F(xiàn)edora,,CentOS,,Red Hat,Gentoo等,。這些是Linux內(nèi)核的發(fā)行版(通常稱為發(fā)行版),。 完善的操作系統(tǒng)。 有什么不同,? 有些是為特定目的而構(gòu)建的(例如:Kali Linux附帶了安全測(cè)試工具),。 包管理,,包更新頻率和安全性方面的大多數(shù)差異。 如果您想了解更多,,請(qǐng)?jiān)L問(wèn)opensource.com,。 有趣的事實(shí):Mac OS X經(jīng)過(guò)Unix認(rèn)證。 結(jié)論我們已經(jīng)介紹了很多,。 讓我們花一點(diǎn)時(shí)間將它們放在一起,。 Unix是成熟的操作系統(tǒng),Linux是受Unix啟發(fā)的內(nèi)核(操作系統(tǒng)的核心),。 他們專注于做一件事情,,并做好。 一切都是過(guò)程或文件,。 內(nèi)核是核心,,它公開(kāi)了系統(tǒng)調(diào)用,這些實(shí)用程序可以利用它們,。 進(jìn)程使用文件作為輸入和輸出,。 我們可以控制這些文件,可以重定向它們,,這對(duì)過(guò)程沒(méi)有影響,。 管道可以將輸出從一個(gè)過(guò)程重定向到另一個(gè)過(guò)程的輸入中。 Shell中的每個(gè)命令首先派生,,然后執(zhí)行,,然后將退出代碼返回給等待的父對(duì)象。 還有很多,。 如果有可能進(jìn)行無(wú)損壓縮,,那么我會(huì)在后期進(jìn)行。 因此,,歡迎來(lái)到野外,,您很好。 出口 感謝Vatika Harlalka,,Nishit Asnani,,Hemanth K. Veeranki和Hung Hoang閱讀了此草稿。 腳注· 這只是一首詩(shī)嗎,? · 終端也像外殼一樣運(yùn)行著子進(jìn)程,。 您可以通過(guò)ps -ef查看所有正在運(yùn)行的進(jìn)程。 · Shell是用于與操作系統(tǒng)交互的接口,。 它既可以是命令行界面(CLI),,也可以是圖形用戶界面(GUI)。 在本文中,我們僅關(guān)注CLI,。 打開(kāi)終端時(shí),,歡迎您的默認(rèn)程序是外殼程序。 閱讀更多 · 這不是100%正確的,,還有更多細(xì)微差別,,我們將盡快解決。 · 我花了太多時(shí)間在iPhone和iOS上,。 它是一個(gè)索引節(jié)點(diǎn),,而不是一個(gè)iNode。 · 另外,,這是我開(kāi)始編寫本指南的時(shí)間,。 大概我完成了。 注意年份,。 · 我從man 5 passwd獲得了所有這些信息,。 · 本手冊(cè)分為八個(gè)部分,每個(gè)部分用于特定目的,。 閱讀更多,。 · 這是一個(gè)強(qiáng)大的想法,只有在Unix設(shè)計(jì)上說(shuō):'一切都是文件',,這才是正確的,。 · 有趣的事實(shí)-由于它們未經(jīng)認(rèn)證,因此不能稱為Unix,,并且Unix是商標(biāo),。 (本文翻譯自Neil Kakkar的文章《How Unix Works: Everything You Were Too Afraid to Ask》,,參考: |
|