---
# System prepended metadata

title: 系統程式筆試考古題2019
tags: [考古題]

---

# 系統程式筆試考古題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()，當Ｃ執行完回到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


