[APUE] 再读之进程控制

来源:互联网 发布:无痕植发的价格 知乎 编辑:程序博客网 时间:2024/05/22 13:09

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





0 0