當前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導(dǎo)讀]關(guān)注「Linux大陸」,一起進步!繼?300來行代碼帶你實現(xiàn)一個能跑的最小Linux文件系統(tǒng)?之后,我們來看看如何60行C代碼實現(xiàn)一個shell!在實現(xiàn)它之前,先看看這樣做的意義。美是有目共睹的。Unix之美,稍微體會,便能得到。1969年,Unix初始,沒有fork,沒有exe...

關(guān)注Linux大陸」,一起進步!


繼?300來行代碼帶你實現(xiàn)一個能跑的最小Linux文件系統(tǒng)?之后,我們來看看如何60行C代碼實現(xiàn)一個shell

在實現(xiàn)它之前,先看看這樣做的意義。

美是有目共睹的。Unix之美,稍微體會,便能得到。

1969年,Unix初始,沒有fork,沒有exec,沒有pipe,沒有?“一切皆文件”?,但是那時它已經(jīng)是Unix了。它簡單,可塑。

Melvin Conway在1963年的論文中敘述fork思想時就解釋說并行路徑要用結(jié)果來交互,也就是在匯合的join點來同步結(jié)果。這個同步點所得到的,就是一個并行進程的?輸出?。

在此之外,Unix還有另一個原則,就是?組合小程序!

Unix把一系列功能單一的小程序組合成一個復(fù)雜的邏輯,這個原則有以下優(yōu)勢:

  • 每一個小程序都很容易編寫。

  • 每一個小程序可以分別完成。

  • 每一個小程序可以分別迭代修復(fù)。

  • 多個小程序可以自由組合。

這是典型的模塊化思想,小到統(tǒng)籌佐餐燒飯,大到組成生命的嘌呤嘧啶,都不自覺地和這種模塊化思想相契機,原來這就是真理。?程序盡量小,只做一件事并且做好它。

Unix程序在自身的邏輯之外對外暴露的只有輸入和輸出。那么?用輸出連接另一個程序輸入?就是一種好的方法。所謂Conway的join點對于Unix進程指的就是輸出。

對外暴露的越少,程序越內(nèi)聚。這是一種范式,類似RISC處理器也是抽象出僅有的load和store來和內(nèi)存交互。

簡單來講,Unix程序通過輸入和輸出來彼此連接。下面是一幅來自Wiki的圖示:

詳見Pipeline (Unix):
https://en.wikipedia.org/wiki/Pipeline_(Unix)

Unix的另一個原則,即著名的?“一切皆文件!”?連接輸出和輸入的那個管道在Unix中被實現(xiàn)為Pipe,顯然,它也是文件,一個FIFO文件。

說實話,協(xié)作幾個小程序形成一個大邏輯的思想還是來自于Convey,在Convey的論文里,他稱為?協(xié)程,?Pile可以說是直接實現(xiàn)了?Convey協(xié)程?之間的交互。有關(guān)這段歷史,請看:
http://www.softpanorama.org/Scripting/Piporama/history.shtml

用Pipe連接作為輸出和輸入連接Unix進程可以做成什么事情呢?讓我們?nèi)ジ惺芤粋€再熟悉不過的實例,即數(shù)學(xué)式子:


我們把運算符加號,乘號,除號(暫不考慮括號,稍后解釋為什么)這些看作是程序(事實上它們也真的是),那么類似數(shù)字3,5,7,6就是這些程序的輸入了,這個式子最終需要一個輸出,獲得這個輸出的過程如下:

  1. 數(shù)字3,5是加號程序的輸入,3 5執(zhí)行,它獲得輸出8.

  2. 第1步中的輸出8連同數(shù)字7作為乘號程序的輸入,8 ×?7執(zhí)行,獲得輸出56.

  3. 第2步中的輸出56連同數(shù)字6作為除號的輸入,…

這個數(shù)學(xué)式子的求值過程和pipe連接的Unix程序組合獲得最終結(jié)果的過程完全一致。

如果你相信數(shù)學(xué)可以描述整個世界,那么Pipe連同Unix程序同樣是描述這個世界的語言 。

在數(shù)學(xué)領(lǐng)域,程序?就是所有的運算符,加號,減號,乘號,除號,乘方,開方,求和,積分,求導(dǎo)…它們無一例外,?只做一件事。

在Unix看來也同樣。它做的事情和下面的應(yīng)該差不多,而且更多:

寫出上面的式子中每一個數(shù)學(xué)運算符的程序并不困難,比如加號程序:

// plus.c
#include
int main(int argc, char **argv)
{
int a, b;

a = atoi(argv[1]);
b = atoi(argv[2]);

a = a b;
printf("%d\n", a);
}
同樣,我們可以寫出除法,直到偏導(dǎo)的程序。然后我們通過pipe就能將它們組合成任意的數(shù)學(xué)式子。

