一:fork()函數簡單認識
//創建進程;
//進程的概念:一個可執行程序,執行起來就是一個進程,再執行起來一次,它就又是一個進程(多個進程可以共享同一個 可執行文件)
//文雅說法:進程 定義爲程序執行的一個實例;
//在一個進程(程序)中,可以用fork()創建一個子進程,當該子進程創建時,
//它從fork()指令的下一條(或者說從fork()的返回處)開始執行與父進程相同的代碼;
//a)說白了:fork()函數產生了一個和當前進程完全一樣的新進程,並和當前進程一樣從fork()函數裏返回;
//原來一條執行通路(父進程),現在變成兩條(父進程+子進程)
//fork():一分二; *********
(1.1)fork()函數簡單範例
//ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 'bash|PID|nginx'
//fork()之後,是父進程fork()之後的代碼先執行還是子進程fork()之後的代碼先執行是不一定的;這個跟內核調度算法有關;
//kill子進程,觀察父進程收到什麼信號:SIGCHLD信號 ,子進程變成了殭屍進程Z
//信號處理函數
void sig_usr(int signo)
{
printf("收到了SIGUSR1信號,進程id=%d!\n",getpid());
}
int main(int argc, char *const *argv)
{
pid_t pid;
printf("進程開始執行!\n");
//先簡單處理一個信號
if(signal(SIGUSR1,sig_usr) == SIG_ERR) //系統函數,參數1:是個信號,參數2:是個函數指針,代表一個針對該信號的捕捉處理函數
{
printf("無法捕捉SIGUSR1信號!\n");
exit(1);
}
//---------------------------------
pid = fork(); //創建一個子進程
//要判斷子進程是否創建成功
if(pid < 0)
{
printf("子進程創建失敗,很遺憾!\n");
exit(1);
}
//現在,父進程和子進程同時開始 運行了
for(;;)
{
sleep(1); //休息1秒
printf("休息1秒,進程id=%d!\n",getpid());
}
printf("再見了!\n");
return 0;
}
(1.2)殭屍進程的產生、解決,SIGCHLD
//殭屍進程的產生:在Unix系統中,一個子進程結束了,但是他的父進程還活着,
// 但該父進程沒有調用(wait/waitpid)函數來進行額外的處置,那麼這個子進程就會變成一個殭屍進程;
//殭屍進程:已經被終止,不幹活了,但是依舊沒有被內核丟棄掉,因爲內核認爲父進程可能還需要該子進程的一些信息;
//作爲開發者,堅決不允許殭屍進程的存在;
//如何幹掉殭屍進程:
//a)重啓電腦
//b)手工的把殭屍進程的父進程kill掉,殭屍進程就會自動消失;
//SIGCHLD信號:一個進程被終止或者停止時,這個信號會被髮送給父進程;
//所以,對於源碼中有fork()行爲的進程,我們 應該攔截並處理SIGCHLD信號;
//waitpid();
#include <stdio.h>
#include <stdlib.h> //malloc,exit
#include <unistd.h> //fork
#include <signal.h>
#include <sys/wait.h> //waitpid
//信號處理函數
void sig_usr(int signo)
{
int status;
switch(signo)
{
case SIGUSR1:
printf("收到了SIGUSR1信號,進程id=%d!\n",getpid());
break;
case SIGCHLD:
printf("收到了SIGCHLD信號,進程id=%d!\n",getpid());
//掌握和使用waitpid即可;
//這個waitpid說白了獲取子進程的終止狀態,這樣,子進程就不會成爲殭屍進程了;
pid_t pid = waitpid(-1,&status,WNOHANG); //第一個參數爲-1,表示等待任何子進程,
//第二個參數:保存子進程的狀態信息(大家如果想詳細瞭解,可以百度一下)。
//第三個參數:提供額外選項,WNOHANG表示不要阻塞,讓這個waitpid()立即返回
if(pid == 0) //子進程沒結束,會立即返回這個數字,但這裏應該不是這個數字
return;
if(pid == -1) //這表示這個waitpid調用有錯誤,有錯誤也理解返回出去,我們管不了這麼多
return;
//走到這裏,表示 成功,那也return吧
return;
break;
} //end switch
}
int main(int argc, char *const *argv)
{
pid_t pid;
printf("進程開始執行!\n");
//先簡單處理一個信號
if(signal(SIGUSR1,sig_usr) == SIG_ERR) //系統函數,參數1:是個信號,參數2:是個函數指針,代表一個針對該信號的捕捉處理函數
{
printf("無法捕捉SIGUSR1信號!\n");
exit(1);
}
if(signal(SIGCHLD,sig_usr) == SIG_ERR)
{
printf("無法捕捉SIGCHLD信號!\n");
exit(1);
}
//---------------------------------
pid = fork(); //創建一個子進程
//要判斷子進程是否創建成功
if(pid < 0)
{
printf("子進程創建失敗,很遺憾!\n");
exit(1);
}
//現在,父進程和子進程同時開始 運行了
for(;;)
{
sleep(1); //休息1秒
printf("休息1秒,進程id=%d!\n",getpid());
}
printf("再見了!\n");
return 0;
}
二:fork()函數進一步認識
//b)fork()產生新進程的速度非常快,fork()產生的新進程並不複製原進程的內存空間,而是和
//原進程(父進程)一起共享一個內存空間,但這個內存空間的特性是“寫時複製”,也就是說:
//原來的進程和fork()出來的子進程可以同時、自由的讀取內存,但如果子進程(父進程)對
//內存進行修改的話,那麼這個內存就會複製一份給該進程單獨使用,以免影響到共享這個內存空間的
//其他進程使用;
三:完善一下fork()代碼
//fork()回返回兩次:父進程中返回一次,子進程中返回一次,而且,fork()在父進程中返回的值和在子進程中返回的值是不同的
//子進程的fork()返回值是0;
//父進程的fork()返回值是新建立的子進程的ID,因爲全局量g_mygbltest的值發生改變,導致主,子進程內存被單獨的分開,所以每個的
//g_mygbltest值也不同;
#include <stdio.h>
#include <stdlib.h> //malloc,exit
#include <unistd.h> //fork
#include <signal.h>
int g_mygbltest = 0;
int main(int argc, char *const *argv)
{
pid_t pid;
printf("進程開始執行!\n");
//---------------------------------
pid = fork(); //創建一個子進程
//要判斷子進程是否創建成功
if(pid < 0)
{
printf("子進程創建失敗,很遺憾!\n");
exit(1);
}
//現在,父進程和子進程同時開始 運行了
//for(;;)
//{
// sleep(1); //休息1秒
// printf("休息1秒,進程id=%d!\n",getpid());
//}
//printf("再見了!\n");
//走到這裏,fork()成功,執行後續代碼的可能是父進程,也可能是子進程
if(pid == 0)
{
//子進程,因爲子進程的fork()返回值會是0;
//這裏專門針對子進程的處理代碼
while(1)
{
g_mygbltest++;
sleep(1); //休息1秒
printf("真是太高興了,我是子進程的,我的進程id=%d,g_mygbltest=%d!\n",getpid(),g_mygbltest);
}
}
else
{
//這裏就是父進程,因爲父進程的fork()返回值會 > 0(實際返回的是子進id程)
//這是專門針對父進程的處理代碼
while(1)
{
g_mygbltest++;
sleep(5); //休息5秒
printf("......。。,我是父進程的,我的進程id=%d,g_mygbltest=%d!\n",getpid(),g_mygbltest);
}
}
return 0;
}
(3.1)一個和fork()執行有關的邏輯判斷(短路求值)
//||或:有1出1,全0出0;
//&&與:全1出1,有0出0;
#include <stdio.h>
#include <stdlib.h> //malloc,exit
#include <unistd.h> //fork
#include <signal.h>
int main(int argc, char *const *argv)
{
fork(); //一般fork都會成功所以不判斷返回值了,我們假定成功
fork();
//((fork() && fork()) || (fork() && fork()));
//printf("每個實際用戶ID的最大進程數=%ld\n",sysconf(_SC_CHILD_MAX));
for(;;)
{
sleep(1); //休息1秒
printf("休息1秒,進程id=%d!\n",getpid());
}
printf("再見了!\n");
return 0;
}
四:fork()失敗的可能性
//a)系統中進程太多
//缺省情況,最大的pid:32767
//b)每個用戶有個允許開啓的進程總數;
//7788