UNIX高级环境编程读书笔记(chapter8)

来源:互联网 发布:软件定制平台 编辑:程序博客网 时间:2024/06/09 13:41
第八章 进程控制
每个进程都有一个非负整型表示唯一的进程ID.虽然是唯一的,但是进程ID可以重用,当一个进程终止后,其进程ID就可以再次使用了.ID为0的进程通常是调度进程,常常是交换进程,是内核的一部分,也被称为系统进程.进程ID1通常是init进程,在自举过程结束时内核调用.
8.1.由fork创建的新进程被称为子进程.fork函数被调用一次单返回两次.两次返回的唯一区别是子进程的返回值是0, 而父进程的返回值则是新子进程的进程ID.一个子进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID.子进程是父进程的副本,但父、子进程并不共享这些存储空间部分.父子进程共享正文段.
#include "apue.h"int glob = 6;char buf[] = "a write to stdout\n";intmain(void){    int var;    pid_t pid;    var = 88;    if (write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) - 1)    {        err_sys("write error");    }    printf("before fork\n");    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid == 0)    {        glob++;        var++;    }    else    {        sleep(5);    }    printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);    exit(0);}
注:一般来说在fork之后是父进程先执行还是子进程先执行是不确定的.strlen计算不包含终止null字节的字符串长度,而sizeof则计算包括终止null字节的缓冲区长度.

8.2.文件共享
父、子进程的每个相同的打开文件描述符共享一个文件表项.
8.3. vfork函数
vfork用于创建一个新进程,而该新进程的目的是exec一个新程序.

vfork和fork之间的一个区别是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行.(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁).子进程对变量glob和var做增1操作,结果改变了父进程中的变量值.因为子进程在父进程的地址空间中运行.


8.4. 如果父进程在子进程之前终止,它们的父进程都改变为init进程,我们称这些进程由init进程领养.这种处理方法保证了每个进程都有一个父进程.

一个已经终止,但是其父进程尚未对其进行善后处理的进程被称为僵死进程. ps(1)将僵死进程的状态打印为Z.

8.5. wait 和 waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD新号.
在这种情况下, 调用wait或waitpid的进程可能会发生的情况:
(1)如果其所有子进程都还在运行,则阻塞;
(2)如果一个子进程已终止, 正等待父进程获取其终止状态,则取得该子进程的终止状态,立即返回.
(3)如果它没有任何子进程,则立即出错返回.


wait等待一个特定进程的函数.


8.6. 打印exit状态的说明:
#include "apue.h"#include <sys/wait.h>voidpre_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%s\n", WTERMSIG(status),                #ifdef WCOREDUMP                         WCOREDUMP(status) ? "(core file generated)" : "");                #else                    "")                #endif    }    else if (WIFSTOPPED(status))    {        printf("child stopped, signal number = %d\n", WSTOPSIG(status));    }}


8.7 演示不同的exit值

#include "apue.h"#include <sys/wait.h>static void pre_exit(int status);int main(void){    pid_t pid;    int status;    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid == 0)    {        exit(7);    }    if (wait(&status) != pid)    {        err_sys("wait error");    }    pre_exit(status);    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid == 0)    {        abort();    }    if (wait(&status) != pid)    {        err_sys("wait error");    }    pre_exit(status);    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid == 0)    {        status /= 0;    }    if (wait(&status) != pid)    {        err_sys("wait error");    }    pre_exit(status);    exit(0);}

8.8 waitpid函数提供了wait函数没有提供的三个功能:
(1)waitpid 可等待一个特定的进程.
(2)waitpid提供了一个wait的非阻塞版本
(3)waitpid支持作业控制

注:调用fork两次以避免僵死进程.

8.9 waitid函数
此函数类似于waitpid,但提供了更多的灵活性.
它使用单独的参数表示要等待的子进程的类型,而不是将此与进程ID或进程组ID组合成一个参数.
8.10 wait3和wait4函数
它要求内核返回由终止进程及其所有子进程使用的资源汇总.
8.11. 竞争条件
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则我们认为这发生了竞争条件.
如果一个进程希望等待一个子进程终止, 则它必须调用一种wait函数. 如果一个进程要等待其父进程终止,则可以使用下列循环:
while(getppid() != 1)
{
sleep(1);
}


8.12. 具有竞争条件的程序

#include "apue.h"#include "tellwait.h"static void charatatime(char *str);int main(void){    pid_t pid;    TELL_WAIT();    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid == 0)    {        WAIT_PARENT();        charatatime("output from child\n");    }    else    {        charatatime("output from parent\n");        TELL_CHILD(pid);    }    exit(0);}static void charatatime(char *str){    char *ptr;    int c;    setbuf(stdout, NULL);    for (ptr = str; (c = *ptr++) != 0; )    {        putc(c, stdout);    }}





8.13. exec函数
用fork可以创建进程,用exec可以执行新程序. exit函数和两个wait函数处理终止和等待终止.这些是我们需要的基本的进程控制语句.
PATH变量包含了一张目录表(称为路径前缀), 目录之间用冒号(:)分隔.
函数execl,execlp,execle要求讲新程序的每个命令行参数都说明为一个单独的参数.这种参数表以空指针结尾.
函数execv,execvp和execve,则应先构造一个指向各参数的直针数组,然后讲该数组地址作为这三个函数的参数.

在执行exec之后,进程的ID没有改变.除此之外,执行新程序的进程还保持了原进程的下列特征(见 P190);


8.14. system函数
在程序中执行一个命令字符串很方便。

system函数实例:

#include <sys/wait.h>#include <errno.h>#include <unistd.h>int system(char *cmdstring){    pid_t pid;    int status;    if (cmdstring == NULL)    {           return 1;    }       if ((pid = fork()) < 0)    {           status = -1;     }       else if (pid == 0)    {           execl("bin/sh", "sh", "-c", cmdstring, (char *)0);        _exit(127);    }       else    {           while (waitpid(pid, &status, 0) < 0)        {               if (errno = EINTR)            {                   status = -1;             }               break;        }       }    return(status);}


设置用户ID程序

gcc -o tsys 8.14.c

#include "apue.h"#include <sys/wait.h>static void pre_exit(int status);int main(int argc, char *argv[]){    int status;    if (argc < 2)    {        err_quit("command-line argument required");    }    if ((status = system(argv[1])) < 0)    {        err_sys("system() error");    }    pre_exit(status);    exit(0);}voidpre_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%s\n", WTERMSIG(status),                #ifdef WCOREDUMP                         WCOREDUMP(status) ? "(core file generated)" : "");                #else                    "");                #endif    }    else if (WIFSTOPPED(status))    {        printf("child stopped, signal number = %d\n", WSTOPSIG(status));    }}

gcc -o printuids 8.15.c

#include "apue.h"int main(void){    printf("real uid = %d, effictive uid = %d\n", getuid(), geteuid());    exit(0);}

结果:
real uid = 0, effictive uid = 0
normal termination exit status = 0
如果一个进程正以特殊的权限运行,它又想生成另一个进程执行另一个程序,则它应当直接使用fork和exec,而且
在fork之后,exec之前要改回到普通权限.设置用户ID程序决不应调用system函数.


8.16 进程会计
启用该选项后,每当进程结束时内核就写一个会计记录.
函数(acct)用于启用和禁用进程会计.唯一使用这一函数的是accton(8)命令.超级用户执行一个带路径名参数的
accton命令启动会计处理.会计记录的结构定义在<sys/acct.h>中.其中, ac_flag成员记录了进程执行期间的某些事件.
会计记录对应于进程而不是程序.


#include "apue.h"int main(void){    pid_t pid;    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid != 0)    {        sleep(2);        exit(2);    }    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid != 0)    {        sleep(4);        abort;    }    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid != 0)    {        execl("/bin/dd", "dd", "if=/etc/termcap", "of=/dev/null", NULL);        exit(7);    }    if ((pid = fork()) < 0)    {        err_sys("fork error");    }    else if (pid != 0)    {        sleep(8);        exit(0);    }    sleep(6);    kill(getpid(), SIGKILL);    exit(6);}#include "apue.h"#include <sys/acct.h>#ifdef HAS_SA_START#define FMT "%-*.*s e = %6ld, chars = %7ld, stat = %3u: %c %c %c %c\n"#else#define FMT "%-*.*s e = %6ld, chars = %7ld, %c %c %c %c\n"#endif#ifndef HAS_ACORE#define ACORE 0#endif#ifndef HAS_AXSIG#define AXSIG 0#endifstatic unsigned longcompt2ulong(comp_t comptime){    unsigned long val;    int exp;    val = comptime & 0x1fff;    exp = (comptime >> 13) & 7;    while (exp-- > 0)    {        val *= 8;    }    return val;}int main(int argc, char *argv[]){    struct acct acdata;    FILE *fp;    if (argc != 2)    {        err_quit("usage: pracct filename");    }    if ((fp = fopen(argv[1], "r")) == NULL)    {        err_sys("can't open %s\n", argv[1]);    }    while (fread(&acdata, sizeof(acdata), 1, fp) == 1)    {        printf(FMT, (int)sizeof(acdata.ac_comm),(int)sizeof(acdata.ac_comm), acdata.ac_comm,                        compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io),        #ifdef HAS_SA_START            (unsigned char) acdata.ac_stat,        #endif        acdata.ac_flag & ACORE ? 'D' : ' ',        acdata.ac_flag & AXSIG ? 'X' : ' ',        acdata.ac_flag & AFORK ? 'F' : ' ',        acdata.ac_flag & ASU ? 'S' : ' ');    }    if (ferror(fp))    {        err_sys("read error");    }    exit(0);}

8.17用户标识
系统通常记录用户登陆时使用的名字, 用getlogin函数可以获取此登录名.
8.18. 进程时间
我们可以测量的三种时间:墙上时钟时间, 用户CPU时间和系统CPU时间.任意进程都可以调用 times函数以获得它自己及已终止子进程的上述值.

#include "apue.h"#include <sys/times.h>static void pr_times(clock_t, struct tms *, struct tms *);static void do_cmd(char *);void pr_exit(int status);int main(int argc, char *argv[]){    int i;    setbuf(stdout, NULL);    for (i = 1; i < argc; i++)    {        do_cmd(argv[i]);    }    exit(0);}static void do_cmd(char *cmd){    struct tms tmsstart, tmsend;    clock_t start, end;    int status;    printf("\ncommond: %s\n", cmd);    if ((start = times(&tmsstart)) == -1)    {        err_sys("times error");    }    if ((status = system(cmd)) < 0)    {        err_sys("system() error");    }    if ((end = times(&tmsend)) == -1)    {        err_sys("times error");    }    pr_times(end-start, &tmsstart, &tmsend);    pr_exit(status);}static voidpr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend){    static long clktck = 0;    if (clktck == 0)    {        if ((clktck = sysconf(_SC_CLK_TCK)) < 0)        {            err_sys("sysconf error");        }    }    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);}voidpr_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%s\n", WTERMSIG(status),                #ifdef WCOREDUMP                         WCOREDUMP(status) ? "(core file generated)" : "");                #else                    "");                #endif    }    else if (WIFSTOPPED(status))    {        printf("child stopped, signal number = %d\n", WSTOPSIG(status));    }}



                                             
0 0
原创粉丝点击