現(xiàn)在談?wù)刄nix組合程序的具體寫法,如果我們要化簡薛定諤方程,我們應(yīng)該如何用Unix命令寫出與上述式子等價的組合程序命令行呢?我們無法像數(shù)學(xué)家手寫那樣隨意使用括號,顯然,計算機并不認識它。我們能夠使用的只有兩個符號:

  1. 代表具體Unix小程序的命令。

  2. Pipe符號"|"。

換句話說,我們需要寫出一個?鏈式組合表達式。?這時就要用到前綴表達式了。

數(shù)學(xué)式子里的括號,其實它無關(guān)緊要,括號只是給人看的,它規(guī)定一些運算的優(yōu)先級順序,這叫?中綴表達式?,一個中綴表達式可以輕松被轉(zhuǎn)換為?前綴表達式,后綴表達式?,從而消除括號。事實上,Unix的Pipe最初也面臨過這樣的問題,到底是中綴好呢,還是前/后綴好呢?

我們現(xiàn)在使用的Unix/Linux命令,以cp舉例:

cp $in $out
這是一個典型的前綴表達式,但是當pipe的發(fā)明者McIlroy最初引入pipe試圖組合各個程序時,最初上面的命令行被建議成:

$in cp $out
就像我們的(3 5) ×?8 一樣。但是這非常不適合計算機處理的風(fēng)格,計算機不得不首先掃描解析這個式子,試圖:

  1. 理解?“括號括起來的要優(yōu)先處理”?這句復(fù)雜的話;

  2. 區(qū)分哪些是輸入,哪些是操作符…

對于式子(3 5) ×?8?的求值,計算機更適合用一種在簡單規(guī)則下非常直接的方式去?順序執(zhí)行?求解,這就是前綴表達式的優(yōu)勢。

× 8 ? 35就是(3 5)?×?8?的前綴表達式,可以看到,沒有了括號。對于pipe組合程序而言,同樣適用于這個原則。于是前綴命令成了pipe組合命令的首選,現(xiàn)如今,我們可以用:

pro1 $stdin|pro2|pro3|pro4|...|proX $stdout
輕松組合成任意復(fù)雜的邏輯。

Pipe協(xié)同組合程序的Unix原則是一個創(chuàng)舉,程序就是一個加工過濾器,它把一系列的輸入經(jīng)過自己的程序邏輯生成了一系列的輸出,該輸出又可以作為其它程序的輸入。

在Unix/Linux中,各種shell本身就實現(xiàn)了這樣的功能,但是為了徹底理解這種處理方式的本質(zhì),只能自己寫一個才行。來寫一個微小的shell吧。

再次看上面提到的Unix Pipe的處理序列:

pro1 $stdin|pro2|pro3|pro4|...|proX $stdout
如果讓一個shell處理以上組合命令,要想代碼量少,典型方案就是遞歸,然后用Pipe把這些遞歸調(diào)用過程給串起來,基本邏輯如下:

int exec_cmd(CMD *cmd, PIPE pipe)
{
// 持續(xù)解析命令行,以pipe符號|分割每一個命令
while (cmd->next) {
PIPE pp = pipe_create();
if (fork() > 0) {
// 父進程遞歸解析下一個
exec_cmd(cmd->next, pp);
return 0;
}
// 子進程執(zhí)行
dup_in_out(pp);
exec(cmd->cmdline);
}
if (fork() > 0) {
wait_all_child();
return 0;
} else {
dup_in_out(pp);
exec(cmd->cmdline);
}
}
按照上面的思路實現(xiàn)出來,大概60行左右代碼就可以:

// tinysh.c
// gcc tinysh.c -o tinysh
#include
#include
#include
#include

#define CMD_BUF_LEN 512
char cmd[CMD_BUF_LEN] = {0};

void fork_and_exec(char *cmd, int pin, int pout)
{
if (fork() == 0) {
if (pin != -1) {
dup2 (pin, 0);
close(pin);
}
if (pout != -1) {
dup2 (pout, 1);
close(pout);
}
system(cmd);
exit(0);
}
if (pin != -1)
close(pin);
if (pout != -1)
close(pout);
}

int execute_cmd(char *cmd, int in)
{
int status;
char *p = cmd;
int pipefd[2];

while (*p) {
switch (*p) {
case '|':
*p = 0;
pipe(pipefd);
fork_and_exec(cmd, in, pipefd[1]);
execute_cmd(p, pipefd[0]);
return 0;
default:
p ;
}
}
fork_and_exec(cmd, in, -1);
while(waitpid(-1,
本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