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操作,结果改变了父进程中的变量值.因为子进程在父进程的地址空间中运行.
一个已经终止,但是其父进程尚未对其进行善后处理的进程被称为僵死进程. 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
- UNIX高级环境编程读书笔记(chapter8)
- 《Unix环境高级编程》读书笔记(1)
- Unix环境高级编程读书笔记(一)
- UNIX高级环境编程读书笔记(chapter4)
- UNIX高级环境编程读书笔记(chapter5)
- UNIX高级环境编程读书笔记(chapter7)
- UNIX高级环境编程读书笔记(chapter9)
- UNIX高级环境编程读书笔记(chapter10)
- UNIX高级环境编程读书笔记(chapter11)
- UNIX高级环境编程读书笔记(chapter12)
- UNIX高级环境编程读书笔记(chapter13)
- UNIX高级环境编程读书笔记(chapter14)
- 《unix环境高级编程》 读书笔记 (1)
- 《unix环境高级编程》 读书笔记 (2)
- 《unix环境高级编程》 读书笔记 (3)
- 《unix环境高级编程》 读书笔记 (4)
- 《unix环境高级编程》 读书笔记 (5)
- 《unix环境高级编程》 读书笔记 (6)
- PACS中DICOM打印图像质量控制的研究
- jquery 改善验证码
- 企业移动应用管理平台 - MDM,MAM,MCM
- 一张图让你看懂各开源license
- MONGODB 与sql聚合操作对应图
- UNIX高级环境编程读书笔记(chapter8)
- Timer 定时器
- androi ndk configure under ubuntu 12.04
- Session
- UFLDL 教程学习笔记(一)神经网络
- hadoop 计算pi
- SpringMVC技术详解(一)
- Medoo Count的使用:读取记录条数
- 初雪之恋