做網(wǎng)站需要多少固定帶寬seo人才網(wǎng)
? ? ? ? 前言:本篇講解linux下的重定向相關(guān)內(nèi)容。 在本篇中, 博主將會(huì)帶著友友們一邊實(shí)驗(yàn), 一邊探索底層原理。 通過(guò)本篇的學(xué)習(xí), 友友們將會(huì)了解到重定向是如何實(shí)現(xiàn)的, 重定向的本質(zhì)是什么, 重定向和進(jìn)程替換之間的關(guān)系等等, 本篇內(nèi)容將會(huì)豐富我們對(duì)于進(jìn)程的理解。
? ? ? ? ps:由于本節(jié)內(nèi)容涉及到文件fd,所以本節(jié)內(nèi)容適合了解文件fd的友友們進(jìn)行觀看。?
目錄
文件描述符的分配規(guī)則
重定向的本質(zhì)
dup
dup2的使用——輸出重定向
?dup2的使用——輸入重定向
自定義shell實(shí)現(xiàn)重定向指令
文件的重定向和進(jìn)程替換
重定向的參數(shù)
如何理解計(jì)算機(jī)下“一切皆文件”
文件描述符的分配規(guī)則
? ? ? ? 想要知道文件描述符的分配規(guī)則, 我們需要使用一個(gè)實(shí)驗(yàn)來(lái)測(cè)試出來(lái)。 下面我們開(kāi)始進(jìn)行這個(gè)實(shí)驗(yàn):
? ? ? ? 在這個(gè)實(shí)驗(yàn)中, 我們會(huì)用到wrrite, open函數(shù), 如下圖為man手冊(cè):
? ? ? ? 其中, open函數(shù)需要包含頭文件sys/types.h、sys/stat.h、fcntl.h
? ? ? ? write函數(shù)需要包含unistd.h頭文件????????
? ? ? ? 然后, 我們的代碼如下:
需要用到的頭文件:
下面是我們的代碼:
這個(gè)程序運(yùn)行后, 就是如下結(jié)果:
圖中打印fd, 然后將hello linux的內(nèi)容打印到log.txt文件中, 再輸出log.txt的內(nèi)容, 就如同上圖
????????上圖的fd打印為3, 我們知道, 0, 1, 2對(duì)應(yīng)的是stdin, stdout, stderr。所以新文件fd就到了3號(hào)fd。
????????接下來(lái), 就開(kāi)始測(cè)試文件描述符的分配規(guī)則。
????????從0號(hào)小標(biāo)開(kāi)始, 尋找最小的沒(méi)有使用的數(shù)組位置,它的下標(biāo)就是新文件的文件描述符。
? ? ? ? 我們從上圖可以看出, 文件描述符是3。 而0, 1, 2都被占用了。 我們就可以考慮——對(duì)于文件來(lái)說(shuō), 文件描述符都是從小到大創(chuàng)建的。?
? ? ? ? 那么我們?yōu)榱蓑?yàn)證這個(gè)猜想, 就可以消除0號(hào)下標(biāo)的指向。 那么0號(hào)就空出來(lái)了。 這個(gè)時(shí)候我們?cè)賱?chuàng)建的文件就是被映射在了0號(hào)下標(biāo)處。
下面是測(cè)試代碼:
然后打印出來(lái)的fd如下:
上面的結(jié)果就是說(shuō), 消除了0號(hào)fd位置的指針, 當(dāng)我們?cè)俅蜷_(kāi)一個(gè)文件的時(shí)候就可以將這個(gè)文件指針?lè)湃?號(hào)位置。?
我們?cè)訇P(guān)閉1號(hào)fd進(jìn)行測(cè)試:
然后運(yùn)行結(jié)果:
?
沒(méi)有打印出內(nèi)容的原因是因?yàn)?號(hào)是顯示器文件, 關(guān)閉后就不會(huì)再向顯示器中打印了。
關(guān)閉2再測(cè)試一下
然后運(yùn)行結(jié)果:
清除2號(hào)指針后, 然后打開(kāi)文件, 2號(hào)就會(huì)保存新打開(kāi)的文件指針。 然后打開(kāi)的fd就變成了2, 打印出來(lái)的是2, 同樣符合我們的假設(shè)。
- 那么現(xiàn)在就可以下結(jié)論了——文件描述符對(duì)應(yīng)的分配規(guī)則是什么? 從0下標(biāo)開(kāi)始, 尋找最小的沒(méi)有使用的數(shù)組位置, 它的下標(biāo)新文件的文件描述符。
重定向的本質(zhì)
我們?cè)谏厦嫣骄课募枋龇姆峙湟?guī)則的時(shí)候, 知道了1號(hào)文件描述符被清空后, 再新打開(kāi)的文件的文件指針就會(huì)保存到1號(hào)文件描述符中。 ——這個(gè)過(guò)程起始就是重定向。
下面重新捋一下這個(gè)過(guò)程, 對(duì)于上面這個(gè)過(guò)程, 我們的進(jìn)程本來(lái)有一個(gè)文件描述符表:
? ? ? ? 然后我們將1號(hào)fd指向顯示器的文件的指針收回, 然后創(chuàng)建新文件log.txt, 將log.txt的struct_files的地址放到1號(hào)文件的fd處。
我們看下面的具體代碼:
????????上面這個(gè)1號(hào)fd轉(zhuǎn)化到過(guò)程, 也就是上面黃框框的代碼段。 對(duì)于操作系統(tǒng)來(lái)說(shuō), 他知不知道fd的指向發(fā)生了變化呢? 答案是不知道!!對(duì)于操作系統(tǒng)來(lái)說(shuō), 他不管fd下面做了什么, 他只認(rèn)fd。 所以, 如果還向1里面寫(xiě)東西, 那么就是本來(lái)向顯示器文件里面寫(xiě)東西轉(zhuǎn)化為向log.txt文件里面寫(xiě)東西。 而這個(gè)過(guò)程就是重定向。
? ? ? ? 那么我們?nèi)绻胍蚱渌募锩鎸?xiě)東西, 是不是就需要將這個(gè)文件的指針覆蓋到1號(hào)fd里面? ——這就是重定向。 重定向只需要將想要重定向到文件的指針覆蓋到1號(hào)所在的fd里面!!
dup
上面我們講道理重定向的底層原理。 但是整個(gè)代碼很長(zhǎng)——需要一開(kāi)始關(guān)閉1號(hào)fd文件描述符指針, 然后將新打開(kāi)文件的文件描述符指針?lè)诺?號(hào)文件中。 實(shí)際上, 系統(tǒng)就是提供了一種fd覆蓋的接口——dup系列, 下面是man手冊(cè):
上面有三個(gè)dup系列函數(shù), 常用的是dup2. 下面我們具體查看一下dup2的用法:
第一個(gè)參數(shù)名為newfd, 對(duì)應(yīng)上面的1號(hào)fd, 第二個(gè)參數(shù)名是oldfd, 對(duì)應(yīng)上面的新打開(kāi)的文件。 也就是說(shuō)將oldfd里面的內(nèi)容拷貝到newfd里面。
dup2的使用——輸出重定向
????????dup2可以直接將數(shù)組中的一個(gè)fd覆蓋到另一個(gè)數(shù)組fd。 我們dup2的第一個(gè)參數(shù)是新打開(kāi)的文件fd, 第二個(gè)參數(shù)是要拷貝到的fd的位置。
????????如下為代碼:
運(yùn)行結(jié)果如下:
? ? ? ? 我們也可以把清空寫(xiě)改成追加寫(xiě):
運(yùn)行結(jié)果如下:
?dup2的使用——輸入重定向
先創(chuàng)建一個(gè)數(shù)組進(jìn)行拷貝拷貝, 然后向顯示器中讀取, 如果讀取, 那么打印讀取的內(nèi)容。
此時(shí)是向鍵盤(pán)中讀取:
我們使用dup2, 將新打開(kāi)的文件覆蓋到0號(hào)fd。 就是輸入重定向, 將新打開(kāi)的文件的數(shù)據(jù)打印:
如圖就是將新打開(kāi)文件的數(shù)據(jù)打印到inbuffer。 再將inbuffer的數(shù)據(jù)打印。 我們?cè)趌og.txt里面寫(xiě)上aaaaaaaa
下面是打印內(nèi)容:
自定義shell實(shí)現(xiàn)重定向指令
????????如何自己實(shí)現(xiàn)>, >>, < 指令
????????要自己實(shí)現(xiàn)>, >>, <指令, 我們就要拿出我們之前寫(xiě)的自定義shell的代碼了。?
????????在代碼中, 我們需要先新定義幾個(gè)宏——NONE代表沒(méi)有重定向, IN_RDIR代表輸入重定向, OUT_RDIR代表輸入輸出重定向, APPEND_RDIR代表追加重定向。
? ? ? ? 也要定義兩個(gè)新的變量——rdirfilename指向重定向文件的首地址, rdir代表重定向的標(biāo)志。
如下圖宏定義:
新創(chuàng)建的變量:
在交互函數(shù)里面分析是否有重定向, check_rdir就是重定向判斷的函數(shù):
下圖是check_rdir的實(shí)現(xiàn):
然后我們?cè)僭趫?zhí)行普通命令的板塊里面創(chuàng)建一個(gè)新的代碼塊。 也就是當(dāng)id == 0的時(shí)候, 判斷此時(shí)的rdir的狀態(tài), 如果是NONE才是exec, 正常加載執(zhí)行邏輯。 如下是代碼:
然后我們還要在每次輸入指令的時(shí)候都給rdir和rdirfilename做初始化:
運(yùn)行出結(jié)果之后:
?
文件的重定向和進(jìn)程替換
????????現(xiàn)在有一個(gè)問(wèn)題, 就是在重定向的時(shí)候, 我們修改了fd。 然后加載了子進(jìn)程, 為什么這樣做是正確的呢?——要解決這個(gè)問(wèn)題, 就要拿起進(jìn)程的知識(shí)了, 如下圖:
????????在上面的圖里面, PCB和文件管理, 是內(nèi)核數(shù)據(jù)結(jié)構(gòu); 而虛擬地址空間, 物理內(nèi)存, 頁(yè)表, 是進(jìn)程數(shù)據(jù)結(jié)構(gòu),?這兩個(gè)是結(jié)偶關(guān)系。 而對(duì)于物理內(nèi)存, 程序和代碼加載替換掉物理內(nèi)存, 頁(yè)表重新映射物理內(nèi)存。 這個(gè)過(guò)程, 在內(nèi)核數(shù)據(jù)結(jié)構(gòu)里, 并不關(guān)心。
? ? ? ? 所以, 文件的重定向和進(jìn)程替換之間互不影響!!!
重定向的參數(shù)
我們使用重定向, 可能遇到下圖這種只有一部分?jǐn)?shù)據(jù)重定向到了新文件, 但是還有一部分直接打印到了顯示器的情況:
????????上面描述的問(wèn)題就是重定向了一部分, 但是還有一部分沒(méi)有重定向, 這是因?yàn)閷?duì)于重定向來(lái)說(shuō), 默認(rèn)是將打印到顯示器的數(shù)據(jù)重定向到文件中。
- ? ? ? ? 想要將兩部分——stdout、stderr兩個(gè)部分的數(shù)據(jù)都進(jìn)行重定向, 就需要使用參數(shù)fd, 使用方式如下: fd > 文件。
? ? ? ? 如下圖使用:
這兩種方法我們要談的是第二種方法:./newfile.exe 1 > both.log 2>&1, 這里面2>&1的意思就是說(shuō), 將1fd的內(nèi)容拷貝到2fd里面去。 而1已經(jīng)重定向到了both.log, 所以, 將1的內(nèi)容拷貝到2fd里面去后。 本應(yīng)該打印到2fd里面的內(nèi)容也會(huì)被打印到文件中。
如何理解計(jì)算機(jī)下“一切皆文件”
? ? ? ? 對(duì)于計(jì)算機(jī)來(lái)說(shuō), 所有的操作計(jì)算機(jī)的動(dòng)作, 都是以進(jìn)程的方式進(jìn)行操作的。所有訪問(wèn)文件的操作, 最終都是用進(jìn)程的方式訪問(wèn)文件的。
? ? ? ? 計(jì)算機(jī)上所有應(yīng)用的所有操作, 最終都會(huì)被系統(tǒng)解釋成進(jìn)程。 目前, 所有對(duì)文件的操作, 全部都依賴進(jìn)程的操作。?
? ? ? ? 而且, 我們知道, 對(duì)于馮諾依曼體系結(jié)構(gòu)來(lái)說(shuō), 底層大部分都是外設(shè)!!!如下圖:
????????上面就是一個(gè)一個(gè)打開(kāi)文件后創(chuàng)建的結(jié)構(gòu)體, 下面就是底層硬件。
- ? ? ? ? ?對(duì)于上面圖中的底層設(shè)備, 每一個(gè)外設(shè)的讀寫(xiě)方法都是不一樣的, 也就是他們的struct file是不一樣的。 所以這個(gè)時(shí)候每一個(gè)struct file里面都有一個(gè)指針指向struct operation_func類型的結(jié)構(gòu)體。
如下圖:
那么, 未來(lái)操作系統(tǒng)為了進(jìn)行文件操作, 就會(huì)先創(chuàng)建一個(gè)進(jìn)程:
然后, 操作系統(tǒng)又專門(mén)給我們定義了系統(tǒng)調(diào)用:
????????所以, 操作系統(tǒng)就實(shí)現(xiàn)了在上層統(tǒng)一使用read, write接口, 然后在下層根據(jù)文件的不同, 找到不同的write, read方法。
? ? ? ? ?所以, 一切皆文件——就是操作系統(tǒng)在文件層面上封裝一層struct file結(jié)構(gòu)體對(duì)象, 然后, 根據(jù)這個(gè)對(duì)象里面的指針找到對(duì)應(yīng)文件的write, read。 而這里的write, read同樣是一層封裝各種各樣讀寫(xiě)方法的結(jié)構(gòu)體。 而真正的各種設(shè)備的讀寫(xiě)方法如何實(shí)現(xiàn), 我們并不關(guān)心!!!
? ? ? ? 從struct file往上, 就是用戶!是給我們看到的, 我們看到的, 就是struct file。 看到的就是——一切皆文件!!!
? ? ? ? 所以, 在linux中, 在struct file這一層, 被稱作VFS——virtual file system虛擬文件系統(tǒng)。
- ? ? ? ? 以后, 當(dāng)我們的進(jìn)程再想實(shí)現(xiàn)open, write這些接口的時(shí)候, 就會(huì)先找到struct file。 然后struct file就回去找到自己里面的operation_func, 至于operation里面是什么情況, struct file并不關(guān)心。 而這, 就是多態(tài)。 這里面的一層一層的指針的包含關(guān)系, 就叫做繼承!!
以上, 就是本節(jié)的全部?jī)?nèi)容, 下面是博主整理的個(gè)人筆記: