这里主要介绍Unix/Linux中进程创建,fork()函数和exec()函数。
这里先介绍一下什么是进程:
进程是正在执行的程序的一个实例。每个实例都有自己的地址空间和执行状态。当操作系统给内核数据结构添加了适当的信息并分配了运行程序代码所需要的资源时,程序就成了进程。一个进程有一个地址空间(它可以访问的内存)和至少一个称为线程的控制流。进程的变量既可以进程生命周期中始终存在(静态存储),也可以在执行进入下一个程序块时自动分配、离开这个程序块时将其释放(自动存储)。
进程的状态:
一、fork() 函数
进程可以通过调用fork函数来创建新进程,调用进程成为父进程,被创建的进程称为子进程。由于fork函数会复制父进程的内存映象,因此新进程会收到父进程地址空间的一份拷贝。两个进程在fork语句后会继续执行后面的指令(分别在自己的内存映象中执行)。
#include <unistd.h>
pid_t fork(void);
创建两个完全相同的进程没什么用。fork函数的返回值是让父进程和子进程区分自己并执行不同代码的关键特性。fork函数向子进程返回0,并将子进程的进程ID返回给父进程。 fork函数调用失败时会返回-1,并设置errno。如果系统没有创建子进程的必要资源,或者如果再创建子进程就会超出进程数的限制,fork()就会将errno设为EAGAIN。如果失败,那么fork()不会创建子进程。
fork()函数通过复制父进程在内存中的映象来创建新进程。子进程继承了父进程的属性,如环境和权限。子进程还继承了父进程的某些资源,如打开的文件和设备。
并不是父进程的每个属性或者资源都会被子进程继承。 例如,子进程有一个新进程ID,当然还有一个不同的父进程ID。子进程的CPU使用时间被重置为0.子进程不会获得父进程持有的锁。如果父进程设置了报警,则报警到期时子进程并不会被通知。即使父进程在调用fork函数时有挂起信号,子进程在启动时也不会带有挂起的信号。
虽然子进程继承了父进程的进程优先级和调度属性,但它仍然是作为一个独立的实体与其他进程争夺处理器时间的。在繁忙的分时系统上运行的用户可以通过创建更多进程来获得更多的CPU时间。繁忙系统的系统管理员可以通过限制进程创建来放置用户创建进程获得更多的资源。
下面的程序中执行了fork()后,父进程和子进程会输出它们各自的进程ID。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
pid_t childpid;
childpid = fork();
if (childpid == -1) {
perror("Failed to fork");
return 1;
}
if (childpid == 0) {
// 子进程代码
printf("I am child %ld\n", (long)getpid());
} else {
printf("I am parent %ld\n", (long)getpid());
}
return 0;
}
运行结果:
root@mrzhi:/home/mrzhi/Desktop# ./test
I am parent 4307
I am child 4308
note 1 : 上述的例子中,由于这些进程输出可能以任何顺序出现,这取决于父进程和子进程的执行顺序。如果程序在同一个系统中运行多次,那么输出的顺序可能总是相同的,也可能不同。
二、wait()函数
在进程创建子进程时,父进程和子进程都是从fork()函数出继续执行的。父进程可以通过执行wait() 函数或者waitpid() 函数一直阻塞到子进程结束。
wait()函数会导致调用程序暂停执行,直到子进程的状态成为可用的或者调用程序接收到的一个信号为止。进程状态最常在进程终止后成为可用的,但当进程被停止时它也可能变成可用的。
waitpid()函数允许父进程等待一个特定的子进程,该函数还允许父进程非阻塞地检查子进程是否已经终止。
waitpiid()有三个参数:pid、指向返回状态所在位置的指针和用于指定可选项的标志位。
- 如果pid为-1,waitpid函数就会等待任意一个子进程。
- 如果pid大于0,waitpid函数就会等待进程ID为pid的那个特定子进程。
- pid参数还有其他两种可能的值。如果pid为0,waitpid函数就会等待同一个进程组中的任意子进程作为调用程序;如果pid小于-1,waitpid函数就会等待由pid的绝对值指定的进程组中的任何一个子进程。
waitpid()的参数options是一个或者多个标志位的按位或。 即使子进程的状态不是立即可用的,WNOHAN选择也会使用waitpid函数返回。WUNTRACED选择会使waitpid函数报告那些已经被停止的、未报告的子进程的状态。
#include <sys/wait.h>
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
如果wait或waitpid函数是因为子进程的状态被报告而返回的,这些函数就会返回这个子进程的进程ID。如果出现了错误,这些函数就会返回-1并设置errno。如果WNOHANG选项调用waitpid(),它就会返回0来报告可能有需要等待的子进程,但这些进程的状态是不可用的。这两个函数的强制性错误为:
错误 | 原因 |
---|---|
ECHILD | 调用者没有需要等待的子进程(wait()函数),或者pid指定的进程或进程组不存在(waitpid()函数),或者pid指定的进程组中没有哪个成员是调用者的子进程(waitpid()函数) |
EINTR | 函数被信号中断 |
EINVAL | waitpid() 函数的options参数是无效的 |
案例:
#incl