# 系統程式筆試考古題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