# 系統程式筆試考古題2019
>###### tags: `考古題`
>###### edited by SleepyCat
1. 簡單比較一下傳統linux權限控制及ACL (使用getfacl和setfacl)的權限控制
>ACL stands for Access Control List
傳統的linux權限只有Owner, Group, Others,實際上的案例如老師ppt裡提到的 :
老師、助教、學生、其他人,對於作業的權限的就不一樣,這樣就需要4種權限。
因此我們可以利用ACL,例如指令**setfacl -m u:Guest1:rx ./tmp**,指的就是針對tmp這個檔案
設定一個名為Guest1的user,並給他rx的權限。
2. 如何用google查詢在windows底下,監測檔案系統的函式名稱?
Hint<用linux的關鍵字搭配作業系統名稱>
>inotify windows
3. 目前在linux系統中只有一個行程(該行程為prog),是活躍的狀態(就是需要CPU的意思),其他的行程都是inactive的狀態(目前不需要CPU),請問下列這些指令會讓行程的執行時間造成什麼影響?
Hint<nice -n -5是提高此行程對其他行程的優先權,nice -n 5則是降低>
>我覺得不影響,因為整個系統只有一個行程在佔用CPU
4. 請問下列程式的執行結果,解釋你的答案
printf(“Hello”);
fork();
printf(“Ron\n”);
>結果如下:
HelloRon
HelloRon
因為standard output stream是line buffer,也就是說需要一個’\n’或是fflush()才能將
Buffer裡的東西輸出到螢幕上,而Hello字串並沒有\n跟在後面,當程式呼叫fork()的時候,
parent buffer裡的東西也會被複製到child buffer,所以Hello會印兩次。
5. 請描述如何同時對parent和child除錯
Hint<attach ,set waiting = 0 ,while loop>
```c=
//debugfork.c
int main(void)
{
pid_t pid;
int waiting = 1;
pid = fork();
if(pid == 0){
while(waiting);
printf("child");
}
else{
printf("child pid = %d\n",pid);
printf("parent");
}
printf("End\n");
return 0;
}
```
>code裡面加入while迴圈,並把變數waiting(目前值為1)傳入,讓child卡住,接著開兩個terminal,分別操作如下:
其中一個terminal輸入**gdb ./debugfork**並把中斷點設在while迴圈的下一行,利用**p pid**可以得到child的pid
另外一個terminal執行**sudo gdb debugfork**,不需要輸入**r**讓他跑起來
接著在利用**attach pid**綁定要處理的行程,最後利用**set waiting=0**讓剛剛停著的地方繼續跑
6. 請問何謂殭屍行程?
>Zombie Process就是當行程結束後,它的exit status沒有適當地被他的parent process處理,導致該process雖然已結束,但process資料卻沒辦法完全從記憶體中清除。
當Process結束時,它會變成EXIT_ZOMBIE的狀態,並發送SIGCHLD signal給parent process,這時候parent process應該要呼叫wait()system call來讀取child process的exit status及其他資訊,child process才會被完全從記憶體清除。
然而若是parent process忘記做這件事,這些zombie children就會卡在記憶體之中了。
7. System()和execv()都可以執行外部程式,請問下列程式碼會在螢幕上印出多少個"fork"?
```c=
int main(int argc, char* argv[]){
system("ls");
fork();
fork();
printf("fork\n");
return 0;
}
```
>會印出4個fork
>system函數會建立獨立行程,相當於執行shell -c string,新的進程執行完畢system才返回。(Blocking),實際上system()是靠著fork、wait和exec(“sh”, “ -c”, cmd)實做出來的。
```c=
int main(int argc,char* argv[]){
char* const para_list[] = {"/bin/ls","-l",NULL};
execv("/bin/ls",para_list);
fork();
fork();
printf("fork\n");
return 0;
}
```
>會印出0個fork
>exec家族的函式會直接覆蓋掉所有原先的進程的text、data、stack、heap等記憶體空間,所以原先的code完全沒用了,會跑新的ls指令內容,但process id和nice值等等並不會改變
8. 如何使用signal函式攔截Ctrl-C這組按鍵輸入?
> 信號SIGINT由按下Ctrl-C發出,我們可以利用signal(SIGINT, signal_handler);並將收到SIGINT信號之後要完成的事定義在signal_handler()裡面。
9. Linux中signal編號1-31,與編號SIGRTMIN~SIGRTMAX這兩個族群有什麼不同?
Hint<如果連續、大量的發送signal各會發生什麼事情>
>前面32個是不可靠訊號,後面的是可靠訊號
不可靠訊號指的是發送的次數和接受次數常常不同,在signal_handler中呼叫sleep(),然後連續按十次Ctrl+C。可能只會收到第一次Ctrl+C,OS會自動把第二次的Ctrl+C遮罩起來,變成pending的狀態,第三次就GG收不到
>可靠的訊號雖然稱為可靠,只要送訊號的速度快到OS kernel無法負荷,也不是那麼可靠
10. 函數呼叫為A()->B()->C(),當C執行完回到B()的時候,可以使用longjmp到C()函數嗎?
```c=
jmp_buf_a, jmp_buf_b, jmp_buf_c;
A(){
setjump(&jmp_buf_a);
B();
}
B(){
setjmp(&jmp_buf_b);
C();
longjmp(&jmp_buf_c, 1); //這一行可以嗎?
}
C(){
setjmp(&jmp_buf_c);
}
```
>我覺得不一定,上一回合執行C()的時候,雖然透過setjmp已經設定好SP、PC、R0~R31,但當C返回的時候,SP指標會移到B的地方,這時候就要**看系統有沒有把C堆疊內部的東西清掉**,如果沒有清掉,才有可能在B()函式中利用longjmp跳回C()
11. 請解釋dup的用途,並解釋下列的程式碼會將printf裡的內容寫到哪個地方
```c=
pipe(pipefd);
ret = fork();
if (ret==0) { /*child*/
close(0);
dup(pipefd[0]);
close(pipefd[1]);
close(pipefd[0]);
printf("hello\n");
}
else{
/*...*/
}
```
>程式一開始關閉了stdin,然後dup就會找最小的、無人使用的file descriptor number也就是0來使用,它會把這個fd指向dup參數中的fd所指向的file table
>上方程式為例,將0號fd關閉後,利用dup將0號指向pipefd[0]所指向的file table,然而這並不影響要輸出到stdout的printf,所以hello依然會印在螢幕上,但是從鍵盤輸入的東西會被導向到pipefd[0]
12. 請問pipe可否繼承給子行程,如果沒有特殊的設定,請問執行execve-like的系統呼叫以後,pipe可否繼承給新載入的程式碼?
Hint<在Linux內可以打像下列這樣的指令:$ ls -R / | sort,bash如何達成這樣的指令呢?>
>子行程可以繼承pipe。在exec之前將stdout導向到pipe,可以在exec之後存取pipe。
例如**ls -R / | sort**
第一個process(ls)會將stdout指向pipefd[1],然後執行exec(“ls”);
第二個process(sort)會把stdin導向pipfd[0],才執行exec(“sort”)。
13. 在linux中,如果父行程已經結束,子行程會自動結束嗎子行程會變成zombie嗎?
>父行程先于子行程退出,子行程就會變成孤兒行程,被1號行程init行程或是systmd收養,並由他們完成狀態收集作業,不會變成zombie
14. linux中,哪兩個信號不能夠被捕獲?
>SIGKILL,SIGSTOP這兩個不能
>SIGKILL 立即结束行程運行,這個信號不能被阻塞、捕獲和忽略
>SIGSTOP 可以暫停行程運行,直至SIGCONT信號發出
15. 在linux中,如果使用read讀取一個超大的檔案到記憶體,在此過程中使用者按下Ctrl-C發出SIGINT信號,且使用者使用signal_handler捕獲Ctrl-C,此時read的行為會是怎麼樣?
```c=
void signal_handler(int signum){
printf("reading...\n");
}
```
>read到一半被取消,精確地來講,收到signal的時候,目前在執行的system call:read會被取消(主程式的system call被取消),signal是由OS來呼叫,它和主程式不是同步執行的。signal_handler返回時,read會重做,使用者會感覺讀取時間比平常久,還有並不是所有system call都會重做,像sleep()就不會,不會的會回傳EINTR