一、程序
程序(program)是存放在磁盘上、处于某个目录中的一个可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行。
二、进程和进程ID
程序的执行实例被称为进程(process)。某些操作系统使用任务(task)表示正在执行的程序。
UNIX系统确保每个进程都有一个唯一的数字标识符,称为进程ID(process ID)。进程ID总是一个非负整数。
程序清单1-4 打印进程ID
[root@localhost unix_env_advance_prog]# cat prog1-4.c #include "apue.h" int main(void) { printf("hello world from process ID %d ", getpid()); exit(0); }
编译后运行:
[root@localhost unix_env_advance_prog]# ./prog1-4 hello world from process ID 23987 [root@localhost unix_env_advance_prog]# ./prog1-4 hello world from process ID 23988
此程序运行时,它调用函数getpid得到其进程ID。
三、进程控制
有三个用于进程控制的主要函数:fork、exec和waitpid。(exec有六种变体,但经常把它们统称为exec函数。)
程序清单1-5 从标准输入读入命令并执行(类shell程序的简化实现):
[root@localhost unix_env_advance_prog]# cat prog1-5.c #include "apue.h" #include <sys/wait.h> int main(void) { char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; printf("%% "); /* print prompt (printf requires %% to print %) */ while(fgets(buf, MAXLINE, stdin) != NULL) { if(buf[strlen(buf) - 1] == ' ') buf[strlen(buf) - 1] = 0; /* replace newline with null */ if((pid = fork()) < 0) { err_sys("fork error"); } else if(pid == 0) /* child */ { execlp(buf, buf, (char *)0); err_ret("couldn't execute: %s", buf); exit(127); } /* parent */ if((pid = waitpid(pid, &status, 0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); }
编译后运行:
[root@localhost unix_env_advance_prog]# ./prog1-5 % ls apue.h Makefile prog1-1.c prog1-2.c prog1-3.c prog1-4.c prog1-5.c error.c prog1-1 prog1-2 prog1-3 prog1-4 prog1-5 %
用标准I/O函数fgets从标准输入一次读一行,当键入文件结束符(通常是Ctrl+D)作为行的第1个字符时,fgets返回一个null指针,于是终止循环,进程也就终止。
因为fgets返回的每一行都以换行符终止,后随一个null字节,故用标准C函数strlen计算此字符串的长度,然后用一个null字节替换换行符。这样做是因为execlp函数要求参数以null而不是以换行符结束。
调用fork创建一个新进程。新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork向父进程返回新子进程的进程ID(非负),对子进程则返回0。因为fork创建一个新进程,所以说它被调用一次(由父进程),但返回两次(分别在父进程及子进程中)。
在子进程中,调用execlp以执行从标准输入读入的命令。这就用新的程序文件替换了子进程原先执行的程序文件。fork和跟随其后的exec两者的组合是某些操作系统所称道产生(spawn)一个新进程。在UNIX系统中,这两个部分相互分隔,构成两个函数。
子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这一要求由调用waitpid实现,其参数指定要等待的进程(在这里,pid参数是子进程ID)。waitpid函数返回子进程的终止状态(status变量)。如果需要,可以使用status变量的值准确地判定子进程是因何终止的。
小知识:^D表示一个控制字符。控制字符是特殊字符,其形成方法是:在键盘上按下控制键——通常被标记为Control或Ctrl,同时按另一个键。Control-D或^D是默认的文件结束符。
四、线程和线程ID
通常,一个进程只有一个控制线程(thread),同一时刻只执行一组机器指令。对于某些问题,如果不同部分各使用一个控制线程,那么整个问题解决起来就容易很多。另外,多个控制线程也能充分利用多处理器系统的并行性。
在一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。因为它们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。
与进程相同,线程也用ID标识。但是,线程ID只在它所属进程内起作用。一个进程中的线程ID在另一个进程中并无意义。当在一个进程中对多个线程进行操作时,我们用线程ID引用相应的线程。
控制线程的函数与控制进程的函数类似,但另有一套。在进程模型建立很久之后,线程模型才被引入到UNIX系统中,这两个模型之间存在复杂的相互作用。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。