Linux 進程(一):環境及其控制

進程環境

main啓動

當內核執行C程序時,在調用main前先調用一個特殊的啓動例程。可執行程序將此啓動例程指定爲程序的起始地址,接着啓動例程從內核中取出命令行參數和環境變量值,而後執行main函數。算法

 

進程終止

使進程終止的方式有8種,其中5種爲正常終止,3種爲異常終止:shell

終止類型數組

說明bash

正常終止app

(1)   從main返回ide

(2)   調用exit函數

(3)   調用_exit或_Exitoop

(4)   最後一個線程從啓動例程返回佈局

(5)   在最後一個線程中調用pthread_exitfetch

異常終止

(1)   調用abort

(2)   接收到一個信號

(3)   最後一個線程對取消請求作出響應

 

相關函數:

#include <stdlib.h>

void exit(int status);

void _Exit(int status);

 

#include <unistd.h>

void _exit(int status);

 

說明:

在main中,return(0)與exit(0)是等價的。

exit函數老是執行一個標準I/O庫的清理關閉工做,即對全部打開的流調用fclose函數。

 

#include <stdlib.h>

int atexit(void (*func)(void));

返回值:成功,0;失敗,-1

說明:

atexit函數用於向進程註冊函數,最多能夠註冊32個。這些函數將由exit調用。咱們將這些函數稱爲終止處理函數。若是同一個函數登記了屢次,則也會被調用屢次。

exit調用順序與這些函數的註冊順序恰好相反

 

# atexit函數的使用
[root@benxintuzi IO]# cat atexit.c
#include <stdio.h>
#include <stdlib.h>

static void my_exit1(void);
static void my_exit2(void);

int main(void)
{
        if(atexit(my_exit1) != 0)
                printf("can't register my_exit1\n");
        if(atexit(my_exit1) != 0)
                printf("can't register my_exit1\n");
        if(atexit(my_exit2) != 0)
                printf("can't register my_exit2\n");

        printf("main is done\n");
        return 0;
}

static void my_exit1(void)
{
        printf("first exit handler\n");
}

static void my_exit2(void)
{
        printf("second exit handler\n");
}

[root@benxintuzi IO]# ./atexit
main is done
second exit handler
first exit handler
first exit handler

 

環境變量

每一個程序都有一張環境表,是一個字符指針數組,每一個指針包含一個以null結束的字符串的地址。全局變量extern char** environ指向該環境表的地址:

 

以下函數用於操做環境變量的值:

#include <stdlib.h>

char* getenv(const char* name);

返回值:成功,返回與name關聯的value;失敗,返回NULL

int setenv(const char* name, const char* value, int rewrite);

int unsetenv(const char* name);

int putenv(char* str);

返回值:成功,0;失敗,-1

說明:

putenv將name=value的字符串放到環境表中。若是name已經存在,則先刪除其原來的定義。

關於參數rewrite:若是rewrite爲0,則保留原有的name定義;rewrite非0,則覆蓋原有的name定義。

unsetenv用於刪除指定的name定義。

 

一般用getenv和putenv來訪問特定的環境變量,而非使用environ變量,可是若是要查看整個環境,則仍然須要使用environ,以下: 

#include <stdio.h>

int main(void)
{
        extern char** environ;

        while(*environ != NULL)
        {
                printf("%s\n", *environ);
                environ++;
        }

        return 0;
}

[root@benxintuzi IO]# ./environ
HOSTNAME=benxintuzi
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash
TERM=vt100
HISTSIZE=1000
HADOOP_HOME=/bigdata/hadoop-2.6.0
SSH_CLIENT=192.168.8.1 55561 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib/qt-3.3
QTINC=/usr/lib/qt-3.3/include
SSH_TTY=/dev/pts/1
USER=benxintuzi
...

 

程序的存儲佈局

C程序由如下幾部分組成:

正文段:存放代碼的地方,一般是可共享的,即該部份內容是可再入的,所以通常只需一份副本,而且設置爲只讀。

