8.2 进程标识符
ID为0的进程通常是调度进程,常常被称为交换教程。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。 进程ID 1通常是init进程,在自举过程结束时由内核调用。该进程的程序文件在unix早期版本中是/etc/init,在新版本中是/sbin/init.此进程负责在自举内核后启动一个unix系统。
#include<unistd.h>
pid_t getpid(void);
pid_t getppid(void); 返回值:调用进程的进程ID 调用进程的父进程ID
uid_t getuid(void); 返回值:调用进程的实际用户ID
uid_t geteuid(void); 返回值:调用进程的有效用户ID
gid_t getgid(void); 返回值:调用进程的实际组ID
gid_t getegid(void); 返回值:调用进程的有效组ID
8.3 fork函数
一个现有进程可以调用fork函数创建一个新进程
#include<unistd.h>
pid_t fork(void); 返回值:子进程中返回0,父进程中返回子进程ID,出错则返回-1
父子进程并不共享这些存储空间部分。父子进程共享正文段。
#include "apue.h" int glob = 6; /* external variable in initialized data */ char buf[] = "a write to stdout\n"; int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) err_sys("write error"); printf("before fork\n"); /* we don't flush stdout */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ glob++; /* modify variables */ var++; } else { sleep(2); /* parent */ } printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0); }
注意:如果把标准输出 重定向到一个文件, before fork 会输出两次!!!!
在fork之后处理文件描述符有两种常见的情况:
(1)父进程待子进程完成。在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它曾进行过读写操作的任一共享描述符的文件偏移量已执行了相应更新
(2)父子进程各自执行不同的程序段。在这种情况下,在fork之后,父子进程各自关闭它们不需要使用的文件描述符,这样就不会干扰对方使用的文件描述符。这种方法是网络服务进程中经常使用的。
某些操作系统将两个操作fork之后执行exec组合成一个,并称其为spawn.
8.4 vfork函数
1.vfork与fork一一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中。因为子进程会立即调用EXEC或EXIT,于是也就不会村访该地址空间。
2.vfork和fork之间的另外一个区别是:vfork保证字进程先运行,在它调用exec或exit之后父进程可能被调度运行。
#include "apue.h"
int glob = 6; /* external variable in initialized data */
int
main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
printf("before vfork\n"); /* we don't flush stdio */
if ((pid = vfork()) < 0) {
err_sys("vfork error");
} else if (pid == 0) { /* child */
glob++; /* modify parent's variables */
var++;
_exit(0); /* child terminates */
}
/*
* Parent continues here.
*/
printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
exit(0);
}
./a.out
before vfork
pid = 29039, glob = 7, var = 89
如果子进程调用exit,而该实现冲洗所有标准I/O流。如果这会死函数库采取的唯一动作,那么我们会见到输出与子进程调用_exit所产生的输出完全相同,没有任何区别。如果该实现也关闭标准I/O流,那么表示标准输出FILE对象的相关存储区将被清0.
8.5 exit函数
进程有下面 5种正常终止方式:
(1) 在main函数内执行return语句。这等效于调用exit.
(2) 调用exit函数
(3) 调用_Exit和_exit是同义的。在UNIX系统中,_Exit和_exit是同义的,并不清洗标准I/O流。_exit函数由exit调用,它处理unix特定的细节。
(4) 进程最后一个线程在其启动历程中执行返回语句。但是 该线程的返回至不会作为进程的返回值。当最后一个线程从其启动历程返回时,该进程以终止状态0返回。
(5) 进程的最后一个线程调用pthread_exit函数。如同前面一样,在这种情况下,进程终止状态是0,这与传送给pthread_exit的参数无关。
三种异常终止方式如下:
(1) 调用abort。
(2) 当进程接收到某些信号时。
(3) 最后一个线程对”取消”请求做出响应。
不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。
对于三个终止函数(exit,_exit和_Exit),实现这一点的方法是,将其退出状态参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。
8.6 wait 和 waitpid函数
如果其所有子进程都还在运行,则阻塞。
如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
如果它没有任何子进程,则立即出错返回。
如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回。但是如果注意在任何时候调用wait,则进程可能会阻塞。
#include < sys/wait.h>
pid_t wait (int * statloc);
pid_t waitpid(pid_t pid , int * statloc, int options);
两个函数返回值:若成功则返回进程ID , 0.若出错则返回-1;
这两个函数的区别如下:
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
Waitpid并不等于在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程
8.7 waitid函数
#include<sys/wait.h>
int maitid(idtype_t idtype, id_t id,siginfo_t *infop,int options); //返回值:若成功则返回0 若出错则返回-1
与waitpid相似,waitid允许一个进程制定要等待的子进程。但它使用单独的参数表示要等待的子进程的类型,而不是将此与进程ID或进程组ID组合成一个参数。ID参数的作用与IDTYPE的值相关。
idtype参数: P_PID 等待一个特定的进程:id包含要等待子进程的进程ID
P_PGID 等待一个特定进程组中的任意子进程:ID包含要等待子进程的进程组ID
P_ALL 等待任一子进程:忽略ID
#include "apue.h" #include <sys/wait.h> char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL }; int main(void) { pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* specify pathname, specify environment */ if (execle("/home/sar/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *)0, env_init) < 0) err_sys("execle error"); } if (waitpid(pid, NULL, 0) < 0) err_sys("wait error"); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* specify filename, inherit environment */ if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0) err_sys("execlp error"); } exit(0); }
#include "apue.h" int main(int argc, char *argv[]) { int i; char **ptr; extern char **environ; for (i = 0; i < argc; i++) /* echo all command-line args */ printf("argv[%d]: %s\n", i, argv[i]); for (ptr = environ; *ptr != 0; ptr++) /* and all env strings */ printf("%s\n", *ptr); exit(0); }
#include "apue.h" #include <sys/wait.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ if (execl("/home/sar/bin/testinterp", "testinterp", "myarg1", "MY ARG2", (char *)0) < 0) err_sys("execl error"); } if (waitpid(pid, NULL, 0) < 0) /* parent */ err_sys("waitpid error"); exit(0); }
结果:
#!/home/sar/bin/echoarg foo
$ ./a.out
argv[0]: /home/sar/bin/echoarg
argv[1]: foo
argv[2]: /home/sar/bin/testinterp
argv[3]: myarg1
argv[4]: MY ARG2
#include <sys/wait.h> #include <errno.h> #include <unistd.h> int system(const char *cmdstring) /* version without signal handling */ { pid_t pid; int status; if (cmdstring == NULL) return(1); /* always a command processor with UNIX */ if ((pid = fork()) < 0) { status = -1; /* probably out of processes */ } else if (pid == 0) { /* child */ execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); _exit(127); /* execl error */ } else { /* parent */ while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { status = -1; /* error other than EINTR from waitpid() */ break; } } } return(status); }
shell的-c选项告诉shell程序取下一个命令行参数(在这里是cmdstring)作为命令输入。
#include "apue.h" #include <sys/times.h> static void pr_times(clock_t, struct tms *, struct tms *); static void do_cmd(char *); int main(int argc, char *argv[]) { int i; setbuf(stdout, NULL); for (i = 1; i < argc; i++) do_cmd(argv[i]); /* once for each command-line arg */ exit(0); } static void do_cmd(char *cmd) /* execute and time the "cmd" */ { struct tms tmsstart, tmsend; clock_t start, end; int status; printf("\ncommand: %s\n", cmd); if ((start = times(&tmsstart)) == -1) /* starting values */ err_sys("times error"); if ((status = system(cmd)) < 0) /* execute command */ err_sys("system() error"); if ((end = times(&tmsend)) == -1) /* ending values */ err_sys("times error"); pr_times(end-start, &tmsstart, &tmsend); pr_exit(status); } static void pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend) { static long clktck = 0; if (clktck == 0) /* fetch clock ticks per second first time */ 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); }