一、系统调用介绍:
本章内容将会在Linux系统下,通过系统调用来体验进程的生命周期。由于涉及到一些系统调用,那么首先先解释一下所涉及的系统调用:
1.fork():
fork()方法是用来创建一个进程的,fork()函数创建的进程为子进程,调用fork()函数的进程称父进程。调用fork()函数过后, 系统会创建一个与原进程几乎相同的进程(感觉就像是将父进程的程序代码复制了一份给子进程),然后父子进程都继续往下执行。
fork()函数调用成功后它能返回两个值,因为如果调用成功,父进程的代码就会复制给子进程,这样也就有了两个fork()函数,所以返回两个值。父进程的fork()函数返回值是子进程的pid, 子进程的fork()函数返回值是0。若进程创建失败,则原进程不会被复制, 返回值为-1。
2.wait():
wait()是一个用于等待子进程结束并获取其退出状态的系统调用。它在操作系统中的作用非常重要,允许父进程控制子进程的执行,以及处理子进程的退出状态。
-
等待子进程结束:
当调用
wait()
时,父进程会被阻塞,直到一个子进程结束。父进程将控制权交给操作系统,并在子进程结束后继续执行。 -
获取子进程的退出状态:
通过
wait()
返回的子进程PID,可以获得子进程的退出状态信息,存储在status
变量中。通常,可以使用宏来检查子进程的退出状态:WIFEXITED(status)
:检查子进程是否正常退出,如果是正常退出则返回真(非零值)。WEXITSTATUS(status)
:获取正常退出的子进程的退出码,这是一个整数值,通常用来指示子进程的执行结果。- 其他宏如
WIFSIGNALED
和WTERMSIG
用于处理异常终止的情况。
3.execlp():
execlp()是一个用于在当前进程中执行新程序的系统调用,它会在系统的环境变量 PATH
中查找可执行文件。execlp()
允许在当前进程中执行新程序,方便地查找可执行文件,以及传递参数给新程序。这在创建新进程并执行外部命令时非常有用。
4.sleep():
当调用 sleep(seconds)
时,当前进程会暂时挂起,不再执行任何代码,直到指定的秒数过去为止。这可以用来实现定时操作、等待一段时间后再继续执行等情况。sleep()
函数会休眠指定的秒数,并返回未休眠完的剩余秒数(如果有的话)。如果休眠时间已经耗尽,它会返回0。该系统调用会导致当前进程完全挂起,不会占用 CPU 资源。
5.exit():
exit(int status) 是一个用于终止当前进程的函数。它用于通知操作系统当前进程已经完成,并且可以选择返回一个整数退出状态码。当调用 exit(status)
时,当前进程会被立即终止。这意味着进程的所有代码和资源都会被操作系统回收,包括打开的文件、分配的内存等。通过 status
参数,可以指定进程的退出状态,这个状态可以在其他程序或脚本中通过特殊变量来获取。这个状态码通常用于指示程序的执行结果,例如,0 表示成功,非零值表示错误或其他情况。
二、示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t child_pid;
// Fork一个新的进程
child_pid = fork();
if (child_pid == 0) {
// 这是子进程
printf("子进程: PID=%d\n", getpid());
// 使用exec调用执行一个新的程序
execlp("/bin/ls", "ls", NULL);
// 如果exec失败,会继续执行以下代码
perror("exec");
return 1;
} else if (child_pid > 0) {
// 这是父进程
printf("父进程: PID=%d, 子进程的PID=%d\n", getpid(), child_pid);
// 等待子进程结束
int status;
wait(&status);
if (WIFEXITED(status)) {
printf("子进程正常退出,退出码:%d\n", WEXITSTATUS(status));
}
} else {
perror("fork");
return 1;
}
// 父进程继续执行
printf("父进程继续执行\n");
// 等待一段时间
sleep(2);
// 退出进程
exit(0);
// 这里的代码不会被执行
printf("这段代码不会被执行\n");
}
结果:
接下来我们来分析一下这段代码,首先父进程fork了一个子进程,fork的返回值大于0,表示当前的进程为父进程,因此,代码就执行到了else if代码块中,父进程先打印信息(结果中的第一行),接下来执行wait()方法,父进程阻塞了,并且等待子进程执行完毕。用于在子进程中,fork的返回值为0,子进程就开始进入if代码块中执行了,子进程先打印自己的PID,执行了ls指令后(结果中第二,三,四行),会转到父进程的wait()代码处,父进程会继续执行(结果中第五、六行)。
OK,通过以上的分析,相信大家对于进程的生命周期也有了一定的了解。