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中找

#### 返回值:
失败:返回 **-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)