初始化數據段:包含程序中須要明確指定初值的變量,好比:int maxcount = 99;

未初始化數據段(bss段):該段的數據不指定初值也可,由於內核在程序開始執行前,會將此段中的數據初始化爲0或者NULL。該部分有時也被稱爲常量區或全局區。

棧區:存放臨時變量,函數地址或者環境上下文信息等。

堆區:動態存儲分配,位於bss段和棧區之間。

一個典型的存儲空間示意圖以下:

 

Linux中的size程序用戶報告一個程序所佔用的存儲空間信息:

[root@benxintuzi IO]# size passwd01 memstr

text    data     bss     dec     hex   filename

1360    264      8       1632    660    passwd01

2157    284      8       2449    991    memstr

 

動態存儲分配函數以下:

#include <stdlib.h>

void* malloc(size_t size);

void* calloc(size_t nobj, size_t size);

void* realloc(void* ptr, size_t newsize);

void free(void* ptr);

 

說明:

malloc分配指定字節數的存儲區,初始值不肯定。

calloc分配指定長度的存儲區,初始值爲0。

realloc增長或者減小存儲區的長度,新增的區域內初始值不肯定。

 

資源限制

每一個進程都有一組資源限制,可使用以下函數查詢和更改:

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit* rlptr);

int setrlimit(int resource, const struct rlimit* rlptr);

返回值:成功,0;失敗,-1

struct rlimit

{

rlim_t rlim_cur;  /* soft limit: current limit */

rlim_t rlim_max;  /* hard limit: maximum value for rlim_cur */

}

 

說明:

任何一個進程均可將一個軟限制值更改成小於等於其硬限制值。

任何一個進程均可下降其硬限制值,但它不能小於軟限制值,並且這種下降對於普通用戶是不可逆的。

只有超級用戶進程能夠提升硬限制值。

resource參數取值以下:

RLIMIT_AS:進程總的可用存儲空間的最大長度。

RLIMIT_CORE:core文件的最大字節。若爲0,則阻止建立core文件。

RLIMIT_CPU:CPU時間的最大量值(秒)。若超過此軟限制,則向該進程發送SIGXCPU信號。

RLIMIT_DATA:數據段總的最大字節長度,包含:初始化數據段、bss段、堆區。

RLIMIT_FSIZE:能夠建立文件的最大字節長度。若超過此軟限制,則向該進程發送SIGXFSZ信號。

RLIMIT_MEMLOCK:進程使用mlock可以鎖定的在存儲空間中的最大字節長度。

RLIMIT_MSGQUEUE:進程爲消息隊列可分配的最大存儲字節數。

RLIMIT_NICE:nice值影響進程的調度優先級。

RLIMIT_NOFILE:進程可打開的最大文件數量。更改此限制將影響sysconf函數在參數_SC_OPEN_MAX中的返回值。

RLIMIT_NPROC:每一個用戶ID可擁有的最大子進程數。更改此限制將影響sysconf函數在參數_SC_CHILD_MAX中的返回值。

RLIMIT_RSS:最大駐留內存集字節長度。

RLIMIT_SIGPENDING:進程可排隊的信號最大數量。

RLIMIT_STACK:棧的最大字節長度。

RLIMIT_SWAP:用戶可消耗的交換空間的最大字節數。

 

以下程序打印當前系統支持的全部資源限制狀況:

[root@benxintuzi IO]# cat reslimit.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>

#define doit(name)      pr_limits(#name, name)

static void     pr_limits(char*, int);

