亚洲有码Av一区二区三区_国产高清啪啪免费视频_69色视频国产_国产成人人人爆出白浆_国产精品自在线拍国_一本久久伊人热热精品无码_午夜性刺激在线看免费带字幕_助力高品质欧美狂喷水_亚洲精品日韩无码_精品无码一区二区三区蜜臀_麻豆高清国产AV_熟妇人素无码中文字幕_亚洲a级片在线观看_国产欧美日韩三区_99国产成人高清在线观看

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運營的一線實戰(zhàn)洞察。

手寫自定義Shell:從fork/exec到管道重定向的Linux進程實戰(zhàn)

手寫自定義Shell:從fork/exec到管道重定向的Linux進程實戰(zhàn) 1. 為什么要在2025年親手寫一個Shell這個項目比你想的更值錢先別急著關(guān)頁面。我知道你的第一反應(yīng)是Shell不是現(xiàn)成的嗎Bash、Zsh、Fish隨便挑一個都比自己寫的強我干嘛要自討苦吃這個反應(yīng)沒錯但恰恰是問題所在?,F(xiàn)成的Shell把你該學(xué)的東西都藏起來了。你在終端里敲ls -l | grep txt的時候Bash替你做了三件事解析字符串、創(chuàng)建管道、拉起子進程。這三件事每一個都是操作系統(tǒng)核心機制的實戰(zhàn)考點而你平時根本看不見它們。我見過太多人Linux命令敲得飛起但一問fork()和exec()的區(qū)別就卡殼能寫出幾百行的Shell腳本卻不理解為什么管道兩端的命令是“同時”運行的。倒不是說他們笨而是工具太好的時候人對底層機制會失去感知。這就跟開自動擋開久了不會開手動擋一樣不是技術(shù)問題是習(xí)慣問題。自定義Shell這個項目本質(zhì)上就是把你平時用的黑盒打開讓你看看里面是什么。你不需要做出一個能和Bash抗衡的產(chǎn)品你只需要做出一個能跑ls、cd、cat file | grep foo、./program out.txt的“玩具”——但這趟走下來你對Linux進程模型、文件描述符、字符串處理的理解會比你讀三章APUE來得扎實。而且實用性也很強。很多公司面試會問“設(shè)計一個迷你Shell”或者讓你在紙上寫fork exec wait的組合代碼。你親手寫過一遍這種題就是送分題。退一步說就算不為了面試?yán)斫釹hell的工作方式對你寫配置腳本、排查啟動問題、理解Docker容器的進程行為都有直接幫助——因為你終于知道那些“神奇”的操作底下到底發(fā)生了什么。下面我直接把這套實現(xiàn)拆開講從項目結(jié)構(gòu)、核心機制到具體代碼、踩坑記錄一條線走完。2. 動手前的核心知識儲備進程模型、系統(tǒng)調(diào)用與解析器設(shè)計的底層邏輯寫Shell之前必須先建立三個底層認(rèn)知。這三點如果不提前搞清楚寫出來的東西大概率是“看起來像Shell跑起來全是病”。2.1 進程模型Shell其實是個“包工頭”Linux下的進程模型一句話概括就是進程是“復(fù)制”出來的不是“創(chuàng)建”出來的。所有用戶態(tài)進程都是從一個叫init的祖先進程一路fork()復(fù)制出來的。關(guān)鍵認(rèn)知是一個程序跑起來之后你通常有兩個實體——父進程和子進程。父進程負責(zé)“管理”子進程負責(zé)“干活”。Shell就是父親的典型代表它接收你輸入的命令然后fork()出一個子進程再在子進程里用exec()把命令真正換上去。打個比方。Shell是包工頭你讓他蓋一棟樓執(zhí)行命令他不會自己上腳手架而是打電話叫一隊工人fork()一個新進程然后告訴工人具體干什么活exec()把程序加載進來。包工頭自己在旁邊蹲著等工人干完wait()好匯報給你。這跟我們平時跑程序的感覺完全不一樣。我們直覺上覺得“程序是被操作系統(tǒng)創(chuàng)建出來的”但Linux底下的語言是先復(fù)制一份當(dāng)前狀態(tài)再替換成新程序。這兩個步驟必須拆開理解否則你會對整個Shell的實現(xiàn)邏輯產(chǎn)生疑惑。2.2 三大系統(tǒng)調(diào)用fork、exec、wait這是Shell的基石必須吃透。fork()調(diào)用一次返回兩次。一次在父進程里返回子進程的PID一次在子進程里返回0。返回值是負則說明創(chuàng)建失敗。這段代碼pid_t pid fork(); if (pid 0) { // 子進程這里繼續(xù)跑的是從父進程復(fù)制過來的代碼副本 } else if (pid 0) { // 父進程pid 就是子進程的進程ID } else { perror(fork); }很多人不理解“返回兩次”是怎么回事。換個說法fork()完成那一刻系統(tǒng)里憑空多了一個進程這個進程和父進程幾乎一模一樣連程序計數(shù)器PC的位置都一樣。也就是說子進程從fork()返回的那一行開始繼續(xù)執(zhí)行同一份代碼。唯一能區(qū)分兩人的就是返回值——父進程拿到的是子進程的PID子進程拿到的是0。exec()注意它不是函數(shù)而是一族函數(shù)——execl、execv、execvp等等。它們的作用是一個進程把自己“換掉”把當(dāng)前進程的代碼段、數(shù)據(jù)段、棧全部替換成新程序的然后從頭開始跑新程序。成功的話不會返回失敗才返回 -1。我們后面統(tǒng)一用execvp()因為它的第二個參數(shù)直接接受一個字符串?dāng)?shù)組argv形式對解析命令行特別方便。wait()/waitpid()父進程調(diào)用這個就會阻塞直到某個子進程結(jié)束。這個“阻塞”很重要——Shell必須等命令執(zhí)行完才能顯示下一行提示符這跟你在終端里的直覺體驗一致。三者合起來的完整流程父進程讀入一行命令fork()出子進程子進程調(diào)用execvp()變成那條命令本身父進程調(diào)用waitpid()等子進程退出子進程退出后父進程打印提示符繼續(xù)讀下一條命令。這個循環(huán)永遠不會變Shell的一切都是圍繞它展開的。2.3 解析器設(shè)計把“字符串”變成“參數(shù)數(shù)組”Shell讀到的是一行字符串比如ls -l /home/user。但execvp()需要的是char *argv[] {ls, -l, /home/user, NULL};所以你要做的是把char *line變成char **args。這個過程叫命令行解析是整個項目里最考驗“耐煩心”的部分——它不涉及什么高深算法但全是邊界條件。一個常見的誤區(qū)是別只用空格拆分。因為Shell命令里可以有引號echo hello world和轉(zhuǎn)義echo hello\ world粗暴按空格拆會把hello world拆成兩個參數(shù)。我們第一版可以不管引號但你要知道這個邊界存在否則后續(xù)擴展時容易翻車。我會用strsep()這個不太常見的函數(shù)來做拆分原因后面正文里說。它比strtok()安全——strtok()內(nèi)部有靜態(tài)變量線程不友好而且會把連續(xù)分隔符跳過有時候這不是你想要的。解析器可以分兩步走先用一個函數(shù)把整行命令按空白拆成獨立單詞再把結(jié)果整理成NULL結(jié)尾的指針數(shù)組。任何哈希、樹結(jié)構(gòu)、復(fù)雜狀態(tài)機在這個項目里都是過度設(shè)計——你需要的只是循環(huán)、指針、和一點點耐心。3. 第一個能跑的Shell骨架從main函數(shù)到循環(huán)框架好基礎(chǔ)打完了開始寫代碼。我不會一次性給你一個900行的完整項目而是從最小的骨架開始一步步長出一個能用的Shell。3.1 用getline()讀入命令比你想的要注意更多細節(jié)讀取命令行的標(biāo)準(zhǔn)做法是getline()它自動管理緩沖區(qū)比你用fgets()滿省心得多。核心代碼#define _GNU_SOURCE #include stdio.h #include stdlib.h #include string.h char *line NULL; size_t bufsize 0; ssize_t nread getline(line, bufsize, stdin); if (nread -1) { // EOF 或出錯 free(line); exit(0); } // 去掉末尾換行符 line[strcspn(line, \n)] \0;三個易踩的坑坑1getline()返回的是ssize_t不是int。無符號類型和有符號類型比較時會有隱式轉(zhuǎn)換問題編譯器會報警告。養(yǎng)成用ssize_t的習(xí)慣???去掉換行符千萬別用strlen(line) - 1。如果文件最后一行沒有換行符strlen減一就把合法字符刪掉了。用strcspn(line, \n)找到換行符的位置安全得多???命令行過長時getline()會自動擴容緩沖區(qū)不用你操心。但這也意味著你必須free()否則每次循環(huán)都泄漏一段內(nèi)存。別偷懶。3.2 主循環(huán)框架“讀命令 - 解析 - 執(zhí)行 - 等待”的無限循環(huán)Shell的本質(zhì)就是一個死循環(huán)退出條件只有兩個用戶敲exit或者輸入流遇到EOF比如按CtrlD。int main(void) { char *line NULL; size_t bufsize 0; ssize_t nread; while (1) { printf(mysh ); fflush(stdout); // 一定要有 nread getline(line, bufsize, stdin); if (nread -1) { printf(\n); break; // EOF 退出 } line[strcspn(line, \n)] \0; // 空命令直接跳過 if (strlen(line) 0) continue; char **args parse_command(line); if (args[0] NULL) { free(args); continue; } // 檢查是不是內(nèi)建命令是的話直接執(zhí)行不是再fork if (is_builtin(args[0])) { run_builtin(args); } else { run_external(args); } free(args); } free(line); return 0; }這段代碼展示了整體骨架。注意fflush(stdout)這一行——沒有它提示符可能不會立即顯示尤其在管道或腳本環(huán)境下。這是初學(xué)者最容易忽略的細節(jié)之一后面會說更多。3.3parse_command()我為什么推薦strsep()而不是strtok()解析函數(shù)要把ls -l /home變成{ls, -l, /home, NULL}。我推薦用strsep()char **parse_command(char *line) { int bufsize 64; int index 0; char **args malloc(bufsize * sizeof(char *)); char *token; if (!args) { perror(malloc); exit(EXIT_FAILURE); } char *rest line; while ((token strsep(rest, \t\r\n)) ! NULL) { if (*token \0) continue; // 跳過連續(xù)空白 args[index] token; index; if (index bufsize) { bufsize 64; args realloc(args, bufsize * sizeof(char *)); if (!args) { perror(realloc); exit(EXIT_FAILURE); } } } args[index] NULL; return args; }為什么不用strtok()三個理由每一個都夠你在生產(chǎn)環(huán)境里吃一次虧1. 線程安全。strtok()內(nèi)部維護了一個靜態(tài)變量來保存剩余字符串位置多個線程同時調(diào)用會串?dāng)?shù)據(jù)。Shell雖然是單線程的但這個壞習(xí)慣不值得養(yǎng)成。strsep()用局部變量rest手動控制。2. 連續(xù)空白符的處理粒度不一樣。strtok()默認(rèn)把連續(xù)分隔符當(dāng)成一個strsep()會返回空字符串需要你手動跳過。看起來是多寫了一行但給了你更多控制權(quán)。3. 解析失敗時便于排查。因為你每一步都在自己的局部變量上進行斷點調(diào)試時可以看到全部中間狀態(tài)。這里有個關(guān)鍵細節(jié)args數(shù)組里的指針直接指向原line字符串里的位置。也就是說解析沒有strdup沒有新分配內(nèi)存只是把原來的空格替換成\0然后記錄每個單詞的起點。這樣解析速度極快但代價是——你必須保證line在args使用期間不被釋放或修改。在我們的主循環(huán)里args在run_external()返回后才被釋放而execvp()在子進程里已經(jīng)完成加載所以沒問題。3.4 執(zhí)行外部命令fork execvp waitpid 的標(biāo)準(zhǔn)模板run_external()是核心中的核心void run_external(char **args) { pid_t pid fork(); if (pid -1) { perror(fork); return; } if (pid 0) { // 子進程直接替換成目標(biāo)程序 if (execvp(args[0], args) -1) { perror(execvp); exit(EXIT_FAILURE); // 必須exit否則會繼續(xù)跑父進程的循環(huán) } } else { // 父進程等待子進程完成 int status; if (waitpid(pid, status, 0) -1) { perror(waitpid); return; } // 可以在這里檢查 status判斷命令是否正常退出 } }這段代碼你倒背如流也不過分。但我必須強調(diào)一個致命細節(jié)子進程里調(diào)用execvp()失敗后必須立即exit(EXIT_FAILURE)。如果漏掉這行子進程就會繼續(xù)往下執(zhí)行進入父進程的循環(huán)邏輯然后子進程也跑去讀命令、fork進程——你就獲得了一群裂變出來的Shell。這是新手最容易犯、后果最嚴(yán)重的錯誤。在子進程分支里永遠記住exec一族函數(shù)成功就不返回失敗也必須立即退出。還有一點execvp()找可執(zhí)行文件依賴環(huán)境變量PATH。它內(nèi)部會自動搜索PATH目錄下的可執(zhí)行文件。你傳入的args[0]如果是簡單的ls它就會去/usr/bin等目錄找。如果想執(zhí)行當(dāng)前目錄下的程序用戶必須輸入./a.out像真正的Shell一樣。4. 內(nèi)建命令的處理為什么必須自己實現(xiàn)cd、exit、echo你可能想問cd不也是一個可執(zhí)行文件嗎我直接execvp(cd, args)不就行了不行。原因得從進程模型里找——你還記得吧子進程是父進程的“復(fù)制品”子進程里改了什么都不會影響父進程。cd的本質(zhì)是改變進程的當(dāng)前工作目錄這屬于進程自身狀態(tài)。如果你fork()一個子進程去執(zhí)行cd子進程的工作目錄變了但父進程Shell本體的工作目錄紋絲不動。等你回到主循環(huán)打出下一個提示符目錄還是原來那個。這就是“內(nèi)建命令必須由Shell進程自己執(zhí)行”的根本原因。內(nèi)核態(tài)的表現(xiàn)也是如此改變工作目錄的是chdir()系統(tǒng)調(diào)用它只對發(fā)起調(diào)用的進程起作用。沒有一條路能讓子進程去改父進程的工作目錄某些調(diào)試器玩法另說但那不是普通命令的路徑。4.1cd的實現(xiàn)比你想的復(fù)雜一點點int mysh_cd(char **args) { if (args[1] NULL) { // 沒有參數(shù)回到主目錄 const char *home getenv(HOME); if (home NULL) { fprintf(stderr, mysh: cd: HOME not set\n); return 1; } if (chdir(home) ! 0) { perror(mysh: cd); return 1; } } else { if (chdir(args[1]) ! 0) { perror(mysh: cd); return 1; } } return 0; }兩個容易忽略的細節(jié)第一cd不帶參數(shù)時應(yīng)該回到HOME目錄。這是POSIX標(biāo)準(zhǔn)約定也是用戶在交互式Shell中的直覺。別嫌麻煩加上去。第二參數(shù)多于一個時怎么處理標(biāo)準(zhǔn)cd只接受一個參數(shù)加上可選的-P/-L標(biāo)志。我們做個簡化版只取args[1]多余參數(shù)報錯即可。4.2exit的實現(xiàn)要有返回值意識int mysh_exit(char **args) { if (args[1] ! NULL) { // 如果帶了退出碼參數(shù)就解析它 int code atoi(args[1]); exit(code); } exit(0); }注意這里有個小訣竅你可以在主循環(huán)里讓run_builtin()返回一個特殊值提示“該退出Shell了”而不是在mysh_exit()里直接exit()。這樣設(shè)計更靈活以后你想加“退出前保存歷史記錄”“打印最后一條統(tǒng)計信息”之類的功能就還有機會攔截。直接exit()相當(dāng)于把后門焊死了。4.3echo的一個隱藏問題參數(shù)已經(jīng)解析好了要不要處理引號echo在處理參數(shù)時要多個心眼。我們已經(jīng)說了第一版解析器不會處理引號所以用戶輸入echo hello world時parse_command()會拆成{echo, \hello, world\, NULL}。那echo打印出來就是hello world帶引號的兩個單詞。這是整個項目最折磨人的部分也最能體現(xiàn)“做玩具和做產(chǎn)品的差別”。如果我們想支持引號就得升級解析器讓它識別...內(nèi)的空格不算分隔符。但注意優(yōu)先級先讓核心跑起來再考慮完善。第一版的echo就是這么簡單int mysh_echo(char **args) { for (int i 1; args[i] ! NULL; i) { printf(%s, args[i]); if (args[i1] ! NULL) printf( ); } printf(\n); return 0; }等核心功能都跑通了你還想支持引號我們再單獨寫一個quote_aware_parse()替換掉parse_command()。這樣項目迭代順序清晰每一輪都有可驗證的成果。4.4 內(nèi)建命令的狀態(tài)管理用一個字符串?dāng)?shù)組和函數(shù)指針表內(nèi)建命令多了以后代碼別寫成一大串if-else。用表驅(qū)動的方法更清爽struct builtin_t { char *name; int (*func)(char **args); }; struct builtin_t builtins[] { {cd, mysh_cd}, {exit, mysh_exit}, {echo, mysh_echo}, {help, mysh_help}, // 可以自己加一個幫助命令 {NULL, NULL} }; int is_builtin(char *cmd) { for (int i 0; builtins[i].name ! NULL; i) { if (strcmp(cmd, builtins[i].name) 0) return 1; } return 0; } int run_builtin(char **args) { for (int i 0; builtins[i].name ! NULL; i) { if (strcmp(args[0], builtins[i].name) 0) { return builtins[i].func(args); } } fprintf(stderr, mysh: %s: not a builtin\n, args[0]); return 1; }以后想加pwd、export、history各自寫個函數(shù)然后在表里加一行就行主循環(huán)代碼一行不用改。代碼要寫到“加功能比加分支更容易”的程度這才叫好的項目結(jié)構(gòu)。5. 重定向與管道讓命令真正組合起來的Unix哲學(xué)外殼跑通了但現(xiàn)在的Shell是“單命令執(zhí)行器”。你沒法寫出ls out.txt也沒法寫出cat file | grep foo。這兩種操作涉及操作系統(tǒng)里最核心的一個概念文件描述符。5.1 文件描述符與重定向的底層原理一切都是文件Linux下“一切都是文件”進程通過文件描述符整數(shù)編號訪問文件。0號是標(biāo)準(zhǔn)輸入1號是標(biāo)準(zhǔn)輸出2號是標(biāo)準(zhǔn)錯誤。鍵盤輸入、屏幕輸出本質(zhì)上都是通過這幾個文件描述符完成的。ls out.txt的意思就是把1號文件描述符指向的文件換成out.txt。這樣ls寫往標(biāo)準(zhǔn)輸出的內(nèi)容就進了文件。實現(xiàn)用的系統(tǒng)調(diào)用是dup2()int fd open(out.txt, O_WRONLY | O_CREAT | O_TRUNC, 0644); dup2(fd, STDOUT_FILENO); // 把標(biāo)準(zhǔn)輸出重定向到 fd close(fd);dup2(fd, STDOUT_FILENO)的意思是“把fd復(fù)制到1號位置”即讓1號描述符指向fd指向的那個打開文件描述。之后printf輸出的內(nèi)容就去文件里了。注意dup2()之后必須close(fd)——因為1號和fd都指向同一個文件表項你只需要保留1號就夠了多一個引用只會造成資源泄漏。5.2 支持、、的解析三個運算符三種打開方式修改解析邏輯的步驟掃描args數(shù)組看有沒有、、或2之類的標(biāo)記標(biāo)記后面的那個參數(shù)就是文件名在子進程執(zhí)行execvp()之前先打開文件調(diào)用dup2()重定向然后執(zhí)行命令args里要把重定向操作符和文件名剔除只留下真正的命令參數(shù)。實現(xiàn)上我建議在run_external()里增加一個預(yù)處理步驟void run_external(char **args) { pid_t pid fork(); if (pid 0) { // 子進程 for (int i 0; args[i] ! NULL; i) { if (strcmp(args[i], ) 0) { int fd open(args[i1], O_WRONLY | O_CREAT | O_TRUNC, 0644); dup2(fd, STDOUT_FILENO); close(fd); args[i] NULL; // 截斷命令參數(shù) break; } // 同理處理 和 } execvp(args[0], args); perror(execvp); exit(EXIT_FAILURE); } // 父進程照舊 }有一個必須想清楚的細節(jié)為什么重定向在子進程里做而不在父進程里做因為在父進程里改文件描述符會影響到下一輪主循環(huán)而且重定向是“這一次命令”的行為不是Shell自己的行為。所以必須在fork()之后、execvp()之前在子進程內(nèi)部完成重定向——此時子進程有獨立的文件描述符表隨便改都不影響父進程Shell。用O_RDONLY用O_WRONLY | O_CREAT | O_APPEND。三個操作符映射到三種打開方式記住這個表就行。5.3 管道的實現(xiàn)|操作符的本質(zhì)是把一個進程的輸出接到另一個進程的輸入管道比重定向更微妙。ls | grep txt的邏輯是創(chuàng)建管道pipe()系統(tǒng)調(diào)用得到一個讀端和一個寫端ls的標(biāo)準(zhǔn)輸出重定向到寫端grep的標(biāo)準(zhǔn)輸入重定向到讀端兩個進程可以同時運行數(shù)據(jù)從ls流往grep。代碼實現(xiàn)void run_pipeline(char **left_args, char **right_args) { int pipefd[2]; pipe(pipefd); // pipefd[0] 讀端, pipefd[1] 寫端 pid_t p1 fork(); if (p1 0) { // 左命令輸出到寫端 close(pipefd[0]); // 用不到讀端 dup2(pipefd[1], STDOUT_FILENO); close(pipefd[1]); execvp(left_args[0], left_args); perror(execvp left); exit(EXIT_FAILURE); } pid_t p2 fork(); if (p2 0) { // 右命令從讀端讀入 close(pipefd[1]); // 用不到寫端 dup2(pipefd[0], STDIN_FILENO); close(pipefd[0]); execvp(right_args[0], right_args); perror(execvp right); exit(EXIT_FAILURE); } // 父進程關(guān)閉兩端 close(pipefd[0]); close(pipefd[1]); waitpid(p1, NULL, 0); waitpid(p2, NULL, 0); }這里有幾個反直覺的點我當(dāng)年全踩過第一父子進程必須關(guān)掉沒用的一端。假設(shè)ls進程沒關(guān)讀端grep進程沒關(guān)寫端會發(fā)生什么管道還有“活著的引用”grep等待輸入時系統(tǒng)不會發(fā)EOF于是grep永遠等下去整個管道掛死。必須讓管道兩端的進程都關(guān)掉不需要的那一端才能保證數(shù)據(jù)流結(jié)束時有EOF信號。第二管道和重定向可以疊加嗎可以但順序很重要。ls | grep foo out.txt的結(jié)構(gòu)是“左命令輸出進管道右命令的輸出進文件”。實現(xiàn)時先按管道創(chuàng)建子進程然后在右子進程里先做標(biāo)準(zhǔn)輸入的重定向管道讀端再處理重定向。這個組合邏輯會越寫越復(fù)雜所以更優(yōu)雅的方案是逐步擴展成“命令表”command table結(jié)構(gòu)這里先不展開。第三管道的進程是同時還是先后創(chuàng)建記住一個原則先創(chuàng)建所有進程再等待所有進程。不要讓前一個進程先跑完再創(chuàng)建下一個——那樣的話數(shù)據(jù)還沒流到就關(guān)閉了而且會復(fù)制兩次fork()的環(huán)境效率更低。上面代碼先fork()左命令再fork()右命令兩個子進程都建好之后父進程統(tǒng)一waitpid()。5.4 關(guān)注點轉(zhuǎn)移管道實現(xiàn)中容易忽略的“僵尸進程”問題寫管道的時候有個概念你一定會遇到waitpid()的第二個參數(shù)status。我們說父進程“必須”調(diào)用waitpid()等待子進程但如果子進程是管道的一端你可能不知道應(yīng)該等哪一個先結(jié)束。更隱蔽的問題如果父進程在waitpid(p1)上阻塞而p2已經(jīng)退出p2就變成了僵尸進程——它占著PID和進程表項只有父進程waitpid()收割它系統(tǒng)才會徹底回收。長期積累僵尸進程會耗盡系統(tǒng)進程表。所以更穩(wěn)妥的寫法是waitpid(p1, NULL, 0); waitpid(p2, NULL, 0);兩個都等待確保一個都不漏?;蛘哂脀ait(NULL)交替收割。這個細節(jié)在生產(chǎn)環(huán)境的Shell里是必須處理的但大多數(shù)教學(xué)代碼都會略過——我在這兒寫出來是希望你在做項目的時候主動去碰一碰這個問題。6. 交互體驗與常見坑提示符、輸入緩沖、CtrlC、歷史記錄項目做到這里已經(jīng)是個能用的Shell了。接下來這一節(jié)是純“從實戰(zhàn)中踩出來的經(jīng)驗”你在任何教科書上都不太容易湊齊這些細節(jié)但平時敲命令一定會遇到。6.1 提示符的學(xué)問交互模式下的fflush和顏色交互式Shell的提示符必須立即顯示。如果你用printf(mysh )標(biāo)準(zhǔn)輸出默認(rèn)是行緩沖的——只有當(dāng)遇到換行符或緩沖區(qū)滿才真正寫出去。提示符后面沒有換行所以你不fflush(stdout)的話用戶看到的就是一個干瞪眼的空白終端敲了命令才有反應(yīng)。這個問題的表現(xiàn)很反直覺按回車之后提示符才出現(xiàn)又跟著你剛敲的命令終端顯示是亂序的。我第一版Shell就載在這里排查了半天還以為是多線程問題結(jié)果就是少了一行fflush。順帶一提給提示符加顏色會讓Shell看起來更友好但要注意非交互模式下不要用顏色。判斷方法很簡單Shell啟動時看isatty(STDIN_FILENO)是否為真。文件輸入或管道輸入時用了顏色反而要命。生產(chǎn)級Shell如Bash就是這么做的。6.2 信號處理為什么按CtrlC會退出你的Shell這是另一個大坑。默認(rèn)情況下CtrlC會向前臺進程組發(fā)送SIGINT信號默認(rèn)動作是終止進程。你的Shell如果沒做任何處理用戶在終端按CtrlCShell進程本身就會掛掉。你手寫的Shell必須自己處理SIGINT#include signal.h void handle_sigint(int sig) { printf(\n); fflush(stdout); } int main() { signal(SIGINT, handle_sigint); // ... 主循環(huán) }這樣用戶按CtrlC時Shell只是換個行、重新打印提示符而不是整個進程被殺掉。但注意子進程執(zhí)行期間按CtrlC信號會發(fā)給整個前臺進程組。你的Shell和子進程都在前臺需要決定是Shell處理還是子進程處理。更精細的做法是Shell忽略SIGINT而子進程保留默認(rèn)行為。所以signal(SIGINT, SIG_IGN)放在 fork 之前的父進程里然后 fork 出來的子進程里再signal(SIGINT, SIG_DFL)恢復(fù)默認(rèn)。這個細節(jié)跟真實Shell的行為一致你在Bash里跑sleep 10按CtrlC能殺掉sleep但Bash還活著。這里面的機制跑通之后你會對“進程組”和“信號傳遞”有更扎實的理解。6.3 歷史記錄用readline庫還是自己擼到了這一節(jié)你已經(jīng)實現(xiàn)了一個可以用于基本命令執(zhí)行、重定向、管道的Shell。如果還想加“上下鍵”翻歷史命令純粹靠自己寫會非常麻煩——因為全終端控制terminal raw mode的細節(jié)比你想的多得多。兩個選擇選擇一用 GNUreadline庫。這是最實際的選擇。它提供了readline()函數(shù)替代getline()并且自帶歷史記錄功能。代碼從char *line readline(mysh ); add_history(line);兩行就完成了。優(yōu)點顯而易見缺點是這個庫要額外鏈接依賴。教學(xué)演示項目可以不用靠近生產(chǎn)項目時值得引入。選擇二自己實現(xiàn)簡單的歷史記錄。如果你想鍛煉文件操作能力可以用一個數(shù)組存最近N條命令上下鍵通過終端原始模式讀入按鍵序列。這條路適合練手但“終端raw mode”會花掉你不少時間。我建議先把前述核心功能寫好、跑通歷史記錄用簡單數(shù)組版本即可——不要一上來就擼終端控制。6.4 一個容易被忽略的需求處理空命令和注釋符空行、只含空格的行、以#開頭的行應(yīng)該被Shell跳過。這在腳本執(zhí)行場景下尤其重要因為腳本文件里經(jīng)常有空行和以#開頭的注釋。第一版可以先忽略但主循環(huán)里面對args[0] NULL的情況要直接continue。還有個細節(jié)把Shell的“退出碼”返回給操作系統(tǒng)。exit命令后你可以echo $?驗證退出碼是否正確。在主循環(huán)里每次執(zhí)行完外部命令從status里提取退出碼WEXITSTATUS(status)存到一個全局變量last_status里。后面實現(xiàn)、||或者$?變量時就靠這個值。7. 一個值得再走一遍的擴展清單這個項目能長成什么樣寫完核心Shell之后你可以繼續(xù)往這幾個方向擴展。這些擴展每一個都能單獨成文而且會逼你解鎖更多Linux機制。1. 通配符展開ls *.txt是怎么變成ls a.txt b.txt的你需要理解Shell的glob展開機制可以自己調(diào)用glob()函數(shù)實現(xiàn)也可以自己寫遞歸匹配。2. 環(huán)境變量賦值前綴VARvalue command這種寫法需要臨時修改子進程的環(huán)境變量。用setenv()在子進程內(nèi)部改但注意父進程環(huán)境不能變。3.$?特殊變量獲取上一條命令的退出碼。這個我們上面提過是通往條件執(zhí)行、||的第一塊磚。4. 作業(yè)控制CtrlZ暫停進程、jobs列出后臺任務(wù)、fg恢復(fù)前臺運行。這會把高深的前后臺進程組管理引入項目——做完包你理解“作業(yè)控制”是什么。5. 腳本執(zhí)行模式mysh script.sh從文件讀取命令執(zhí)行。這個做起來不難把getline的輸入源換成文件但會觸發(fā)一堆“是否打印提示符”等邊界判斷——跟真實Shell一致非交互模式不打印提示符。6. 引號處理完整支持單引號和雙引號。單引號內(nèi)一切字符按字面意義處理雙引號內(nèi)做變量替換。這需要把簡單的strsep解析器升級成帶狀態(tài)機的解析器。做完你就會明白為什么Shell的解析器一直是“復(fù)雜主題”。每做完一個擴展建議你做一個小對比用自己的Shell跑ls -l /tmp再去Bash里跑同樣的命令看看行為差異有多大。你會感受到從“模擬”到“理解”之間的距離。最后說一點個人體會。磨這個項目的時候我最深的感觸不是“哦原來Shell是這么工作的”而是“需要一個好用的測試工具”——自己寫Shell意味著你手里的每個命令都是被自己實現(xiàn)的代碼執(zhí)行的。當(dāng)你發(fā)現(xiàn)自己寫的Shell居然能正確跑grep -r hello /usr/include | wc -l時那種“操作系統(tǒng)的拼圖在自己手里一塊塊拼上”的感覺特別踏實。而且這是一個適合長期維護的小項目。它不會太大但每次往里加一個功能、處理一個bug都能讓你對Linux的理解再深一圈。過三個月拿出來看看你會發(fā)現(xiàn)自己寫的代碼其實菜得不行——那恰恰說明你成長了。建議從今天開始動手先把3.3小節(jié)的parse_command()跑起來剩下的自然就有動力磨下去了。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
亚欧国产无码精品在线| 操屄不卡视频| 性一交一乱一交A片久久四色| 欧美性爱免费短视频| 男女无套 免费网站| 欧美色图另类图片| 欧洲人妻视频| 操逼日批| 日本免费中文一区二区三区四区| 99免费视频| 大香蕉黄色一级片免费看| 亚洲色图A| 国产黑白丝在线| 另类图片天天影视| 激情专区综合| ss久久| 久偷拍| 久久久久ab| 毛片麻豆91糖心精品毛情片| 亚洲色图91| 日本成人电影资源网| 婷色五月天| 日韩9999| 3d成人精品一区二区| 国产亚洲色婷婷久久99精品91| 欧美一区二区情色| 97在线精品观看视频| 熟女乱伦二区| 97国产|免费| 天天干天天燥| 亚洲精品 超碰| 三上悠亚在线毛片91| 人人考人人摸人人干| 屌妞视频久久久久久久| 超碰97人妻免费在线| 日本精品88888888| 免费观看网黄| 粉嫩久久久久| 亚洲综合五月天| 日韩不卡一二三四| 国产精品成人无码av| 亚洲 欧美 综合 91| 精品久久人妻成人网| 67914在线兔费成人视频| 2020中文在线一区二区三区| 97精品视频在线播放| 亚欧美天堂在线| 第四色奇米影视777| 天天影视网色欲色香| 91人妻中文| 91在线国产后入风骚翘臀美女素人| 手机看片日韩人妻| 1204人成网站色www| 久久ww| 操逼www.| 国内精品嫩模A∨私拍小视频| 78p欧美| 国产精品自拍xxxx| 宅男影院久久久,99| 另类老少妇| 熟女色图在线| 91人妻丝袜无码| 超碰在线香蕉| 91精品久久久| 伦激情人妻另类人妻| 99精品久久久久久| 西西美女视频网| 极品色社| 熟妇亚洲一区二区三区| 人妻激情偷乱视频一区二区三区| 婷婷六月色开| 九九综合| 97色五月天完| 97色插| 国产九月婷婷| 国产亚洲人妻综合日韩 久久| 亚洲av综合色区无码一| 国产久久av| 成年人网站在线免费观看| 高清视频一区| 亚欧成人一级片在线播放| av 模特一区了| 久久亚洲熟妇在线视频| 国产精品久久久午夜夜伦鲁鲁| 欧美亚洲厕所精品偷拍91| 嗯啊不要在线观看嗯啊| 一区二区三区日韩欧美| 97 九色| 美女大乳久久久久久久女人18| 校园春色之综合网| 亚洲日韩XXX| 精品免费国产二区三区| 91 国产丝袜在线播放-百度| 99操逼| 久久日韩肥臀| 日本一级特级毛片视频| 91丨熟女丨丰满熟女| 亚洲一区二区三区欧美日韩| 国产精品女同| 久久精品人妻一区二区三区| 国产黄色视频久久| 亚洲成人综合在线| 欧美少妇大量自拍视频在线观看| 欧美少妇高潮久久91| 国产97av| 国产精品久久久久av| 91free福利| 91黑丝在线播放| 精品乱码在线观看| 亚洲无码国产精品久久| 午夜天天碰综合视频| 久久是精品| 国内精品久久人妻性色av| www久| 日本精品久久久久久久| 懂色中文一区二区三区| 欧美综合骚| 欧美亚洲高清晰| av无码av无码专区| 97超色| 91久久久久久久久18| 亚洲十八禁止| 丝袜六区| 视频不卡中文字幕| 丁香五月婷婷基地| 操国产高清| 亚洲情色五月天 | 黄片aaaaa一区| 亚码人妻| 97人妻碰碰中文无码久热丝袜| 97se综合| 综合色播| 91精品国产日韩欧美综合| 婷婷情色五月天| 日韩激情啪啪啪| 亚洲日韩东京热一区| 国产熟女乱论| 操碰97| 日本99一区二区| 在线欧美69V免费观看视频| 久久久神马影院| a在线视频免费观看| 91另类| 亚洲色图欧美另类在线| 激情丁香五月婷婷| 综合久久久久久久综合网| 国产午夜视频| 久久综合18p| 都市久久精品激情亚洲| 国产精品福利资源在线尤物| 玖玖久久久| 不卡码视频| 国产大学生高潮在线播放| 婷婷操视频| 国产情色第一第二页在线观看| 簧片免费看视频| 国产亚洲精品A在线观看下载| 人妻熟女一区二区在线视频| yazhououmeizongya| 欧美老妇综合网| 成人日韩欧美| 97婷婷色| 日本福利社| 蜜乳AV一区二区三区四| 免费福利视频中文字幕| 91丨九色丨国产丨人妻在线| 日本中文字幕不卡视频| 精品人妻中文字幕高清| 操国产高清| 男人天堂东京热| 91欧美少妇| 99色骚| 亚州欧美综合| 中文无线日韩一区| 欧美成97爱| 欧美色97| 亚洲自拍偷拍视频在线| 嫩草影院在线观看精品| 中文字幕一区二区日韩网| 香蕉热人人精品| 久久毛卡| julia高潮后不停追击中出| 九九视品黄色| 日本欧美m v精品网站加| 婷婷15月天青娱乐| 偷拍视频青青草在线视频| 久热一区二区| 插穴性爱视频在线观看| 天美传媒国产原创中文字幕亚洲欧美另类 | 强奸乱伦麻豆| 性欧美另类高清| 一区 欧美 日韩 麻豆| 超碰午夜| 自拍内地三级在线观看| 91粉芽高清在线一区二区| 嗯阿好爽好紧| 精品人妻一区二区三区不卡断| 韩国手机不卡无码三级视频| 91原创在线观看| 97久久天天综合色天天综合色电影| 亚洲色图日韩丝袜制服一区二区五月在线| 婷婷五月天无码| 欧美一级黄片免费播放| 少妇综合网| 亚洲激情久久久伊人综合| 另类av综合久久| 韩国轻伦国内自拍一区| 精品v1区| 一区麻豆 高清中文字幕| 亚洲欧美精品91| 91丨九色丨熟女高潮| 日本一区二区做爱的视频| 免费a级毛片av无码久久精品中文字幕| 神马麻豆福利院| 午夜亚洲| 欧美翘臀视频网站一区二区三区| dy888午夜老子影视达达兔| 91热| 欧美黑人极品高潮喷吹熟女黑人性暴力日韩在线欧美极品一区二区老师黑人潮喷一 | 91欧美美女日韩国产婷婷| 日本精品国产视频| 97超碰人操| 久久久精品一区二区| 亚欧洲一区二区视频| 中文字幕免费看| 人人妻天天做天天爽| 精品二区三四区五电影 | 欧美三级不卡| 2017超碰| 亚洲欧美清纯| 五月丁香六月综合缴清无码 | 久久五月天婷婷丁香中文字幕| 免费看黄片现成| 青娱乐欧美激情一区二区| 黄色片一区二区三区四区五区 | 欧美色日本| 1.igao73.com 加入收藏 免费专区 国产精品 中文字幕 日韩精品 欧美精品 精彩 | 国产亚洲精品第一最新| 婷婷中文字幕| 欧美 亚洲 大香| 天天日美女的B| 51一区二区三区| 欧美翘臀视频网站一区二区三区| 无码高清少妇久久| 日本九九九九| 天堂在线一区二区| 91日韩国产欧美亚洲另类精盘州至城都| 97超碰美女| 色综合久久av| 2011国产精品| 插B在线观看| 亚洲精品国产精品成人| 激情小说成人日本无码一| 六月丁香啪啪| 后入福利视频| 天天做天天爱夜夜爽毛片试看| 91成人久久| 夜夜一区二区| 懂色影视久久| 欧美成人AⅤ大片在线观看| 久热九九| 国产精品久久久久久9999| 久9精品| 秋霞久久亚洲精品成人| 先锋精品av色鲁| 色娱乐色呦呦夜夜夜夜av| 亚洲天堂2020| 天天综合91在线| 国产精品一级二级在线| 中文字幕乱在线伦视频中文字幕乱码在线 | 五十路六十路七十路熟婆| 欧美顶级黄片AAAAA在线免费看| 91精品电影18| 日韩猛交| 91久久久久久| 蜜桃午夜视频一区二区 | 欧美日韩人人精品| 88xx成人精品视频| 久久精品亚洲婷婷| 青青青在线高清视频在线一二三四区| 国产精品婬乱一级毛片彝族| 国产午夜视频| 亚洲性爱电影| 亚洲日韩青青草色月| 久久午夜神马| 亚码激情| 思思热一热婷婷热一热| 人人摸人人添人人操| 欧美永久激情一区二区| 亚洲性爱电影| 亚洲吊色| jizzjizz欧美| 91制服丝袜| 亚洲中文字幕网| 国产中文字幕在线| 亚洲色诱惑| 中出91视频| 亚洲欧洲网站免费观看| 日韩大香蕉AV影片| 五月天激情网站| 78精品| 久久丁香| 夜夜骑夜夜操| 久久国产精品,久久国产| 精品国模无码| 在线五区| 91精品成人www| 国产一国产一级毛片古装| 91人人爽人人爽人人人,gav福利视频导航,日韩欧美亚洲国产字幕四区 | 男女香蕉一区二区| 婷婷色综合| 亚洲风情在线观看| 欧美熟妇精品黑人巨大一二三区| 一二三四视频中文字幕在线看| 91久久伊人婷婷青青草| 中国大陆国产高清AⅤ毛片| 91在线/欧洲| 日本性爱网址| 懂色Av一区二区三区| 天综合中文| 国产精品永久免费10000| 97日视频| 欧洲色色| 日本免费中文字幕在线| 精品无人区麻豆乱码1区2区图片| 美女黄页| 精品在线观看视频在线| 又大又大又大又粗爽高潮观看| 另类av天堂| 日本高清_区二区三区| 9精品久久久久| 亚洲天堂人妻一区二区| 亚洲中文一区二区三区| 男人的天堂VA| 色娱乐色呦呦夜夜夜夜av| 深田咏美亚洲精品福利社| 日本久久超碰| 久操av在线| 日本久久久久久久久| 久久国产乱子伦精品免费女人| 亚洲脚交| A啊啊在线观看| 无码少妇精品一区二区60岁老人| 都市久久精品激情亚洲| 欧美激情区| 中文字幕视频免费| 色穴精品| 999久久久精品国产| 日韩精品碰碰| 操逼操2| 欧美极品少妇| 欧美色图成人网一区二区 | 久久久婷| 无码WWW免费视频网站| 精品999一区二区| 精品少妇人妻| 综合激情一一91| 嗯嗯啊啊用力视频免费| 小草三级久久观看| 五月色综合| 人人摸人人叼| 亚洲aV无码成人在线观看| 成人精品久久久午夜福利| 久久岛国| 日韩99999| 囯产精品强| 亚洲蜜臀懂色| 嫩草 人人网精品| 国产精品天美传媒| 一本一道人妻久久一区二区三区| 色播丁香| 色爱综合网| 久久久无码av精| 成人a大片在线观看| 国产精品视频内谢女人| 97亚洲在线| 午夜啊啊| 亚洲欧美成人网站AAA| 久久大黄片| 成人性交午夜免费片| 欧美啪啪天堂| 欧美精品激情| 搡老熟女免费视频 | 性在久久久久久| 成年人三级黄色片视频| 在线无码网站| 看大黄色大片原件| 人人操人人狠狠操| 伊人AAA| 中文字幕 码 自拍 视频 区| 日韩人妻精品久久久久| 9久久9综合| 97香蕉人人乳| 天天躁夜夜躁狠狠躁AV| 97亚洲在线| 校园春色综合网| 很很操在线| 日本熟女不卡视频| 男人高清无码一区二区| 久久五月丁香| 天美麻豆精品视频99| 日韩99999色| 久久綜合很很很| 小草av不卡亚洲二区| 久久久国产精品亚洲精品| 九一国产精品| 9丨久久九九九| 精品日韩人妻视频| 欧美性爱一级操| 深爱激情五月天| 91女网站| 欧美黑人168页欧美黑人167| 亚洲色人阁| 亚洲成人黄色在线观看| 91丨熟女丨丰满熟女| 色情综合网| 67194无码不卡| 97干综合网| 一区二区视频在看| 操B视频日韩无码| 欧美国产伊人久久久久| 国产欧美精品日韩区二区麻豆天美| 97色爱| 久久久78| 大香蕉一区二区在线观看.| 天天色综亚洲91污| 国产精品91ai| 亚洲图片91| 中文字幕日韩精品久久| 色香蕉影院| 少妇人妻无码| 亚州综合色| 曰韩操B| 国产11页| 99热99re6国产在线播放| 97在线观看| 国产精品岛国片在线观看| 久久精品欧美一区蜜桃| 素人伊尹大香蕉免费下载视频| 五月婷亚洲精品天堂| 精品性爱一二三区| 嗯嗯啊啊好大好爽| 91在线视频国产网站| 香蕉人人操tv| 青青草乱入乱欲视频在线观看| 欧美熟妇精品黑人巨大91| 91社操逼| 成人十八禁日韩欧美一二三| 综合97| 激情五月综合| 丝袜加勒比| 岛国免费黄色网址| 亚洲av总站| 97这里有精品| 99久久婷婷| 亚洲伊人青青草| 探花在线免费观看视频国产一区| 国产农村妇女精品1区二区| 欧美啪啪啪91| 免费AV播放| 五十路熟女,国产欧美精品区一区二区三区| 超碰97人妻免费在线| 天天爽天天| 国产精品久久久午夜夜伦鲁鲁| 激情抓乳插进去啪啪啪日韩| 99re不伦| 久久精品久久久久久久久| 人人喜人人妻| 国产在线综合网| 插入综合网| 日本天天干天天操一区| 久久久爆乳翘臀一线天伦理视频| AV有码在线| www.超碰| 1769成人国产精品视频| 9超碰免费| 精品国产91内射久久| 综合网久久| 国产精品熟女九色九色蜜臀| 欧美 综合 亚洲| 亚洲五码一区二区三区| 国产九九九九九九| 国产久久久久久久久一区二区| 久久禁| www.夜夜| 高清无码久操视频| 国产精品视频在线播放 | 91日日| 亚洲精品xxx| 人妻熟女一区二区| 大香蕉伊人久久| 国产精品久久久久久久久久久久久久吹 | 黄色大片一区二区密桃丝袜| 天天射天天操天天干天天吃2018| 天堂涩涩| 97干在线视频| 久久精品国产亚洲AV清纯| 五月婷婷hd| 亚洲无码?第一页| 国产精品对白自产拍| 日韩一区二区精品视频| 黑人精品XXX一区一二区| 国产精品熟妇一区二区三| 国产精品无码av| 亚洲色棕合| 在线观看一卡二卡| 国产男女边吃边摸视频网站| 欧美亚洲清纯| 国内外色色色色色成人视频| 中日韩免费看男女操逼大全| 91青青在线视频| 五月婷婷丁香六月| 亚洲图片偷拍欧美| 国产一区二区三区精品观看啪| 97超碰国产亚洲精品| 精品一区二区成人| 久久久人妻| 美女啪欧美一区| 国产亚洲禁久一区二区| 97碰久久| 亚洲双插| 91网亚洲| 色色色综合| 日韩色欲久久一二三四区| 抽插一区二区视频| 麻豆AV96熟妇人妻| 91人人爽人人爽人人人,gav福利视频导航,日韩欧美亚洲国产字幕四区 | 九九九九久久久| 黄色电影在线播放综合网站 | 操碰97| 最新亚洲黄色免费电影| 国产一区二区久久| 午夜精品久久久久久久男人的天堂| 欧美亚洲素人制服精品| 久久这里只有精品9| 亚洲综合性网址| 玖色AV| 亚洲综合九九| 伊人网综合在线视频| 九九九久| 4虎在线视频| 97超碰天天爱天天爱| 天天流夜夜操| 性爱1区| 久久成人国产精品| 亚洲影院365| 亚洲中文字幕熟女少妇一区二区| 亚洲国产亚洲天堂| 欧美日韩亚洲五月天婷婷| 欧美后进式| 狠狠穞A片一區二區三區| 亚洲欧美日韩免费电影| 老熟女乱伦片| 成人a v在线播放免费| 搡老女人老91二区| 欧美精品庄| 亚洲九九视频| 呻吟 欧美 日本 中出| 98精品国产乱码久久久久久| 欧亚第一综合网| 欧美狠狠操| AV天堂电影网| AV色天香在线| 亚洲欧洲精品成人| 蜜臀在线免费观看在线免费观看| 黄色成品网站| 嫩草 人人网精品| WWW4虎| 综合网久久| 欧美中文字幕一区 | 99热超碰| 久操大香蕉手机视频在线看| 热久久精品| 青青草原人妻| 九九热超碰97亚洲最新香蕉 | 观看视频图片一区二区三区| 青青操97| 97在线欧洲| 加勒比海成人视频网 | AAAAAAAAA黄片| 欧美一级特黄淫片在线观看| 探花一区在线| 亚洲成人免费电影| 992这里有精品| 超碰公开久久网| 欧美亚洲丝袜美女电影| 久久久久921| 后入福利| 女人香蕉久久毛毛片精品| 啊啊啊啊好爽好舒服一区二区易域| 91高潮| 天天色黄色影院天天操| 日本欧美亚洲高清在线看| 多乙久久久久久| 亚洲天堂男人网| 国产女大学生AV| 蜜乳AV.COM| 黄页视频网站野外| 99综合网| 欧亚日韩三区| 一区二区三区一亚洲中文字幕、综合区灬| 欧美亚州综合网图片| 2021国产成人精品久久| av绯色| 超碰免费在线| 久草在| 91中文字幕制服丝袜免费视频| 免费av高清无码| 亚洲色 国产 欧美 日韩| 91色久| 亚洲永久AV无码精品秋霞| 日本精品九九九| 天天视频黄| 日本三级R| 欧美激情视频一区二区三区不卡| 久久亚洲日韩熟女精品| 久久精品国产亚洲妲己影视| 青青草一本道福利视频| 丁香六月激情| 婷婷激情五月综合| 艾草av| 亚洲欧美综合网站| 久久久久亚洲熟妇熟女| 天天摸夜夜添无码小视频| 13小男生GAY自慰脱裤子| 牛牛aV| 日本天天人人狠狠在线日美女| 岛国999| 国产精品久久aV| 综合色99| wuyechaopeng| 黄片免费视频2019| 一本一道人妻久久一区二区三区 | 日本Suv精品一区二区| 国产Av超碰| 蜜臀av在线播放一区二区三区| 亚州国产成人精品女人久久| 欧美A片中文字幕| 日韩不卡毛片Av免费高清| 超碰人妻中文在线| 一二三区操逼国产91| 99热精品在线| 老熟乱一区二区三区四区| 国产一级舔足在线观看| 超碰超碰欧美| 热热色91| 日本久操视频| 中国少妇啪啪视频| 超碰人人妻| 天堂亚洲精品| 欧美v亚洲v日韩v最新在线二区| 精品无码久久久久| 在线岛国新天堂8| 色 亚洲 91| 强奸抽插av| 91夜色chaopeng| 26uuu最新| 97国产色综合| 思思热在线视频免费| 人妻久热在线| 综合影院永久入口国产| 淫色网综合| av在线播放国产一区| 久久这里精品国产99丫e6| 色姑娘综合网| 美女91在线观看| 最新岛国大片| 骚妻少妇精品性色无码四色A V| 欧美日韩亚洲一区二区在线观看| 超碰538| 99热只有这里有精品| 91N综合网在线| 人人操av| 色牛aV| 亚洲大胆人体av| 在线观看亚洲专区| 99草精| 青娱乐av在线| 精品国产一区二区三区香蕉欧美| 91网站在线播放| 97在线精品| 无码人妻精品一区二区三区99不卡 | 人妻日日干| 久久是精品| 极品五月天噜噜| 黄片qw| 91超碰在线观看| 五月天色图影视| 免费少妇一区二区| 久久久久久久久久久久欧美日| 啪啪啪精品视频| 美女久久久| 一区| 欧美制服网站美腿丝袜| 国产高清精品一区二区三区毛片 | 日韩欧美麻豆 | 亚洲国产天堂| 一区| 不卡在线一区,精品一区二区三区中| 91东北熟女| 91欧美综合在线| 96精品在线| 波多野结衣一级视频| 久久久青青草| 清清草影| 欧美精品成人亚洲| 人人操,操人人| 十八禁黄色| 97在线欧洲| 久久婷婷五月天| 久久亚洲AV成人精品无码| 黄页av| 日本国产二线女色| 丰满搜索结果 -第18页- 久久高清无码| 日韩一级片在线看| 9丨久久九九九| 天天做天天爱天天爽AV| 琪琪精品免费一区二区三区| 午夜啊啊| 亚州Av天美传媒| 人妻无码久久一区二区三区免费| 五月天社区| 日本东京热加勒比久久| 久草精品国产99| 丝袜美腿91| 97在线欧| 天天看高清麻豆| 麻豆人妻少妇在线免费观看| 久操大香蕉手机视频在线看 | 成人精品视频| 精品人妻视频一区二区三区蜜桃视频| 六月丁丁香| 日本精品高清一二区一本到| 91搞逼视频| 88xx成人精品视频| 97碰久久| 亚洲交性| 亚洲人妻久久| 78精品在线| 色路综合| 97精品视频在线| 免费国产| 欧美偷偷网| 亚州欧美总和| 久久国语| 色综合久久夜色精品国产天堂| 91精品人妻一区二区三区蜜桃臀| 亚洲色图久久精品蜜| 国产精品一二三区福利| 婷婷五月天网| 亚洲午夜免费狠狠干| 丁香五月天堂网| 国产精品岛国片在线观看| 欧美精品一区二区少妇免费A片| 精品国产av一区二区三区四区入口| 国产又黄又爽| 91网九色蝌蚪操熟女| 在线观看不卡一区二区三区| 精品久久久av无码免费| 亚洲高清无毛一区二区| 十八禁视频一区二区| 亚洲人妻在线一区| 免费伦费视频在线观看| 国产成人精品日本视频| 成人乱码一区二区三少妇| 91久久婷婷| 日韩精品一二三四| 狠狠狠狠狠| 91+欧美| 天天天天天天天天综合| 中文字幕av片| 南澳成人一级片在线播放| 91五十路| 涩综合导航| 日韩欧美午夜视频在线| 色色五月丁香| 色爱综合网欧美| 欧洲与亚洲欧美精品中文字幕| 国产一| 97在线免费视频观看| 色吧5亚洲| 中国一级操逼视频| 97久久超碰| 97超碰jingpin| 97精品97久久| 久久久啊啊啊| 插穴性爱视频在线观看| 国产18精品亚洲精品| 黄网在线播放| 大香蕉十区| 欧美一二三区四五区| 久久久久久无码人妻中文字幕| 日本不卡一二区| 岛国激情视频在线观看| 香蕉婷婷| 久操在97| 91性感在线| 伊人网综合在线视频| 色噜噜狠狠色综合日日| 国产乱码精品久久久久久| 67914在线兔费成人视频| 人妻一区二区三区四区视频| 人妻偷拍一区二区三区| 色丁香五月婷婷| 欧美 亚洲 制服 精品| 91强在线播放| 91蜜桃传媒精品久久久一区二区| 精品国模无码| 亚洲成人久久美女| 亚洲综合99999| 国产精品一区二区三区在线| 无码 黑人一区二区三区| 久久精品人体| 中文字幕色AV| 乱伦av麻豆| 色老汉色| 久久夜夜| 亚洲精品成人激情在线| 欧美 亚洲 综合 制服 另类| 九九九久久久| 男人天堂婷婷五月天校园春色| 久9久精品视频| www.色吧5.com| 91熟女熟妇视频网站| 亚洲综合草草| 国产一区自拍欧美日韩| 二区熟妇韩日| 天天肏天天干| 国产免费一区二区三区最新不卡| 亚洲综合69| 精品国产人成在线| 美欧老女人97| 欧美亚洲素人制服精品| 亚洲。日韩。欧美| 97超碰色| 最新欧洲欧美日本激情网站| 97在线观| 依人大香蕉| 日本女人久久久| 精品-91人妻子系列| 五月天AV资源| 久久一级无码精品毛片6| 性交一区二区在线播放| 91色狼| 免费看污网站| 好爽视频在线观看| 精品久久久久,69国产成人精| 成人天天爽| 操婷婷逼| 97精品97久久| aⅴ日韩成人电影av在线免费看av大全| 日韩三级性| 日韩少妇一区二区三区| 999热日韩精品| 成人精品在线| 九九热AV| 日韩精品人妻系列无码天堂| 亚洲av国产av综合av卡| 97爱欧美| 八人操人人摸人人看| 97超碰中文| 五月婷婷基地| 成人性交午夜免费片| 天天摸天天插天天日| 久久人妻| www.91欧美| 成人八戒网站| 欧美偷拍| 99.色网| 无码最新| 97久操| AV高清一区| 日韩综合成人免费视频| 亚洲性综合| 激情婷婷丁香| 亚洲 欧美 日韩 国产一区二区 | 男人的天堂VA| 国产精品天美传媒| 郑州宾馆老熟女露脸啪啪| 国产又长又大又粗的视频| www.激情| 日韩少妇无码| 青娱乐 成人娱乐在线| 老外又粗又长一晚做五次| 夜夜骑天天燥| 久久久A∨| 人妻干天天| 久久久极品| oumeizonghese,www| 久久久国产成人一区二区三区在线| 射综合网| 成人欧美日超碰| 丁香六月婷婷久久综合| 熟女乱3伦999| 97资源亚洲| 国产小炒后入式| 大香蕉免费中文| ..日韩av毛片精品久久久| 欧中美三级一区二区三区| 亚洲熟妇丝袜在线观看| 五月色网| 精品偷拍13p欧美dodk视频| 91亚洲网站| 成人精品水蜜桃久久久久久久| 又黄又爽在线观看视频 | 97精品网站| 97色论| 亚洲日韩精品在线播放| av绯色| 亚洲国产91精品一区二区久久| 99rre在线精品99re8| 噜噜噜亚洲精品| 黄色大片一区二区密桃丝袜| 亚欧性爱在线无码| 91午夜无码| 国产无马在线| 欧美 亚洲 第一页 | 中国少妇XXXX做受| 亚洲天堂精品日韩电影| 熟女熟妇一区二区三区视频| 一本一首道人妻少妇免费久久| 色婷婷激情| 美日韩成人| 人人操人人操草草| 再深点灬舒服灬太大了添视频| 亚洲阿v天堂在线| 亚洲欧美综合网 | 四虎午夜影院| 偷拍 亚洲 欧美| 久久超碰com| 97久久国产亚洲精品超碰热| 黑人精品久久97| 夂久色| 少妇 综合| 狠狠躁AV| 99热免费| 欧美少妇熟女| 综合网色| 久久亚洲不卡| 亚洲色五月| 野狼激情网| 9久久9综合| 青青草在线成人视频| 91青视频| 91亚洲不卡一区| 久久亚州高清| 人人操天天爽| 91国产美女丝袜足交精品视频| 久久精品72| 欧美97日韩| a片亚洲一本通视频| 91丝袜熟女| 国产熟码AV| 变态另类专区| 超碰91在线| 久久久久久久久久久免费精品| 人人干人人操人人爱| av网站国产主播在线| 中国91AV| 91丝袜视频在线观看| 91在线视频国产网站| 亚洲自拍偷拍视频在线| 超碰在线人妻中文字幕| 男人的天堂网页| 富女玩鸭子一级毛片| www.99中文字幕| 近亲乱伦一区二区| 色丁香五月婷婷| 天天干天天拍| 青青草白白色| 97伦综合| 91综合色| 亚洲欧洲精品视频发布| 午夜精品探花| 北条麻妃99精品青青久久| 九九精品热| 久久亚洲天天做| 91女优在线观看| 国产丁香精品露脸视频| 欧美一级黄色免费专区| 日韩美女啪啪一区| 超碰成人免费| 久久综合精品一区二区三区| 日本孕妇孕交| 中文字幕欧美丝袜07资源| 国产成人超碰在线| 久久草在线综合视频| 青青草视频爽一爽| 天天操天天日天天干| 97超碰欧美手机| 国产9 9在线 | 亚洲| 国产精品久久久久综合| 亚洲人妻一区二区三区| 97超碰久久色| 亚洲男人的天堂va亚洲男人社| 91色人妻| 精品人妻伦一二三区久久| 欧美综合自拍成人自拍第二十页| 精品美女少妇一区二区三区| 日韩综合色图| 狠狠操夜夜| 黄色二级片网站| asc国产精品| 香伊人在线| 男人的天堂亚洲| 亚洲资源网| 黄色免费一级在线毛片| 色一色综合网| 人妻少妇色综合| 中文AV制服乱伦| 爱我干综合| 亚洲综合69| 天天日天天操天天射河南省| 国产美女高潮叫床视频| 麻豆一区在线| 东北女人操逼| 色色婷婷五月| 亚洲AV乱码专区国产噜噜亚洲| 狠狠躁伊人中文字幕| 老司机老司机午夜影院| 日本一区二区电影网站| 男人的天堂com| 欧美日韩操逼嗦吊| 日韩人妻无码精品系列| 日本中文字幕不卡视频| 亚洲色啪| 超碰久久.com| 全球成人中文在线| 久久精品夜色国产亚洲AV| 欧美丝袜91| 日韩亚洲国产视频| 96AV久久久| 色吧5亚洲| 爱干爱射网啊啊啊| 高清无码人妻久久久一区二区三区aⅴ| 天天躁日日躁AAA片李宗瑞| 天天摸夜夜操视频| 欧美亚洲色图另类国产| 成人色女网| 精品国产Av无码久久久伦古装| 97久久精品亚洲| 日本精品五区| 翘臀vidoes| 日本淫乱女一区二区三区视频| 久久久久9| 欧美劲爆第一页| 尹人大香蕉视频在线| 夜夜久久久| av九九| 91 丝袜在线播放| 91久久久久免| 日韩兔费看黄片| 少妇蜜汁| 人人做天天爱| 99国内熟女露脸视频| 亚洲国产婷婷在线播放| 久久超碰av在线| 亚洲AV无码国产成人| 激情四射婷婷六月天| ?亚洲伊人伊成久久人综合网| 综合网欧| 日韩中文字幕精品一区在线| 精品无码一二三四区| 日本狂喷奶水在线播放212| 九九天堂| 98人妻精品一区二区色欲 | 东京热av男人的天堂| 中国AV美女| 91精品国久久久久久无码| 国产精品无码成人精品| 情色av电影| 成人无码在线视频网站| 久9久| 欧美一级久久久久久久大片动画| 91狠狠狠| 亚洲精品男人的天堂| se吧提供91精品国产91久久久久久| 九一精品牛牛一区二区| 一本一道波多野毛片中文在线| 日韩精品资源| 丝袜综合| 无码在线亚洲| 东京热一区二区中文字幕| 婷婷美人网| 久久九色| 丰满美女一级毛片在线播放| 精品女同一区| 3571色综合一区二区二区| 欧亚性爱在线视频| 男同专区一区二区三区在线| 伊色综合天堂色97| 亚洲黄色网址视频| 久久男人天堂| 亚州欧美总和| 99热97| 国内毛片免费h片在线| 亚洲欧美日韩偷拍色图| 精品9999| 色色色999| 国产精品999zyz| 亚洲色图欧美激情| 欧美午夜色妇色鬼| 97自拍视频在线| 国产av又色又爽又黄| 人妻丰满熟妇av无码区蜜桃| 久久綜合很很很| 五月丁香网站| 大香蕉黄色一级片免费看| 九九九九九九九九九国产精品| 97精选久久| 97资源超碰| 五月丁香色综合| 国产三级多多影院2022国产AA一级毛片无码| 欧美高清91| 亚州欧美综合| 91成人社区| 九九热精品免费视频| 黄网在线播放| 五月综合婷婷久久网站| 亚洲无码国产探花在线观看| 日日操夜夜操天天操免费观看麻豆| 国产亚热在线久久| 亚洲天堂区| 久久人人爽人人爽人人片Ⅴ| 欧美亚洲色图另类国产| 国产精品白丝www| 久久精品一区二区三区蜜桃臀| 日日夜夜草草草| 亚洲天堂另类美腿| 婷婷久久综合久| 亚洲欧洲国产综合av| 成年女人黄网站| 日本成人免费一区二区三区| 一区在线精品中文字幕| 久久人妻熟女一区二区| 日韩二区三四区五区六区在线看| 九九国产热| AV在线资源| 男人的天堂激情| 91N五十路| 一个人在线看的黄色电影网站| 国产少妇肉丝在线观看| 丁香成人五月天| 亚洲丝袜二区| 狠狠综合网| 夜夜操狠狠操| 天天日B狠狠操| 综合国产97| 国产精品交换一区二区| 人妻激情另类| 91大胆欧美| 天天爽天天| 97人肏| 欧美丝袜激情| 久热久一区二区三区| 日本一区二区三区精品| 上床不卡网站| 97丝袜亚洲在线播放| 高潮综合网| 狠狠操狠狠燥|