記錄學習內容。
以下內容大多引用大大們的文章。
內容可能有錯誤。
程式來源,教學來源:
[Linux C] fork 觀念由淺入深
https://wenyuangg.github.io/posts/linux/fork-use.html
買了這本書,執行緒很詳細:
深入理解Linux程式設計:從應用到核心
https://www.books.com.tw/products/0010756897
OS - Ch3 行程 Process
https://mropengate.blogspot.com/2015/01/operating-system-ch3-processes.html
之前紀錄的
https://ithelp.ithome.com.tw/articles/10228684
筆記:
1
C 的fork 是 產生 process ,不是thread
2
僵屍程序 (Zombie Process):
沒有做任何事, 只是佔著並耗用系統資源 就是 僵屍程序 (Zombie Process)
Zombie Process 會占用 行程ID , 造成系統不能把ID 讓給其他行程 。
這邊的想法是
子行程一下子就完成工作了,但還是活著
因為 父行程 sleep(30秒),沒有 wait(NULL) 來獲取子行程的退出資訊,讓子行程結束。
造成 子行程在30秒內沒結束 ,讓子行程這段時間 變成 Zombie Process
程式:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t PID = fork();
switch(PID){
case -1:
perror("fork()");
exit(-1);
case 0:
//exit(0);
break;
default:
//wait(NULL); //父行程沒有關掉子行程,讓子行程等父行程sleep(30)後,才一起關掉
sleep(30);
}
printf( "PID is %d\n", getpid());
return 0;
}
用 ps aux 觀察:
ted 23579 0.0 0.0 0 0 pts/1 Z+ 01:24 0:00 [output] <defunct>
所以應該真的是僵屍狀態
http://linux.vbird.org/linux_basic/0440processcontrol.php#ps_l
Z (Zombie):僵屍狀態,程序已經終止但卻無法被移除至記憶體外。
parent 死了,child 會變 orphan(孤兒),parent 睡了,child 事情做完會變 zombie(殭屍)
這種狀況,好像是 orphan(孤兒)
//程式來源:[Linux C] fork 觀念由淺入深
//https://wenyuangg.github.io/posts/linux/fork-use.html
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
// 從呼叫 fork 開始, 會分成兩支程序多工進行
pid_t PID = fork();
switch(PID){
// PID == -1 代表 fork 出錯
case -1:
perror("fork()");
exit(-1);
// PID == 0 代表是子程序
case 0:
printf("I'm Child process\n");
printf("Child's PID is %d\n", getpid());
sleep(20);
break;
// PID > 0 代表是父程序
default:
printf("I'm Parent process\n");
printf("Parent's PID is %d\n", getpid());
}
return 0;
}
結果:
I'm Parent process
Parent's PID is 21176
I'm Child process
Child's PID is 21177
子行程還在,但是父行程早就結束了:
20954 pts/1 00:00:00 bash
21177 pts/1 00:00:00 output
21189 pts/1 00:00:00 ps
用 ps aux 觀察
ted 21177 0.0 0.0 4504 72 pts/3 S 01:26 0:00 ./output
S (Sleep):該程式目前正在睡眠狀態(idle),但可以被喚醒(signal)。
寫入時複製
https://zh.wikipedia.org/wiki/%E5%AF%AB%E5%85%A5%E6%99%82%E8%A4%87%E8%A3%BD
Copy-on-write 是什麼 ?
如果有多個呼叫者(callers)同時請求相同資源(如記憶體),
他們會共同取得相同的指標指向相同的資源 ,
直到某個呼叫者修改資源的內容時,系統才會真正複製一份專用副本(private copy)給該呼叫者 。
主要的優點是如果呼叫者沒有修改該資源,就不會有副本(private copy)被建立,因此多個呼叫者只是讀取操作時可以共享同一份資源。
子行程 和 父行程 代碼 一模一樣 比較少見 ,所以跟變數比較有關。
意思是說,子行程 的資料(data,變數 ) ,沒有修改的話,就還是跟 父行程 一樣 。
所以就不需要 在複製一份 ,只要指到 和父行程data 一樣的位址,共用記憶體就好了 。
要修改的時候 , 在真正複製一份就好 。
父行程 和 子行程 誰先跑 ?
跟這有關:
完全公平排程器
https://zh.wikipedia.org/wiki/%E5%AE%8C%E5%85%A8%E5%85%AC%E5%B9%B3%E6%8E%92%E7%A8%8B%E5%99%A8
可能還是以父行程為主 ,但是也可以調整成 子行程 為主 。
沒有保證 誰先誰後 。
execlp,exec,execv
載入特定的binary code 到 memory執行
像是:
execlp("/bin/ls","ls",NULL);
會印出當前目錄下的所有檔案名稱
程式:
//程式來源:[Linux C] fork 觀念由淺入深
//https://wenyuangg.github.io/posts/linux/fork-use.html
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
int exit_status;
int sum = 0;
pid_t PID = fork();
switch(PID){
case -1:
perror("fork()");
exit(-1);
case 0:
printf("[Child] I'm Child process\n");
printf("[Child] Child's PID is %d\n", getpid());
//printf("[Child] Enter my exit status: ");
//execlp("/bin/ls","ls",NULL); //目錄
sum += 55;
//scanf("%d", &exit_status);
//sleep(3);
break;
default:
printf("[Parent] I'm Parent process\n");
printf("[Parent] Parent's PID is %d\n", getpid());
printf("[Parent] child's PID is %d\n", PID);
//wait(&exit_status);
// WEXITSTATUS is an macro
printf("[Parent] Child's exit status is [%d]\n", WEXITSTATUS(exit_status));
printf("[Parent] sum is [%d]\n", sum);
}
printf("%d END \n",PID);
return 0;
}
可以發現 子行程 和 父行程 都有一份自己的 sum變數
測試fork()後,有幾個行程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t PID = fork();
switch(PID){
case -1:
perror("fork()");
exit(-1);
case 0:
fork();
fork();
}
printf("PID is %d\n", getpid());
return 0;
}
結果:
PID is 20821
PID is 20822
PID is 20824
PID is 20823
PID is 20825
【小黑馬作業系統考古】範圍: 恐龍書第一章~第四章
https://ithelp.ithome.com.tw/articles/10229521
程式練習:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t PID = fork();
switch(PID){
case -1:
perror("fork()");
exit(-1);
case 0:
fork();
fork();
break;
default:
fork();
break;
}
if(PID==0) fork();
printf("PID is %d\n", getpid());
return 0;
}
結果:
PID is 15097
PID is 15099
PID is 15098
PID is 15103
PID is 15101
PID is 15104
PID is 15100
PID is 15105
PID is 15102
PID is 15106
fork()、vfork()、clone()系統呼叫差異
https://blog.xuite.net/ian11832/blogg/23967641
fork / vfork 比較
https://jyhshin.pixnet.net/blog/post/26588006
vfork程式練習
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t PID = vfork();
switch(PID){
// PID == -1 代表 fork 出錯
case -1:
perror("fork()");
exit(-1);
// PID == 0 代表是子程序
case 0:
printf("I'm Child process\n");
printf("Child's PID is %d\n", getpid());
sleep(20);
break;
// PID > 0 代表是父程序
default:
printf("I'm Parent process\n");
printf("Parent's PID is %d\n", getpid());
}
return 0;
}
結果:
I'm Child process
Child's PID is 93
I'm Parent process
Parent's PID is 92
//就是child 會先執行完 ,才換parent 。
如果是fork , 前面的寫法,結果有可能是:
I'm Parent process
Parent's PID is 21176
I'm Child process
Child's PID is 21177
vfork 和 execlp("/bin/ls","ls",NULL);練習
int main(){
pid_t PID = vfork();
switch(PID){
// PID == -1 代表 fork 出錯
case -1:
perror("fork()");
exit(-1);
// PID == 0 代表是子程序
case 0:
execlp("/bin/ls","ls",NULL); //目錄
printf("I'm Child process\n");
printf("Child's PID is %d\n", getpid());
sleep(20);
break;
// PID > 0 代表是父程序
default:
printf("I'm Parent process\n");
printf("Parent's PID is %d\n", getpid());
}
return 0;
}
結果:
I'm Parent process
Parent's PID is 49
代表 行緩衝 ,如果沒有\n ,還會在記憶體裡 ,沒有真正的input, output
,所以會有兩次hello ,因為 child process 把 parent process 的記憶體複製過去了,所以前面的printf還在
// fork3.c
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
printf("hello !");
// 建立子行程
pid = fork();
if (pid == 0) {
// 子行程
printf("Child process!\n");
} else if (pid > 0) {
// 父行程
printf("Parent process!\n");
} else {
// 錯誤
printf("Error!\n");
}
return 0;
}
結果
hello !Parent process!
hello !Child process!
// fork3.c
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
printf("hello ! \n");
// 建立子行程
pid = fork();
if (pid == 0) {
// 子行程
printf("Child process!\n");
} else if (pid > 0) {
// 父行程
printf("Parent process!\n");
} else {
// 錯誤
printf("Error!\n");
}
return 0;
}
hello !
Parent process!
Child process!