int main(void)
{
#ifdef  RLIMIT_AS
        doit(RLIMIT_AS);
#endif

        doit(RLIMIT_CORE);
        doit(RLIMIT_CPU);
        doit(RLIMIT_DATA);
        doit(RLIMIT_FSIZE);

#ifdef  RLIMIT_MEMLOCK
        doit(RLIMIT_MEMLOCK);
#endif

#ifdef RLIMIT_MSGQUEUE
        doit(RLIMIT_MSGQUEUE);
#endif

#ifdef RLIMIT_NICE
        doit(RLIMIT_NICE);
#endif

        doit(RLIMIT_NOFILE);

#ifdef  RLIMIT_NPROC
        doit(RLIMIT_NPROC);
#endif

#ifdef RLIMIT_NPTS
        doit(RLIMIT_NPTS);
#endif

#ifdef  RLIMIT_RSS
        doit(RLIMIT_RSS);
#endif

#ifdef  RLIMIT_SBSIZE
        doit(RLIMIT_SBSIZE);
#endif

#ifdef RLIMIT_SIGPENDING
        doit(RLIMIT_SIGPENDING);
#endif

        doit(RLIMIT_STACK);

#ifdef RLIMIT_SWAP
        doit(RLIMIT_SWAP);
#endif

#ifdef  RLIMIT_VMEM
        doit(RLIMIT_VMEM);
#endif

        exit(0);
}

static void pr_limits(char* name, int resource)
{
        struct rlimit           limit;
        unsigned long long      lim;

        if (getrlimit(resource, &limit) < 0)
                printf("getrlimit error for %s\n", name);
        printf("%-14s  ", name);
        if (limit.rlim_cur == RLIM_INFINITY) {    # 常量RLIM_INFINITY表示無限制
                printf("(infinite)  ");
        } else {
                lim = limit.rlim_cur;
                printf("%10lld  ", lim);
        }
        if (limit.rlim_max == RLIM_INFINITY) {
                printf("(infinite)");
        } else {
                lim = limit.rlim_max;
                printf("%10lld", lim);
        }
        putchar((int)'\n');
}

[root@benxintuzi IO]# ./reslimit
RLIMIT_AS       (infinite)  (infinite)
RLIMIT_CORE              0  (infinite)
RLIMIT_CPU      (infinite)  (infinite)
RLIMIT_DATA     (infinite)  (infinite)
RLIMIT_FSIZE    (infinite)  (infinite)
RLIMIT_MEMLOCK       65536       65536
RLIMIT_MSGQUEUE      819200      819200
RLIMIT_NICE              0           0
RLIMIT_NOFILE         1024        4096
RLIMIT_NPROC          3861        3861
RLIMIT_RSS      (infinite)  (infinite)
RLIMIT_SIGPENDING        3861        3861
RLIMIT_STACK      10485760  (infinite)

 

進程控制

進程標識

每一個進程都用一個非負整型ID惟一標識,可是該進程ID是可複用的,只不過大多數Unix系統採用延遲複用算法,即若是一個進程終止後,必須通過某個固定的間隔纔可將該進程ID用於表示其餘進程,這樣作能夠防止將新進程誤認爲是前一個已經終止的進程。

 

特殊進程:

進程ID

說明

0

用於標識調用進程(交換進程swapper),其並不執行任何磁盤上的程序,所以也被稱爲系統進程。

1

用於標識init進程,在自舉結束時由內核調用,位於/sbin/init,用於在自舉後啓動一個Unix系統。init一般讀取與系統有關的初始化文件(/etc/rc*或/etc/inittab、/etc/init.d),將系統引導到一個狀態。init進程不會終止,可是其並非系統進程,而是一個普通的用戶進程,只不過是以超級用戶特權運行。

 

其餘ID標識操做函數:

#include <unistd.h>

pid_t getpid(void);

pid_t getppid(void);

uid_t getuid(void);

uid_t geteuid(void);

gid_t getgid(void);

gid_t getegid(void);

 

說明:

getpid返回進程ID,getppid返回父進程ID,getuid返回實際用戶ID,geteuid返回有效用戶ID,getgid返回實際組ID,getegid返回有效組ID。

 

進程建立

函數

說明

#include <unistd.h>

pid_t fork(void);

(1)   fork函數用於子進程的建立,調用一次,返回兩次。父進程返回子進程ID,而子進程返回0。

