Linux進程初級:在以前的概念梳理中已經將進程的概念部分大體說明了,如今就是程序部分了。數組
環境:Ubentu 16.04.2(Vmware x_64) + gcc version 5.4.0函數
fork()函數用於從已存在的進程中建立一個新進程。新進程稱爲子進程,原進程稱爲父進程。使用fork()函數獲得的子進程是父進程的一個複製品,它從父進程處繼承了整個進程的地址空間,包括進程上下文、代碼段、進程堆棧、內存信息、打開的文件描述符、信號處理函數、進程優先級、進程組號、當前工做目錄、根目錄、資源控制、控制終端等,子進程獨有的是它的進程號、資源使用、計時器等。指針
如此,在運行了fork()函數後父子進程會運行同一個程序(因爲代碼段等徹底複製),所以須要用一種方法區分它們,不然,兩個進程只能作同一間事。調試
區分父子進程的方法是fork() 的返回值不一樣,父進程返回的是子進程的進程號,而子進程中返回0。日誌
注意:子進程沒有執行fork(),而是從fork()調用的下一條語句開始執行的。code
#include <sys/types.h> //提供類型pid_t定義 #include <unistd.h> //sleep()、fork() #include <stdio.h> //perror()、printf() int main() { pid_t ret; ret = fork(); if(-1 == ret){ perror("fork error:"); return -1; } if(0 == ret){ printf("child:%d PID:%d\n",ret,getpid()); //getpid() returns the process ID of the calling process. }else{ printf("parent:%d PID:%d\n",ret,getpid()); usleep(100); } return 0; }
執行結果:繼承
parent:51477 PID:51476 child:0 PID:51477
從實例中可看出,fork()函數建立了一個子進程,其中父進程返回子進程的進程號,而子進程返回值爲0。進程
exec函數族提供了一系列的在進程中執行另外一程序的方法。exec能夠根據指定的文件名或目錄名找到可執行文件,並用它來代替當前程序的數據段、代碼段、堆棧段。在執行完以後,當前進程除進程號外,其餘內容都被替換了。這裏的可執行文件便可以是二進制文件,也能夠是Linux下任何可執行的腳本文件。內存
exec函數家族一共有6個,它們的功能都一致,只是使用的時候有細微的區別:表中國的前4個函數須要輸入完整的路徑,最後兩個只要給出文件名,系統就會自動按照環境變量PATH所包含的路徑進行查找;另外帶l(list)的表示逐個列舉參數的方式,其類型爲const char *arg;字母v(vertor)表示經過指針數組傳遞,其類型爲char *const argv[];字母e(environment)表示可在envp[]中指定當前進程的環境變量。資源
注:參數列表需以NULL結尾。
#include <sys/types.h> //提供類型pid_t定義 #include <unistd.h> //sleep()、fork() #include <stdio.h> //perror()、printf() #include <sys/wait.h> //wait()、waitpid() int main() { pid_t ret; ret = fork(); if(-1 == ret){ perror("fork error:"); return -1; } if(0 == ret){ if((ret = execlp("ls","ls","-l",NULL)) < 0){ printf("execlp error\n"); } }else{ // while(wait(NULL) == 0); while(waitpid(ret,NULL,WNOHANG) == 0){ printf("child process has not exited\n"); usleep(100); } //WNOHANG爲非阻塞模式,若是是0,父進程會一直阻塞,直到子進程結束。 if(ret) printf("child exited\n"); else printf("exit error\n"); } return 0; }
輸出:
child process has not exited child process has not exited child process has not exited child process has not exited child process has not exited child process has not exited child process has not exited -rwxrwxrwx 1 root root 8864 Sep 14 05:27 a.out child process has not exited -rwxrwxrwx 1 root root 695 Sep 14 05:27 execlp.c child exited
交互式進程和批處理進程在程序上的差異只是交互式多了等待用戶輸入等與用戶交互的動做。還有第三類進程值得咱們注意 - 守護進程。
守護進程:這類進程一直在後臺運行,不少系統進程都是以守護進程的形式存在。守護進程的實現有特定的步驟:
一、建立子進程,父進程退出:子進程變成孤兒進程被init收養。(在Ubentu的高級版本中會被 /sbin/upstart 進程收養,此進程爲圖形化的初始化程序,如在文本界面則沒有此進程)
二、建立新會話:setsid()函數用於建立一個新的會話,並擔任該會話組的組長。做用:讓進程擺脫原會話組的控制;讓進程擺脫原進程組的控制;讓進程擺脫原控制終端的控制。緣由:雖然父進程退出,但原先的會話期、進程組和控制終端並無改變,所以,並非真正意義上的獨立。(進程組:進程組是一個或多個進程的集合。進程組由進程組ID來惟一標識,進程組ID也是一個進程的必備屬性;會話期:會話組是一個或多個進程組的集合,一般,一個會話開始於用戶登陸,終止於用戶退出;或者開始於終端打開,結束於終端關閉。會話期的第一個進程爲會話組長,在此期間該用戶運行的全部進程都屬於這個會話期)
三、改變當前目錄;chdir()函數用於改變當前工做目錄。緣由:使用fork()建立的子進程繼承了父進程的當前工做目錄,因爲在進程運行過程當中,工做目錄是不能卸載的,這對之後的使用會形成麻煩,所以,一般的作法是讓"/"做爲守護進程的當前工做目錄。
四、重設文件權限掩碼;umask()設置文件權限掩碼。做用是屏蔽文件權限中的對應位。例如,若是文件權限掩碼是050,它表示屏蔽了文件組擁有着的可讀與執行權限。fork()的子程序繼承了父進程的文件權限掩碼,這就給子進程使用文件帶來了必定的影響。把文件權限掩碼設置成0(umask(0)),能夠增長守護進程的靈活性。
五、關閉文件描述符:同文件權限掩碼同樣,fork()子進程繼承了父進程中的已經打開了的文件。這些被打開的文件可能永遠不會被守護進程訪問,但他們同樣佔用系統資源,並且還可能致使所在的文件系統沒法被卸載。特別是守護進程與終端無關,因此指向終端設備的標準輸入、標準輸入和標準錯誤流已經失去了存在的價值,應當被關閉。
#include <sys/types.h> //提供類型pid_t定義 #include <unistd.h> //sleep()、fork() #include <stdio.h> //perror()、printf() #include <sys/wait.h> //wait()、waitpid() #include <stdlib.h> //exit(0) #include <string.h> //strlen() #include <fcntl.h> //open()、write() #include <sys/stat.h> //umask() #include <syslog.h> //日誌後臺調試 /var/log/syslog int main() { pid_t ret; ret = fork(); int fd; char buf[] = "6"; if(ret < 0){ perror("fork error:"); return -1; }else if(ret > 0){ usleep(100); exit(0); //父進程退出 } openlog("daemon_syslog",LOG_PID,LOG_DAEMON); printf("pid:%d\n",getpid()); if(setsid() < 0){ syslog(LOG_ERR,"%s\n","setsid"); exit(1); } //建立新的會話 if(chdir("/") < 0){ syslog(LOG_ERR,"%s\n","chdir"); exit(1); } //改變工做目錄 umask(0); //設置文件權限掩碼 int i,num = getdtablesize(); //獲取當前進程文件描述符表大小 for(i = 0;i < num;i++){ //循環關閉已打開文件 close(i); } while(1){ if((fd = open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600)) < 0){ syslog(LOG_ERR,"%s\n","open"); exit(1); } write(fd,buf,strlen(buf)+1); close(fd); sleep(2); } closelog(); exit(0); }