進程的特權基於用戶ID,因此更改進程對象中用戶ID就像更改進程的特權,以下是若干個能夠更改進程對象用戶ID的接口:node
setuid(uid)shell
if(進程具備超級用戶特權)/* 是指有效用戶ID==0 */ 實際用戶 ID=uid; 有效用戶 ID=uid; 保存的設置用戶 ID=uid; else if(uid==實際用戶 ID || uid==保存的設置用戶 ID) 有效用戶 ID=uid; else return -1;
setgid(gid)編程
if(進程具備超級用戶特權)/* 是指有效用戶ID==0 */ 實際組 ID=gid; 有效組 ID=gid; 保存的設置組 ID=gid; else if(gid==實際組 ID || gid==保存的設置組 ID) 有效組 ID=gid; else return -1;
setreuid(ruid,euid)數組
if(ruid == 實際用戶ID || ruid == 有效用戶ID) 實際用戶ID = ruid; if(euid == 實際用戶ID || euid == 有效用戶ID || euid==保存的設置用戶ID) 有效用戶ID = euid; 保存的設置用戶ID = 有效用戶ID; /* If the real user ID is set or the effective user ID is set to a value not equal to the previous real user ID, the saved set-user-ID will be set to the new effective user ID. TODO,如何理解這句話, */
我對這句話(上面註釋裏買你的英語)的理解就是"當實際用戶ID或有效用戶ID更改成一個不一樣於原有的實際用戶ID的值時,保存的設置用戶ID會設置爲新的有效用戶ID",按照這個理論也就是說,數據結構
生成一個程序,全部者爲root;設置 SUID 標誌,而後以普通用戶(如 ww)啓動,此時進程的三個ID應該是:ruid=ww;euid=root;保存的設置用戶ID=root函數
此時調用 setreuid(getuid(),getuid());此時:ruid=ww;euid=ww;保存的設置用戶ID=root;[由於此時 euid 的新值與原來的 ruid 一致,因此不更改保存的設置用戶ID]測試
再調用 setreuid(getuid(),root);此時 root== 保存的設置用戶ID,因此理論上應該會將有效用戶ID設置爲 root;可是測試發現此時提示 Operation not permitted,並且有效用戶ID沒變,猜想應該是第2步將保存的設置用戶ID修改成了有效用戶ID的新值(即ww),who know;ui
ww$ cat main.c #include <unistd.h> #include <stdio.h> int main(int argc,char *argv[]){ int ruid = getuid(); int euid = geteuid(); printf("ruid:%d;euid:%d\n",getuid(),geteuid()); if( setreuid(ruid,ruid)<0 ) printf("%m\n"); printf("ruid:%d;euid:%d\n",getuid(),geteuid()); if( setreuid(ruid,euid)<0 ) printf("%m\n"); printf("ruid:%d;euid:%d\n",getuid(),geteuid()); return 0; } ww$ gcc main.c -o test ww$ su 密碼: root# chown root:root test root# chmod +s test root# ls -l test -rwsrwsr-x 1 root root 8671 2月 4 00:03 test root# exit ww$ ./test ruid:1000;euid:0 ruid:1000;euid:1000 Operation not permitted ruid:1000;euid:1000
setregid(),同 setreuid(),只是將用戶ID更改成組ID.spa
seteuid(uid)命令行
if(進程具備超級用戶特權)/* 是指有效用戶ID==0 */ 有效用戶 ID=uid; else if(uid==實際用戶 ID || uid==保存的設置用戶 ID) 有效用戶 ID=uid; else return -1;
setegid(),同 seteuid(),
文件建立模式屏蔽字,在建立文件(包括任何類型的文件)時起做用.如:在調用 open(),mkdir() 建立文件時,文件的實際權限 = mode(參數指定的權限) & ~umask.
/* 此時新建目錄的權限應該是 rwxrwxrwx(0777),可是因爲 umask=0002, 因此目錄的實際權限: 0777 & ~0002 = 111111101b,即 rwxrwxr-x. */ safe_mkdir(argv[1],0777);
在進程對象中,並無保存當前工做目錄的完整路徑名,而只是存放着一個指針,該指針指向着當前工做目錄對應的 v-node(參考Unix環境高級編程第三章,打開文件的數據結構).
int origin_cwd_fd = safe_open(".",O_RDONLY);/* 保存當前工做目錄,即不使用 getcwd() 來保存當前工做目錄 */ safe_chdir("..");/* 切換當前工做目錄 */ safe_fchdir(origin_cwd_fd);/* 恢復當前工做目錄 */
附加組 ID,是一個 gid_t 類型的數組,主要用於測試進程對文件是否具備訪問的權限.
經過 getgroups()/setgroups() 來設置與獲取進程組 ID,具體函數說明以下:
/** * 獲取進程當前附加組 ID. * @param size 若爲0,則返回附加組 ID 的數目,進程能夠先獲取附加組 ID 的數目,而後在複製足夠的空間. */ int getgroups(int size, gid_t *list); /** 將 list 指向的長度爲 size 的數組設置爲進程的附加組 ID,內核不會檢測附加組 ID 的有效性.如 @code gid_t group_list[]={1,2,3,4,5};//內核不會檢測是否存在 ID 爲 1 的用戶組. setgroups(sizeof(group_list)/sizeof(gid_t),group_list); @endcode */ int setgroups(size_t size, const gid_t *list);
每一個進程都有一組資源限制,用於限制該進程對資源的使用,防止過分使用,相關數據結構與接口以下:
struct rlimit { rlim_t rlim_cur;/* 資源的的當前限制值,即進程對資源的使用不能超過該值 */ rlim_t rlim_max;/* rlim_cur 的最大值, */ }; /* 若 rlim_cur,rlim_max 值爲 RLIM_INFINITY,則表示無限制 */ int getrlimit(int resource, struct rlimit *rlim);/* 獲取對進程在資源 resource 的限制,並存放在 rlim 指向的結構體中. */ int setrlimit(int resource, const struct rlimit *rlim);
int main(int argc,char *argv[]){ struct rlimit res_limit; getrlimit(RLIMIT_CPU,&res_limit);/* 獲取 CPU 使用時間的資源限制 */ ByteArray str("cpu: "); print_rlimit(&res_limit,str); puts(str.constData()); res_limit.rlim_cur = 1; setrlimit(RLIMIT_CPU,&res_limit);/* 將對 CPU 使用時間的資源限制設置爲 1 s. */ for(;;);/* 執行 1s 後將收到信號 SIGXCPU */ return 0; } // 執行輸出: $./Test cpu: rlim_cur: 無限制,rlim_max: 無限制 超出 CPU 時限 (核心已轉儲)
uint alarm(uint seconds);將剩餘鬧鐘時間設置爲 seconds,並返回以前的剩餘鬧鐘時間,當 seconds 大於0時,剩餘鬧鐘事件會每隔1s自減1直至爲0,當剩餘鬧鐘時間減至0時,會發送 SIGALRM 信號到當前進程.
屏蔽信號集,參見"Unix環境高級編程-9-信號"記錄了其詳細用途.
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);設置屏蔽信號集,或者獲取當前屏蔽信號集,流程以下:
若是 oldset 不爲0,則將當前屏蔽信號集複製到 oldset 指向的信號集中.
若是 set 不爲0,則按照 how 的值來設置屏蔽信號集,how 可取:
SIG_BLOCK,此時新的屏蔽信號集爲原有的屏蔽信號集與 set 的並集.
SIG_UNBLOCK,此時新的屏蔽信號集爲原有的屏蔽信號集與 set 補集的交集,即從原有的屏蔽信號集中移除在 set 中包含的信號
SIG_SETMASK,此時將 set 設置爲新的屏蔽信號集.
未決信號集,存放着已經產生,可是因爲被進程阻塞而未遞送給進程的信號.
int sigpending(sigset_t *set);將當前線程與當前進程未決信號集的並集存入 set 中.
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);用來獲取當前的信號處理方式,或者設置新的信號處理方式,流程以下:
若是 oldact 不爲0,則將當前對信號 signum 的處理方式複製到 oldact 指向的緩衝區中.
若是 act 不爲0,則將 act 設置爲信號 signum 新的處理方式.
struct sigaction,具體地描述了信號處理方式,具體結構:
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; };
sa_handlder,能夠是信號處理函數的指針,或者是 SIG_DFL,SIG_IGN.
sa_sigaction,只能是信號處理函數的指針,當 sa_flags 中設置了 SA_SIGINFO 標誌時,調用 sa_sigaction 指向的信號處理函數;不然調用 sa_handler 指向的信號處理函數.
sa_mask,在調用信號處理函數以前,會將 sa_mask 添加到進程的屏蔽信號集中(SIG_BLOCK),並在調用信號處理函數以後,恢復進程的屏蔽信號集.
sa_flags,位掩碼,常見的標誌以下:
SA_RESTART,自動重啓被中斷的系統調用,以下:
int main(int argc,char *argv[]){ struct sigaction int_sigaction; sigemptyset(&int_sigaction.sa_mask); int_sigaction.sa_flags = argc>1 ? SA_RESTART : 0; int_sigaction.sa_handler = sig_handler; sigaction(SIGINT,&int_sigaction,0); printf("sa_flags=%s\n",int_sigaction.sa_flags==SA_RESTART ? "restart" : "0"); int ret = read(0,&argc,sizeof(argc)); if(ret < 0) printf("func=read,errno=%d,error=%s\n",errno,strerror(errno)); return 0; }
$ ./Test --restart # 此時會設置 SA_RESTART sa_flags=restart ^C捕捉到信號,信號=SIGINT # 在執行信號處理函數以後自動重啓被中斷的系統調用 read(), ^C捕捉到信號,信號=SIGINT ^C捕捉到信號,信號=SIGINT ^C捕捉到信號,信號=SIGINT ^\退出 (核心已轉儲) $ ./Test sa_flags=0 ^C捕捉到信號,信號=SIGINT # 在執行信號處理函數以後,read() 出錯返回.以下: func=read,errno=4,error=Interrupted system call
SA_NODEFER,執行信號處理程序以前,內核不自動阻塞該信號(不然,內核會將該信號添加到進程的信號屏蔽集中)
int main(int argc,char *argv[]){ struct sigaction int_sigaction; sigemptyset(&int_sigaction.sa_mask); int_sigaction.sa_flags = argc>1 ? SA_NODEFER : 0; int_sigaction.sa_handler = sig_handler; sigaction(SIGINT,&int_sigaction,0); print_cur_sigprocmask("在調用 pause() 以前"); pause(); print_cur_sigprocmask("在調用 pause() 以後"); return 0; }
$ ./Test 在調用 pause() 以前,屏蔽信號集: 空 ^C在信號處理函數中,屏蔽信號集: SIGINT # 能夠看出,在執行信號處理函數以前,內核自動阻塞了 SIGINT 信號 捕捉到信號,信號=SIGINT 在調用 pause() 以後,屏蔽信號集: 空 $ ./Test --nodefer 在調用 pause() 以前,屏蔽信號集: 空 ^C在信號處理函數中,屏蔽信號集: 空 # 能夠看出,在設置了 SA_NODEFER 以後,在執行信號處理函數以前,內核不會阻塞 SIGINT 信號 捕捉到信號,信號=SIGINT 在調用 pause() 以後,屏蔽信號集: 空
SA_RESETHAND,在執行信號處理函數,將信號的處理方式設置爲默認(SIG_DFL).
void sig_handler(int s){ print_sigaction(SIGINT,"在信號處理函數中"); return; } int main(int argc,char *argv[]){ struct sigaction int_sigaction; sigemptyset(&int_sigaction.sa_mask); int_sigaction.sa_flags = argc>1 ? SA_RESETHAND : 0; int_sigaction.sa_handler = sig_handler; sigaction(SIGINT,&int_sigaction,0); print_sigaction(SIGINT,"在調用 pause() 以前"); pause(); print_sigaction(SIGINT,"在調用 pause() 以後"); return 0; }
$ ./Test 在調用 pause() 以前;信號=SIGINT;處理方式=捕捉;函數=0x400efd ^C在信號處理函數中;信號=SIGINT;處理方式=捕捉;函數=0x400efd 在調用 pause() 以後;信號=SIGINT;處理方式=捕捉;函數=0x400efd $ ./Test --reset-handler 在調用 pause() 以前;信號=SIGINT;處理方式=捕捉;函數=0x400efd ^C在信號處理函數中;信號=SIGINT;處理方式=默認 # 此時,在調用信號處理函數以前,已經將對信號 SIGINT 的處理方式,設置爲默認. 在調用 pause() 以後;信號=SIGINT;處理方式=默認
SA_SIGINFO,參見 sa_handler,sa_sigaction.
SA_NOCLDWAIT,此時不會產生殭屍進程,大概是這樣的,當子進程終止時,內核會根據子進程的終止狀態生成 siginfo_t 對象,而後調用 SIGCHLD 的信號處理函數,而後內核回收子進程,不會產生殭屍進程.
SA_NOCLDSTOP,此時丟棄子進程由於暫停,或者從新喚醒而發出 SIGCHLD 信號,即此時不會調用信號處理函數.
SA_NOCLDWAIT,SA_NOCLDSTOP 僅當爲 SIGCHLD 設置信號處理方式時,纔有效.
/* 解析命令行參數,獲取 sigaction 中 sa_flags 的值 */ int get_sa_flags(int argc,char *argv[]){ if(argc <= 1) return 0; if(strcmp(argv[1],"--no-stop") == 0) return SA_NOCLDSTOP; if(strcmp(argv[1],"--no-wait") == 0) return SA_NOCLDWAIT; return SA_NOCLDSTOP|SA_NOCLDWAIT; } /* 這個例子演示了 SA_NOCLDWAIT,SA_NOCLDSTOP 標誌 */ int main(int argc,char *argv[]){ if(safe_fork() == 0){ printf("child:pid=%d\n",getpid()); for(int i=0;i>=0;++i) ;/* 大約會花費 3.5 s */ for(int i=0;i>=0;++i) ; exit(33); }else{ struct sigaction chld_sigaction; chld_sigaction.sa_flags = SA_SIGINFO|get_sa_flags(argc,argv); sigemptyset(&chld_sigaction.sa_mask); chld_sigaction.sa_sigaction = chld_sighandler; sigaction(SIGCHLD,&chld_sigaction,0); puts("parent:ready"); for(;;) pause(); } return 0; }
$ ./Test #首先即不設置 NO_CLDWAIT,也不設置 NO_CLDSTOP parent:ready child:pid=3640 捕捉到了 SIGCHLD 信號;child_pid=3640;status=暫停;信號=SIGSTOP #在另一個 shell 窗口運行 kill -SIGSTOP 3640,能夠看到此時父進程收到了子進程由於暫停而發送的 SIGCHLD 信號. 捕捉到了 SIGCHLD 信號;child_pid=3640;status=從新喚醒;信號=SIGCONT #在另一個 shell 窗口運行 kill -SIGCONT 3640,能夠看到此時父進程收到了子進程由於暫停而發送的 SIGCHLD 信號. 捕捉到了 SIGCHLD 信號;child_pid=3640;status=正常退出;exit_code=33 # 此後在另一個窗口運行 ps -e | grep Test,能夠看到 # 3639 pts/3 00:00:00 Test # 3640 pts/3 00:00:07 Test <defunct> 子進程變爲了殭屍進程. ^C #終止父進程 $ ./Test --no-wait # 此時只設置 SA_NOCLDWAIT parent:ready child:pid=3648 捕捉到了 SIGCHLD 信號;child_pid=3648;status=暫停;信號=SIGSTOP # 同上,父進程會收到子進程由於暫停,或者暫停後喚醒而發出的 SIGCHLD 信號. 捕捉到了 SIGCHLD 信號;child_pid=3648;status=從新喚醒;信號=SIGCONT 捕捉到了 SIGCHLD 信號;child_pid=3648;status=正常退出;exit_code=33 # 此後在另一個窗口運行 ps -e | grep Test,能夠看到 # 3647 pts/3 00:00:00 Test 此時只有父進程,子進程沒有變爲殭屍進程, ^C # 終止父進程 $ ./Test --no-stop parent:ready child:pid=3656 # 此時父進程沒法收到子進程由於暫停,或者從新喚醒發出的 SIGCHLD 信號. 捕捉到了 SIGCHLD 信號;child_pid=3656;status=正常退出;exit_code=33 ^C
.rodata,.data,存放的是有初始值的變量,因此須要將變量的初始值存放在可執行文件中,在調用 exec() 時,會從可執行文件中將初始值加載到進程的地址空間中.
.bss 存放的是初始值爲0的變量,因此不須要將其初始值保存在可執行文件中,
int a = 33;/* 此時須要在可執行文件中佔用 4 個字節來保存變量 a 的初始值 33. */
環境字符串,結構爲 Name=Value;
/** 遍歷 environ 指向的環境表,查找名爲 name 的環境變量,而後獲取該環境變量的值. 若不存在名爲 name 的環境變量,則返回 0,不然返回一個指針,指向着環境字符串的 value 部分. */ char *getenv(const char *name); /** 修改,或者新增一個環境變量,string 的格式要求爲:Name=Value,若名爲 Name 的環境變量已經存在,則刪除之. 該函數僅是將 string 放入環境表中,並不會爲環境字符串分配空間. */ int putenv(char *string); /** 修改,或者新增環境變量,此時會爲環境字符串分配空間,而後將 name=value 複製到新分配的空間中,而後將空間的首地址存放到環境表中. @param overwrite 指示着當名爲 name 的環境變量已經存在時,是否修改該環境變量. */ int setenv(const char *name, const char *value, int overwrite); /** 刪除名爲 name 的環境變量. 此時就是遍歷 environ 指向的環境表,肯定 name 所在的環境表項,而後從環境表中益處該項. */ int unsetenv(const char *name);
pid,ppid;即進程 ID,父進程 ID.
子進程的進程對象中,tms_utime,tms_stime,tms_cutime,tms_cstime 這幾個字段被清爲0.
子進程的進程對象中,剩餘的鬧鐘時間被清爲0.
子進程的進程對象中,未處理的信號集被清空.
父進程擁有的文件鎖並不會被繼承.
關閉設置了 FD_CLOEXEC 標誌的文件描述符.
可能會更改進程對象的有效ID,保存的設置ID.以下:
if(可執行文件.SUID==1) 進程.有效用戶 ID = 可執行文件.擁有者ID. 進程.保存的設置用戶ID = 進程.有效用戶ID if(可執行文件.SGID == 1) 進程.有效組 ID = 可執行文件.擁有組 ID. 進程.保存的設置組ID = 進程.有效組ID.
信號的信號處理方式,即若信號的信號處理方式爲捕捉,則更改成默認.以下:
/* ./test 程序 */ int main(int argc,char *argv[]){ print_sigaction(SIGCHLD); } /* ./Test */ int main(int argc,char *argv[]){ struct sigaction chld_sigaction; chld_sigaction.sa_flags = SA_SIGINFO; sigemptyset(&chld_sigaction.sa_mask); chld_sigaction.sa_sigaction = chld_sighandler; sigaction(SIGCHLD,&chld_sigaction,0); print_sigaction(SIGCHLD); execl("./test","./test",(char*)0); return 0; }
$ ./Test 信號=SIGCHLD;處理方式=捕捉(SA_SIGINFO);函數=0x40115d # 在執行 execl() 以前, 信號=SIGCHLD;處理方式=默認 # execl() 將對 SIGCHLD 信號的處理方式更改成默認.