(2)   子進程是父進程的副本,獲取父進程的數據、堆、棧等的副本(不共享)。父子進程共享正文段。

(3)   因爲在fork以後常常伴隨着exec,全部如今不少fork實現並不執行數據、堆和棧的徹底副本,而是採用寫時複製(Copy-On-Write, COW)技術,即這些區域先由父子進程共享,內核將他們的權限設爲只讀,若是其中任何一個須要修改時,內核只爲須要修改的區域部分製做一個副本。

(4)   在fork以後先執行父進程仍是子進程是不肯定的,取決於內核使用的調度算法。

[root@benxintuzi process]# cat fork01.c
#include <unistd.h>
#include <stdio.h>

int    globvar = 6;            /* external variable in initialized data */
char    buf[] = "a write to stdout\n";

int main(void)
{
        int             var;            /* automatic variable on the stack */
        pid_t   pid;

        var = 88;
        if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
                printf("write error\n");

        printf("before fork\n");        /* we don't flush stdout */

        if ((pid = fork()) < 0) 
        {
                printf("fork error\n");
        } 
        else if (pid == 0) 
        {                                               /* child */
                globvar++;                              /* modify variables */
                var++;
        } 
        else 
        {
                sleep(2);                               /* parent */
        }

        printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);

        return 0;
}

[root@benxintuzi process]# ./fork01
a write to stdout
before fork
pid = 2270, glob = 7, var = 89
pid = 2269, glob = 6, var = 88

 

fork失敗的兩個主要緣由:

(1)   系統中進程數量太多了。

(2)   該實際用戶ID所擁有的進程數超過了系統限制(由CHILD_MAX指定)。

vfork

vfork函數用於建立一個新進程來執行一個新程序。vfork保證子進程先運行,在其調用exec或exit以後父進程纔可能被調用執行,因此若是在調用這兩個函數以前,子進程依賴於父進程的進一步動做,則會致使死鎖。

 

將上述程序用vfork重寫,因爲vfork能夠保證在調用exec或者exit以前,父進程不會執行,所以不用在父進程中調用sleep休眠了:

[root@benxintuzi process]# cat vfork.c
#include <unistd.h>
#include <stdio.h>

int             globvar = 6;            /* external variable in initialized data */
char    buf[] = "a write to stdout\n";

int main(void)
{
        int             var;            /* automatic variable on the stack */
        pid_t   pid;

        var = 88;
        if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
                printf("write error\n");

        printf("before vfork\n");       /* we don't flush stdout */

        if ((pid = vfork()) < 0) 
        {
                printf("fork error\n");
        } 
        else if (pid == 0) 
        {                                               /* child */
                globvar++;                              /* modify variables */
                var++;
                exit(0);                                /* child terminates */
        } 
        /* parent continues here */
        printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);

        return 0;
}

[root@benxintuzi process]# gcc vf./vfork
a write to stdout
before vfork
pid = 2295, glob = 7, var = 89

 

進程終止

進程有5種正常終止方式以及3種異常終止方式:

5種正常終止方式:

(1)   在main函數內執行return語句,等效於調用exit。

(2)   調用exit函數。exit操做包括調用各類終止處理函數(exit handler)。

(3)   調用_exit或_Exit函數。_Exit的存在是爲進程提供一種無需運行終止處理函數或者信號處理函數而終止的方式。_exit函數是由exit函數調用的,用於處理Unix系統特定的細節。

(4)   進程的最後一個線程在其啓動例程中執行return語句。可是該線程的返回值不用做進程的返回值。當最後一個線程從其啓動例程返回時,該進程以終止狀態0返回。

(5)   進程的最後一個線程調用pthread_exit函數。

 

3種異常終止方式:

(1)   調用abort,其產生SIGABRT信號。

(2)   進程接收到某些信號時。

(3)   最後一個線程對「取消」請求作出響應。

