本章解释了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
#include
#include
#include
#include
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);;
}
用非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
#include
#include
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
#include
#include
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;
}
子进程已经死亡,而父进程并没有接收,这会造成子进程变成僵尸进程(Zombie)。如下示例中,在主进程sleep的两百秒内,子进程将变成僵尸进程。
#include
#include
#include
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
#include
#include
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));
}
}
#include
#include
#include
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
#include
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
#include
#include
#include
int main()
{
printf("User is %s\n", getlogin());
}
通过下面demo可尝试打印对应的进程以及子进程的时间信息。
#include
#include
#include
#include
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);
}
}