HW1 - Simple Shell - 作業系統概論 === [Kamigami55/NCTU-OS-course-labs](https://github.com/Kamigami55/NCTU-OS-course-labs/) ## 目錄 [TOC] ## 作業內容 C或C++皆可 ## 範例程式 ```clike= #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t pid; /* fork another process */ pid = fork(); if (pid < 0) { /* error occurred */ fprintf(stderr, "Fork Failed"); exit(-1); } else if (pid == 0) { /* child process */ execlp("/bin/ls", "ls", NULL); } else { /* parent process */ wait (NULL); printf ("Child Complete"); exit(0); } } ``` ## 系統相關function學習 ### fork() 須引入函式庫: ```clike #include <sys/types.h> #include <unistd.h> ``` fork()函式宣告: ```clike= pid_t fork(void); ``` **功能:** 呼叫時,會讓這支C process分支成兩個child process,同步執行 **回傳值:** 回傳型別為 **pid_t**,可用來辨別現在的process是parent process還是,child process。 是child的話,回傳值會是 **0** 是parent的話,回傳值會是 **child process的pid** **範例程式:** 截自 [linux c语言 fork() 和 exec 函数的简介和用法 - CSDN博客](http://blog.csdn.net/nvd11/article/details/8856278) ```clike= int fork_1(){ int childpid; int i; if (fork() == 0){ //child process for (i=1; i<=8; i++){ printf("This is child process\n"); } }else{ //parent process for(i=1; i<=8; i++){ printf("This is parent process\n"); } } printf("step2 after fork() !!\n\n"); } ``` ### vfork(), \_exit() 功能與fork()相同,但不會複製新一份記憶體,child process和parent process在同一份記憶體上執行,當child process還沒結束時,parent process不會執行 在child process裡,不能用exit(),必須改用_exit()。 \_exit()不會執行多餘的clean up,保護parent process的資源不會提早被清空 - [fork()、vfork()、clone()系統呼叫差異 @ 研究所學習筆記 :: 隨意窩 Xuite日誌](http://blog.xuite.net/ian11832/blogg/23967641-fork%28%29%E3%80%81vfork%28%29%E3%80%81clone%28%29%E7%B3%BB%E7%B5%B1%E5%91%BC%E5%8F%AB%E5%B7%AE%E7%95%B0) - [exit、_exit() 的區別 @ 邱小新の工作筆記 :: 痞客邦 ::](http://jyhshin.pixnet.net/blog/post/26588112-exit%E3%80%81_exit%28%29-%E7%9A%84%E5%8D%80%E5%88%A5) ### exec() 需引用函式庫: ``` #include<unistd.h> ``` exec()本身不是一個函式,而是六個函式的總稱,功能相同,呼叫方法不同: ```clike= int execl(const char *pathname, const char *arg0,.../* (char *)0 */); int execv(const char *pathname, char *const argv[]); int execle(const char *pathname, const char *arg0,.../* (char *)0,char *const envp[] */); int execve(const char *pathname, char *const argv[], char *const envp[]); int execlp(const char *filename, const char *arg0,.../* (char *)0 */); int execvp(const char *filename, char *const argv[]); ``` #### 6函式命名規則: exec\[l or v\]\[p\]\[e\] exec函数里的参数可以分成3个部分,      执行文件部分,     命令参数部分,   环境变量部分. 例如我要执行1个命令   ls -l /home/gateman  执行文件部分就是  "/usr/bin/ls" 命令参赛部分就是 "ls","-l","/home/gateman",NULL              见到是以ls开头 每1个空格都必须分开成2个部分, 而且以NULL结尾的啊. 环境变量部分, 这是1个数组,最后的元素必须是NULL 例如  char * env\[\] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL}; --- e后续,  参数必须带环境变量部分,   环境变零部分参数会成为执行exec函数期间的环境变量, 比较少用 l 后续,   命令参数部分必须以"," 相隔, 最后1个命令参数必须是NULL v 后续,   命令参数部分必须是1个以NULL结尾的字符串指针数组的头部指针. 例如char * pstr就是1个字符串的指针, char * pstr\[\] 就是数组了, 分别指向各个字符串. p后续,   执行文件部分可以不带路径, exec函数会在\$PATH中找 ![](http://img.blog.csdn.net/20151023185149377?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) #### 返回值: 失败:返回 **-1** 成功:无返回 #### 範例程式: ```clike if (fork() == 0){ //child process char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL}; if (execvp("echo",execvp_str) <0 ){ perror("error on exec"); exit(0); } }else{ //parent process wait(&childpid); printf("execvp done\n\n"); } ``` ### wait() #### 引用函式庫: ```clike #include <sys/types.h> #include <sys/wait.h> ``` ```clike pid_t wait(int *_wstatus_); pid_t waitpid(pid_t _pid_, int *_wstatus_, int _options_); ``` #### 回傳值: **wait**(): on success, returns the process ID of the terminated child; on error, -1 is returned. **waitpid**(): on success, returns the process ID of the child whose state has changed; if **WNOHANG** was specified and one or more child(ren) specified by _pid_ exist, but have not yet changed state, then 0 is returned. On error, -1 is returned. ## 字串處理相關function學習 ### 整行字串讀取 char* fgets(char*, int, FILE *); 詳見 [gets() v.s. fgets()](#gets-vs-fgets) ### 字串切割 char* strtok(char*, const char*) ```clike #include <string.h> char * strtok ( char * str, const char * delimiters ); ``` 範例程式 ```clike= /* strtok example */ #include <stdio.h> #include <string.h> int main () { char str[] ="- This, a sample string."; char * pch; printf ("Splitting string \"%s\" into tokens:\n",str); pch = strtok (str," ,.-"); while (pch != NULL) { printf ("%s\n",pch); pch = strtok (NULL, " ,.-"); } return 0; } ``` 輸出: ``` Splitting string "- This, a sample string." into tokens: This a sample string ``` 參數注意: > str: > > Notice that this string is modified by being broken into smaller strings (tokens). > > Alternativelly, a **null** pointer may be specified, in which case the function continues scanning where a previous successful call to the function ended. 參照: - [strtok - C++ Reference](http://www.cplusplus.com/reference/cstring/strtok/) - [\[C/C++\] 切割字串函數:strtok, Network mac address 分割 | 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY](https://blog.wu-boy.com/2010/04/cc-%E5%88%87%E5%89%B2%E5%AD%97%E4%B8%B2%E5%87%BD%E6%95%B8%EF%BC%9Astrtok-network-mac-address-%E5%88%86%E5%89%B2/) - [Zero-copy in-place string splitting in C – TechOverflow](https://techoverflow.net/2017/01/23/zero-copy-in-place-string-splitting-in-c/) ## 檔案相關function ### IO Redirect: dup2() dup(),用來重新導向stdio ```clike dup2(int dst, int src); ``` 更改src指向的檔案變成dst [c - Redirecting exec output to a buffer or file - Stack Overflow](https://stackoverflow.com/questions/2605130/redirecting-exec-output-to-a-buffer-or-file) ### pipe: pipe() pipe(),用來開啟一組pipe 分兩邊,要關閉其中一端才能用 ```clike= int pfd[2]; pipe(pfd); int pid1 = fork(); if (pid1 == 0) { // child1 close } ``` [redirect - Classic C. Using pipes in execvp function, stdin and stdout redirection - Stack Overflow](https://stackoverflow.com/questions/13801175/classic-c-using-pipes-in-execvp-function-stdin-and-stdout-redirection) ### open() [C语言open()函数:打开文件函数_C语言中文网](http://c.biancheng.net/cpp/html/238.html) ## 防止zombie process - [輕描淡寫的低調: 如何防止彊屍程序(zombie)的產生](https://low-understated.blogspot.tw/2009/08/program-ex1-kill-prog1prog1-ps-ef-prog1.html) - [lazyf's den : A Personal Weblog: [技術] 如何避免Linux zombie process的產生](https://lazyflai.blogspot.tw/2007/06/linux-zombie-process.html) - [Eric的攝影世界 – Linux 刪除殭屍 zombie process方法](https://eric0703.pentaxfans.net/2633) 好像也可以用 SIGCHLD 實現 不過我這次是用double fork的方式解決的 ## 研究遇到問題 ### gets() v.s. fgets() 搜尋如何在C裡讀入一整串字串(包含空白)時,發現有**gets()**可以用,但程式執行時,出現警告訊息說gets()不安全 ``` warning: this program uses gets(), which is unsafe. ``` gets()函式宣告: ```clike #include <stdio.h> char * gets ( char * str ); ``` 不安全原因是: gets()的設計是會一直讀取新字元,直到讀取到**換行字元**或**EOF**,可能會讀超過buffer大小,造成overflow,目前gets()在C11已非標準函式 替代方案: 使用fgets() fgets()函式宣告: ```clike #include <stdio.h> char * fgets ( char * str, int num, FILE * stream ); ``` fgets()回傳值: - 成功:**\*str** - 失敗:**NULL pointer** ### char array array - [C语言strdup()函数:复制字符串_C语言中文网](http://c.biancheng.net/cpp/html/166.html) - [Zero-copy in-place string splitting in C – TechOverflow](https://techoverflow.net/2017/01/23/zero-copy-in-place-string-splitting-in-c/) ## 其他 ### BASH原始碼(參考) ==待研究== [The GNU Bourne-Again SHell - Git Repositories [Savannah]](https://savannah.gnu.org/git/?group=bash) ## 參考資料: - [fork(2) - Linux manual page](http://man7.org/linux/man-pages/man2/fork.2.html) - [wait(2) - Linux manual page](http://man7.org/linux/man-pages/man2/waitpid.2.html) - [exec(3) - Linux manual page](http://man7.org/linux/man-pages/man3/exec.3.html) - [linux c语言 fork() 和 exec 函数的简介和用法 - CSDN博客](http://blog.csdn.net/nvd11/article/details/8856278) - [程序员必备知识——fork和exec函数详解 - CSDN博客](http://blog.csdn.net/bad_good_man/article/details/49364947) - [c - Why is the gets function so dangerous that it should not be used? - Stack Overflow](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) - [fgets - C++ Reference](http://www.cplusplus.com/reference/cstdio/fgets/) - [gets - C++ Reference](http://www.cplusplus.com/reference/cstdio/gets/) - [strtok - C++ Reference](http://www.cplusplus.com/reference/cstring/strtok/) - [strtok - C++ Reference](http://www.cplusplus.com/reference/cstring/strtok/) - [\[C/C++\] 切割字串函數:strtok, Network mac address 分割 | 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY](https://blog.wu-boy.com/2010/04/cc-%E5%88%87%E5%89%B2%E5%AD%97%E4%B8%B2%E5%87%BD%E6%95%B8%EF%BC%9Astrtok-network-mac-address-%E5%88%86%E5%89%B2/) - [fork()、vfork()、clone()系統呼叫差異 @ 研究所學習筆記 :: 隨意窩 Xuite日誌](http://blog.xuite.net/ian11832/blogg/23967641-fork%28%29%E3%80%81vfork%28%29%E3%80%81clone%28%29%E7%B3%BB%E7%B5%B1%E5%91%BC%E5%8F%AB%E5%B7%AE%E7%95%B0)