無論進程如何終止,最後都會執行內核中的同一段代碼,這段代碼爲相應進程關閉全部打開的文件描述符,釋放其所佔用的存儲器等。

 

獲取進程終止狀態

當一個進程正常或異常終止時,內核就會向其父進程發送SIGCHLD信號,進而父進程調用wait或waitpid獲取其子進程的終止狀態。調用wait時可能有以下狀況發生:

(1)   若是當前進程沒有任何子進程,則當即出錯返回。

(2)   若是全部子進程都在運行,則阻塞。

(3)   若是其中一個子進程已經終止,則取得該子進程的終止狀態後當即返回。

#include <sys/wait.h>

pid_t wait(int* statloc);

pid_t waitpid(pid_t pid, int* statloc, int options);

返回值:成功,返回進程ID;失敗,返回0或-1

說明:

進程的終止狀態存放於statloc指向的存儲空間中。

<sys/wait.h>中定義了4個宏,用來獲取進程的終止狀態:

WIFEXITED(status)標識正常終止;

WIFSIGNALED(status)標識異常終止;

WIFSTOPPED(status)標識當前暫停子進程;

WIFCONTINUED(status)標識暫停後繼續執行的子進程;

 

關於參數pid:

pid == -1: 等待任一子進程,此時waitpid與wait等效。

pid > 0: 等待與pid相等的子進程。

pid == 0: 等待組ID與調用進程組ID相等的任一子進程。

pid < -1: 等待組ID等於pid絕對值的任一子進程。

 

waitpid與wait的比較:

(1)   waitpid能夠等待一個特定的進程,而wait只能返回任一終止子進程的狀態。

(2)   waitpid提供了一個wait的非阻塞版本。

(3)   waitpid經過WUNTRACED/WCONTINUED選項支持做業控制。

 

[root@benxintuzi process]# cat wait01.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

void pr_exit(int status);

int main(void)
{
        pid_t   pid;
        int             status;

        if ((pid = fork()) < 0)
                printf("fork error\n");
        else if (pid == 0)                              /* child */
                exit(7);

        if (wait(&status) != pid)               /* wait for child */
                printf("wait error\n");
        pr_exit(status);                                /* and print its status */

        if ((pid = fork()) < 0)
                printf("fork error\n");
        else if (pid == 0)                              /* child */
                abort();                                        /* generates SIGABRT */

        if (wait(&status) != pid)               /* wait for child */
                printf("wait error\n");
        pr_exit(status);                                /* and print its status */

        if ((pid = fork()) < 0)
                printf("fork error\n");
        else if (pid == 0)                              /* child */
                status /= 0;                            /* divide by 0 generates SIGFPE */

        if (wait(&status) != pid)               /* wait for child */
                printf("wait error\n");
        pr_exit(status);                                /* and print its status */

        exit(0);
}

void pr_exit(int status)
{
        if (WIFEXITED(status))
                printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
                printf("abnormal termination, signal number = %d\n", WTERMSIG(status));
        else if (WIFSTOPPED(status))
                printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}

[root@benxintuzi process]# ./wait01
normal termination, exit status = 7
abnormal termination, signal number = 6
abnormal termination, signal number = 8

 

另外一個取得進程終止狀態的函數是waitid,與waitpid類似,但waitid容許一個進程指定要等待的子進程。它使用兩個參數表示要等待的子進程所屬的類型。

#include <sys/wait.h>

int waitid(idtype_t idtype, id_t id, siginfo_t* infop, int options);

返回值:成功,返回0;失敗,返回-1

說明:

idtype參數能夠以下:

P_PID:等待一個特定進程,id中包含要等待子進程的進程ID。

P_PGID:等待一個特定進程組的任一子進程,id中包含要等待子進程的進程組ID。

P_ALL:等待任一子進程,此時忽略id。

 

options參數以下(按位或):

WCONTINUED等待一個進程,其曾被中止,而後又繼續運行,但其狀態還沒有報告。

WEXITED:等待已退出的進程。

