作者:mobiledu2502881683 | 来源:互联网 | 2017-11-04 21:25
1.racecondition多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序,这就发生了竞争条件。如果fork之后某种逻辑显式或者隐式依赖于fork之后父进程和子进程的执行顺序(这个顺序不可预知,由内核调度决定),也是racecondition经
1. race condition
多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序,这就发生了竞争条件。如果fork之后某种逻辑显式或者隐式依赖于fork之后父进程和子进程的执行顺序(这个顺序不可预知,由内核调度决定),也是race condition经常发生的情形。
如果一个进程希望等待一个子进程终止,则它必须调用wait函数。如果一个进程要等待其父进程终止,则可使用下列形式的循环:
while(getppid() !=1)
sleep(1);
这种形式的循环(称为定期询问(polling))的问题是它浪费了C P U时间,因为调用者每隔1秒都被唤醒,然后进行条件测试。
而通常的的实现是用信号和IPC机制。在子进程中实现TELL_PARENT(),WAIT_PARENT()函数/宏,父进程里实现WAIT_CLILD和TELL_CHILD宏。
2. exec
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。
exec, fork,wait和exit都是基本的进程控制原语,后面的popen、system之类的函数是基于这些基本的调用来构造的。
#include
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 [] );
/*六个函数返回:若出错则为-1,若成功则不返回*/
后两个如果包含/则被认为是一个路径名,否则则是文件名在PATH环境变量中查找相应的可执行文件。这个可执行文件也可以是一个解释器文件(#!)。
这几个函数中l表示list,v表示向量。list的execl,execle,execlp表示参数的是可变参数列表形式,最后要跟一个空指针表示结束(char *)0
以e结尾的两个函数(execle和execve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ变量为新程序复制现存的环境。(如果系统支持putenv和setenv也可以在后面生成的子进程中修改,但不能影响父进程的环境)
使用/显示当前进程的所有环境变量:
for (char **ptr = environ; *ptr != 0; ptr++)
printf("%s\n", *ptr);
在执行exec后,进程ID没有改变。除此之外,执行新程序的进程还保持了原进程的下列特征:
• 进程ID和父进程ID。
• 实际用户ID和实际组ID。
• 添加组ID。
• 进程组ID。
• 对话期ID。
• 控制终端。
• 闹钟尚余留的时间。
• 当前工作目录。
• 根目录。
• 文件方式创建屏蔽字。
• 文件锁。
• 进程信号屏蔽。
• 未决信号。
• 资源限制。
• tms_utime, tms_stime, tms_cutime以及tmsustime值。
对打开文件的处理与每个描述符的exec关闭标志值(FDCLOEXEC)有关。若此标志设置,则在执行exec时关闭该描述符,否则该描述符仍打开。除非特地用f c n t l设置了该标志,否则系统的默认操作是在exec后仍保持这种描述符打开。
3. setuid(设置真实的UID)getuid,setreuid,seteuid,setfsuid
#include
int setuid(uid_t uid)
//执行成功则返回0,失败则返回-1,错误代码存于errno。
setuid()用来重新设置执行目前进程的UID。当有效的UID必须为0(root)时。在Linux下,当root 使用setuid()来变换成参数中的UID时(EUID和UID同时变为参数中的uid_t uid),也就是说,该进程往后将不再具有可setuid()的权利,如果只是向暂时抛弃root权限,稍后想重新取回权限,则必须使用seteuid()。如果是非root用户使用setuid这个函数,它只能用来
一般在编写具setuid root的程序时,为减少此类程序带来的系统安全风险,在使用完root权限后建议马上执行setuid(getuid());来抛弃root权限。此外,进程uid和euid不一致时Linux系统将不会产生core dump
4. system函数:执行一个字符串命令。
#include
int system(const char* cmdstring);
在实现中调用了fork,exec和waitpid。
5. process accounting
当取了这种选择项后,每当进程结束时内核就写一个会计记录。典型的会计记录是3 2字节长的二进制数据,包括命令名、所使用的CPU时间总量、用户ID和组ID、起动时间等。
会计记录所需的各个数据(各C P U时间、传输的字符数等)都由内核保存在进程表中,并在一个新进程被创建时置初值(例如fork之后在子进程中,内核为子进程初始化一个记录,而不是在新程序exec时。所以会计记录对应的是进程而不是程序。)。进程终止时写一个会计记录。这就意味着在会计文件中记录的顺序对应于进程终止的顺序,而不是它们起动的顺序。为了确定起动顺序,需要读全部会计文件,并按起动日历时间进行排序。
#include
#include
#define ACCFILE "/var/adm/pacct"
......
struct acct acdata;
......
if ( (fp = fopen(ACCTFILE, "r")) == NULL)
err_sys("can't open file", ACCTFILE);
while (fread(&acdata, sizeof(acdata), 1, fp) == 1) {
printf(......acdata.ac_comm...ac.etime)
.......
}
6. 获得而用户登陆名,而非用户标志。
#include
char *getlogin(void);
得到了登录名,就可用getpwnam在口令文件中查找相应记录以确定其登录shell等。
7. 进程时间
#include
clock_t times(struct tms *buf);
获得一个起始时间和一个终止时间,然后经过static long clktck=0; clktck=sysconf(_SC_CLK_TCK); start_times->tms_utime/(double)clktck。
struct tms {
clock_t tms_utime; /* 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 */
}
textbook:APUE