[APUE] 再讀之進程控制

本章解釋了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());
}


3. fork 函數的變種vfork函數。子進程完全共享父進程的進程空間。

下面代碼需要注意的是,子進程退出時候需要用_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);

}

}





發佈了45 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章