WNOHANG:若是沒有可用的子進程退出狀態,當即返回而非阻塞。

WNOWAIT不破壞子進程退出狀態,該子進程退出狀態可由後續的wait/waitpid/waitid取得。

WSTOPPED等待一個進程,它已經中止,但其狀態還沒有報告。

注:

必須指定WCONTINUED、WNOWAIT、WSTOPPED三者之一。

 

infop指向一個siginfo結構,包含了形成子進程狀態改變有關信號的詳細信息。

 

函數exec

當進程調用exec函數時,exec就用磁盤上的一個新程序替換掉當前進程的正文段、數據段、堆區、棧區,而後開始執行新程序的main函數。通常調用fork後當即會調用exec,表示建立新進程後,當即在該進程中執行新程序。有7種exec函數可用:

#include <unistd.h>

int execl(const char* pathname, const char* arg0, ... /* (char*)0 */);

int execv(const char* pathname, char* const argv[]);

int execle(const char* pathname, const char* arg0, ... /* (char*)0, char* const envp[] */);

int execve(const char* pathname, char* const argv[], char* const envp[]);

int execlp(const char* filename, const char* arg0, ... /* (char*)0 */);

int execvp(const char* filename, char* const argv[]);

int fexecve(int fd, char* const argv[], char* const envp[]);

返回值:成功,不返回;失敗,返回-1

 

說明:

關於filename,若是filename中包含/,則將其視爲路徑名;不然就在PATH指定的目錄中搜索可執行文件。

若是execlp和execvp找到了一個可執行文件,可是該文件不是由連接器產生的可執行文件,則就將其看做一個shell腳本,試着調用/bin/sh,並將該filename做爲shell的輸入。

函數名中的l表示列表list,v表示向量vector,e表示環境。所以execl/execlp/execle要求將新程序的每一個命令行參數都做爲一個獨立的參數傳遞,而execv/execvp/execve/fexecve將其按數組方式傳遞。execle/execve/fexecve要求傳遞一個環境表指針,其餘幾個exec函數則使用全局變量environ爲新程序複製現有的環境。

 

首先,查看PATH變量,發現裏面有HOME=/root一項,那麼將如下可執行文件echoall放到/root目錄下,echoall可執行文件源程序以下所示,用於打印當前進程的環境表:
#include <stdio.h>

int main(int argc, char *argv[])
{
        int                     i;
        char            **ptr;
        extern char     **environ;

        for (i = 0; i < argc; i++)              /* echo all command-line args */
                printf("argv[%d]: %s\n", i, argv[i]);

        for (ptr = environ; *ptr != 0; ptr++)   /* and all env strings */
                printf("%s\n", *ptr);

        return 0;
}

而後使用fork建立新進程,使用exec函數運行echoall程序: [root@benxintuzi process]# cat exec01.c
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

char    *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };

int main(void)
{
        pid_t   pid;

        if ((pid = fork()) < 0) {
                printf("fork error\n");
        } else if (pid == 0) {  /* specify pathname, specify environment */
                if (execle("/root/echoall", "echoall", "myarg1",
                                "MY ARG2", (char *)0, env_init) < 0)
                        printf("execle error\n");
        }

        if (waitpid(pid, NULL, 0) < 0)
                printf("wait error\n");

        if ((pid = fork()) < 0) {
                printf("fork error\n");
        } else if (pid == 0) {  /* specify filename, inherit environment */
                if (execlp("/root/echoall", "echoall", "only 1 arg", (char *)0) < 0)
                        printf("execlp error\n");
        }

        return 0;
}

[root@benxintuzi process]# ./exec01
argv[0]: echoall
argv[1]: myarg1
argv[2]: MY ARG2
USER=unknown
PATH=/tmp
[root@benxintuzi process]# argv[0]: echoall
argv[1]: only 1 arg
...
HOME=/root
_=./exec01

 

更改用戶ID和組ID

