本章解釋了fork,exec函數族,exit,wait函數族,解釋器文件,system 函數,以及進程會計和進程時間等。
1. 進程標識。
unix環境進程0爲swapper調度進程,1 爲init系統自舉進程,2爲pagedaemon,負責虛存系統。
六個函數爲
pid_t getpid()
pid_t getppid()
uid_t getuid()
uid_t geteuid()
gid_t getgid()
gid_t getegid()
這邊比較難理解的是有效用戶id和用戶id.這邊涉及到一個設置用戶ID的概念, 說白了,設置用戶ID是文件擁有者,給一張令牌,讓執行本文件的人擁有文件擁有者的權限。
實例:在本文件夾下面創建文件, 文件權限,只有root用戶有寫權限,其他爲讀權限
-rw-r--r-- 1 root staff 24 9 5 20:18 record.log
編寫如下代碼,並root用戶編譯後。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
FILE* fp = fopen("record.log","a+");
if (fp==NULL)
{
printf("error = %d, reason =%s\n", errno, strerror(errno));
return -1;
}
printf("uid= %d, euid=%d\n", getuid(),geteuid());
// we have the authority to open for write now
fclose(fp);;
}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>
用非root用戶執行的結果爲:
error = 13, reason =Permission denied
給設置用戶ID後:chmod +s a.out
用非root用戶執行後的結果爲:
uid= 501, euid=0
可見設置用戶ID給予提權後,uid和euid不一樣了。設置組ID同理。
2. fork 函數。
fork 函數返回兩次,主進程得到的是子進程的進程號,子進程得到0.
子進程會運用cow(寫時複製技術) 複製父進程進程空間的。
用戶ID,Session ID, 控制終端,當前工作目錄,跟目錄,文件屏蔽字,信號屏蔽字,環境變量,
資源限制,鏈接的共享存儲段, 執行打開文件描述符的關閉標誌(?)
子進程不復制父進程的爲:
進程ID,子進程的時間信息(tms_utmie,tms_stmie,tms_cutime,tms_ustime)
父進程的鎖,未決信號集。
下面一段代碼爲APUE作者精心設計,輸出重定向到文件或者終端,打印結果不一樣。一着解釋了父子進程變量複製的過程。二者解釋了,write 函數沒有緩存,而printf行緩存的機制。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob =88;
int main()
{
pid_t pid ;
int var =99;
char buf[]="a write to stdout\n";
if (write(STDOUT_FILENO, buf, sizeof(buf)-1)!= sizeof(buf)-1)
{
printf("write to stdout error\n");
}
printf("Before fork\n");
if((pid = fork())<0)
{
printf("fork error\n");
return -1;
}
else if(pid>0) //parent
sleep(2);
else
{
var++;
glob++;
}
printf("var=%d, glob=%d,pid=%d\n",var,glob,getpid());
}
下面代碼需要注意的是,子進程退出時候需要用_exit(), 直接進入內核,不會刷新IO.故vfork的進程應該用_exit().
當同時不能用return, 這會修改函數棧,造成父進程crash. 如下面代碼所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob =1;
int main(void) {
int var;
var = 88;
pid_t pid;
if ((pid = vfork()) < 0) {
printf("vfork error");
exit(-1);
} else if (pid == 0) {
var++;
return 0;
}
printf("pid=%d, glob=%d, var=%d\n", getpid(), glob, var);
return 0;
}
4. 殭屍進程。
子進程已經死亡,而父進程並沒有接收,這會造成子進程變成殭屍進程(Zombie)。如下示例中,在主進程sleep的兩百秒內,子進程將變成殭屍進程。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
pid_t pid;
if ((pid= fork())<0)
{
printf("fork error");
exit(-1);
}
else if (pid>0)
{
printf("pid_child = %d, pid = %d\n",pid, getpid());
sleep(200);
}
else
{
exit(0);
}
exit(0);
}
5. wait與waitpid
wait 和waitpid 區別: 1. waitpid 可以不阻塞 2.waitpid 不像wait那樣,死等第一個進程。
int waitpid(pid_t pid, int* status, int option).
pid==-1時,等待任何一個子進程
pid>0 , 等待等於PID的進程
pid==0, 等待進程組中任何一個進程
pid<-1, 等待組ID 等於pid絕對值的進程。
下面例子爲pid==-1時,等待不同組中的子進程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
printf("fork error\n");
return -1;
}
else if (pid==0) //child
{
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
setpgid(getpid(),getpid());
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
sleep(5);
exit(1);
}
else //parent
{
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
setpgid(getpid(),getpid());
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
int status;
if( waitpid(-1,&status,0)==-1 )
{
printf("wait pid error\n");
return -1;
}
else
printf("child status is %d\n", WEXITSTATUS(status));
}
}
下面例子爲pid< -1 ,等待組ID等waitpid參數的情況
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
printf("fork error\n");
return -1;
}
else if (pid==0) //child
{
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
setpgid(getpid(),getpid());
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
exit(1);
}
else //parent
{
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
setpgid(getpid(),getpid());
printf("pid is %d, group id is %d\n", getpid(),getpgid(0));
int status;
sleep(5);
if( waitpid(0-pid,&status,0)==-1 )
{
printf("wait pid error\n");
return -1;
}
else
printf("child status is %d\n", WEXITSTATUS(status));
}
}
6. exec 函數族
execl, execv, execle,execve,execlp,execvp.
l代表參數以list的形式傳入,最後一個參數必須以(char*)0 結束。
v代表參數以數值的形式傳入,數組最後一個參數也必須以(char*) 0.
e代表環境變量參數,p代表path,意思爲在環境變量尋找可執行程序。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
printf("fork error\n");
return;
}
else if(pid>0)
{
if(execl("/bin/ls","-lt","/home", (char*)0)==-1)
{
printf("execl error\n");
return -1;
}
}
sleep(2);
printf("*********************************************\n");
if((pid=fork())<0)
{
printf("fork error\n");
return;
}
else if(pid>0)
{
char* execvector[3] = {"-lt", "/home",NULL};
if(execv("/bin/ls",execvector)==-1)
{
printf("execl error\n");
return -1;
}
}
}
7. setuid, seteuid, setreuid
一條規則,root用戶隨便改。其他用戶要改的話需要程序本身擁有設置用戶ID.
8. 解釋器文件
!# 開頭的bash 文件
9. system 函數
10. 獲得用戶標識
char* getlogin(void)
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
printf("User is %s\n", getlogin());
}
11. 進程時間相關。
通過下面demo可嘗試打印對應的進程以及子進程的時間信息。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/times.h>
int main(int argc,char* argv[])
{
pid_t pid;
struct tms start, end;
if( times(&start)==-1)
{
printf("times error\n");
return -1;
}
if ((pid= fork())<0)
{
printf("fork error");
exit(-1);
}
else if (pid==0)
{
sleep(3);
}
else
{
if((pid =wait(NULL))==-1)
{
printf("wait error\n");
return -1;
}
printf("wait pid %d\n",pid);
if(times(&end)==-1)
{
printf("times error\n");
return -1;
}
long clktck=0;
if((clktck=sysconf(_SC_CLK_TCK))<0)
{
printf("sysconf errror \n");
return -1;
}
printf("user: %7.2f\n",(end.tms_utime- start.tms_utime)/(double)clktck);
printf("sys: %7.2f\n",(end.tms_stime- start.tms_stime)/(double)clktck);
printf("child user: %7.2f\n",(end.tms_cutime- start.tms_cutime)/(double)clktck);
printf("child sys: %7.2f\n",(end.tms_cstime- start.tms_cstime)/(double)clktck);
}
}