Unix系統中,訪問控制是基於用戶ID和組ID的,當程序須要訪問當前並不容許訪問的資源時,就須要增長特權,更換本身的用戶ID和組ID,使之具備合適的特權或訪問權限。

能夠用setuid函數設置實際用戶ID和有效用戶ID,用setgid設置實際組ID和有效組ID。關於內核維護的3個用戶ID,有以下說明:

(1)   只有超級用戶進程才能夠更改實際用戶ID。一般,實際用戶ID是在登陸時,由login程序設置的,而login是一個超級用戶進程,當它調用setuid時,設置全部3個用戶ID。

(2)   僅當文件中設置了保存用戶ID位時,exec函數纔會設置有效用戶ID。若是保存用戶ID位沒有設置,則exec函數不會改變有效用戶ID,而是維持其現有值不變。任什麼時候候均可以調用setuid將有效用戶ID設置爲實際用戶ID或者保存用戶ID。

(3)   保存用戶ID是由exec函數複製有效用戶ID而獲得的。若是設置了文件的保存用戶ID位,則在exec根據文件的用戶ID設置了進程的有效用戶ID後,這個副本就被保存起來了。

 

以下函數用戶交換實際ID和有效ID的值:

#include <unistd.h>

int setreuid(uid_t ruid, uid_t euid);

int setregid(gid_t rgid, gid_t egid);

返回值:成功,返回0;失敗,返回-1

說明:

一個非特權用戶可將其有效用戶ID設置爲實際用戶ID或者保存用戶ID,一個特權用戶則可將有效用戶ID設置爲uid,設置不一樣用戶ID的函數圖示以下:

 

 

解釋器文件

解釋器文件的起始行形式是:

#! pathname [optional-argument]

在!和pathname之間的空格是可選的,最多見的解釋器文件開始行爲:

#! /bin/sh

 

pathname一般是絕對路徑名。對解釋器文件的處理是由內核做爲exec系統調用的一部分來完成的。內核使調用exec函數的進程實際執行的並非解釋器文件自己,而是解釋器文件中第一行pathname指定的文件,以下所示:

[root@benxintuzi process]# cat /root/echoall.c
#include <stdio.h>

int main(int argc, char *argv[])
{
        int                     i;
        char            **ptr;
        extern char     **environ;

        for (i = 0; i < argc; i++)              /* echo all command-line args */
                printf("argv[%d]: %s\n", i, argv[i]);

        for (ptr = environ; *ptr != 0; ptr++)   /* and all env strings */
                printf("%s\n", *ptr);

        return 0;
}

[root@benxintuzi process]# cat /root/testinterp
#! /root/echoall echoarg1 echoarg2

[root@benxintuzi process]# cat interp.c
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>

int main(void)
{
        pid_t pid;

        if ((pid = fork()) < 0)
                printf("fork error\n");
        else if (pid == 0)
                if (execl("/root/echoall", "/root/testinterp", "myarg1", "myarg2", (char*)0) < 0)
                        printf("execl error\n");
        if (waitpid(pid, NULL, 0) < 0)
                printf("waitpid error\n");

        return 0;
}

[root@benxintuzi process]# ./interp
argv[0]: /root/testinterp
argv[1]: myarg1
argv[2]: myarg2
HOSTNAME=benxintuzi
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash
TERM=vt100
HISTSIZE=1000
HADOOP_HOME=/bigdata/hadoop-2.6.0
SSH_CLIENT=192.168.8.1 50293 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib/qt-3.3
QTINC=/usr/lib/qt-3.3/include
SSH_TTY=/dev/pts/0
USER=benxintuzi

 

進程調度

調度策略和調度優先級是由內核控制的,進程能夠經過調整nice值下降進程的調度優先級。nice越小,優先級越高。進程能夠經過nice函數獲取或更改其nice值,該操做隻影響進程本身的nice值,不影響其餘進程:

#include <unistd.h>

int nice(int lchr);

返回值:成功,返回新的nice值NZERO;失敗,返回-1

說明:

nice值的範圍通常爲0~(2*NAERO) – 1。

新的nice值new_nice = old_nice + lchr,若是超出nice值的範圍,自動降到最大、最小合法值。

 

#include <sys/resource.h>

int getpriority(int which, id_t who);

返回值:成功,返回-NZERO~NZERO – 1之間的nice值;失敗,返回-1

說明:

getpriority函數不只能夠獲取進程的nice值,還能夠獲取一組相關進程的nice值。

which參數以下:

PRIO_PROCESS:表示進程。

PRIO_PGRP:表示進程組。

PRIO_USER:表示用戶ID。

who參數選擇一個或者多個進程,若是爲0,表示選擇一個。

 

#include <sys/resource.h>

int setpriority(int which, id_t who, int value);

返回值:成功,返回0;失敗,返回-1

 

進程時間

#include <sys/times.h>

clock_t times(struct tms* buf);

返回值:成功,返回時間;失敗,返回-1

說明:

times函數填充tms結構體:

struct tms

{

    clock_t    tms_utime; /* user CPU time */

    clock_t tms_stime;   /* system CPU time */

    clock_t tms_cutime;  /* user CPU time, terminated children */

    clock_t tms_cstime;  /* system CPU time, terminated children */

};

sysconf(_SC_CLK_TCK)返回每秒時鐘的滴答數。

 

以下函數將命令行參數做爲程序運行,而且統計執行每一個程序的時間:

[root@benxintuzi process]# cat times.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/times.h>

static void     pr_times(clock_t, struct tms *, struct tms *);
static void     do_cmd(char *);

int main(int argc, char *argv[])
{
        int             i;

        setbuf(stdout, NULL);
        for (i = 1; i < argc; i++)
                do_cmd(argv[i]);        /* once for each command-line arg */
        return 0;
}

static void do_cmd(char *cmd)           /* execute and time the "cmd" */
{
        struct tms      tmsstart, tmsend;
        clock_t         start, end;
        int                     status;

        printf("\ncommand: %s\n", cmd);

        if ((start = times(&tmsstart)) == -1)   /* starting values */
                printf("times error\n");

        if ((status = system(cmd)) < 0)                 /* execute command */
                printf("system() error\n");

        if ((end = times(&tmsend)) == -1)               /* ending values */
                printf("times error\n");

        pr_times(end-start, &tmsstart, &tmsend);
        printf("status = %d\n", status);
}

static void pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend)
{
        static long             clktck = 0;

        if (clktck == 0)        /* fetch clock ticks per second first time */
                if ((clktck = sysconf(_SC_CLK_TCK)) < 0)
                        printf("sysconf error\n");

        printf("  real:  %7.2f\n", real / (double) clktck);
        printf("  user:  %7.2f\n",
          (tmsend->tms_utime - tmsstart->tms_utime) / (double) clktck);
        printf("  sys:   %7.2f\n",
          (tmsend->tms_stime - tmsstart->tms_stime) / (double) clktck);
        printf("  child user:  %7.2f\n",
          (tmsend->tms_cutime - tmsstart->tms_cutime) / (double) clktck);
        printf("  child sys:   %7.2f\n",
          (tmsend->tms_cstime - tmsstart->tms_cstime) / (double) clktck);
}

[root@benxintuzi process]# ./times "sleep 5" "date" "man bash > /dev/null"

command: sleep 5
  real:     5.03
  user:     0.00
  sys:      0.00
  child user:     0.00
  child sys:      0.00
status = 0

command: date
Sat Aug 29 20:26:49 PDT 2015
  real:     0.01
  user:     0.00
  sys:      0.00
  child user:     0.00
  child sys:      0.01
status = 0

command: man bash > /dev/null
  real:     0.69
  user:     0.00
  sys:      0.00
  child user:     0.19
  child sys:      0.49
status = 0 [root@benxintuzi process]#
相關文章
相關標籤/搜索