📅  最后修改于: 2023-12-03 14:41:18.848000             🧑  作者: Mango
在 Unix (包括 Linux)操作系统中,fork()
和 exec()
是两个非常常用的系统调用,用于创建进程和运行程序,下面我们来详细介绍它们之间的区别。
fork()
系统调用用于创建一个新的进程,这个新的进程几乎与原进程完全相同,包括代码、数据以及堆栈等等。包括进程 ID、父进程 ID 等进程信息也会相同,但是在它们的内存地址和数据空间是独立的。
fork()
的用法如下所示:
#include <unistd.h>
pid_t fork(void);
其中 pid_t
是一种定义在 sys/types.h
中的数据类型,用于表示进程 ID。调用 fork()
会返回两个值,父进程中 fork()
返回新创建的子进程的进程 ID,而在子进程中调用 fork()
则会返回 0。如果 fork()
调用出现了错误,则会返回负数。
下面是 fork()
的示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid = fork();
if (pid < 0) // 出现错误
{
fprintf(stderr, "fork error!\n");
return 1;
}
else if (pid == 0) // 子进程
{
printf("I'm the child process!\n");
printf("My PID is %d.\n", getpid());
}
else // 父进程
{
printf("I'm the parent process!\n");
printf("My PID is %d.\n", getpid());
printf("The PID of my child process is %d.\n", pid);
}
return 0;
}
这个程序中,通过调用 fork()
创建了一个新的子进程,然后在父进程和子进程中分别输出了不同的信息。需要注意的是,输出的进程 ID 都是不同的,这是因为在 fork()
后,父子进程有着不同的进程 ID。
exec()
系统调用用于启动一个新的程序,它会覆盖当前进程的地址空间,并从指定的位置开始执行一个新的程序。因为 exec()
的结果是当前的进程被替换掉,所以它本身不会创建新的进程。
exec()
族函数的用法如下所示:
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
其中各个参数的含义如下:
path
:要执行的可执行文件的路径名或文件名file
:要执行的可执行文件的文件名,相当于在 PATH
环境变量所指定的路径中查找arg
:要传递给新程序的命令行参数,可以是一个字符串或多个字符串argv
:要传递给新程序的命令行参数列表envp
:要传递给新程序的环境变量列表下面是 exec()
函数族的示例代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
char *const argv[] = {"ls", "-l", NULL};
if (fork() == 0) // 子进程
{
execl("/bin/ls", "ls", "-l", NULL);
}
else // 父进程
{
printf("I'm the parent process!\n");
execv("/bin/ls", argv);
}
return 0;
}
这个程序中,首先在子进程中使用了 execl()
函数来执行了 /bin/ls -l
命令,然后在父进程中使用了 execv()
函数来执行了和子进程一样的命令。值得注意的是,如果 exec()
函数执行成功,那么它的调用者进程就不存在了,它会被新执行的程序替换掉。
fork()
创建一个新的进程,该进程几乎与原进程相同,但独立于原进程的地址空间;exec()
执行一个新的程序,在当前进程的地址空间中覆盖原先的程序;fork()
和 exec()
往往结合使用,比如先用 fork()
创建子进程,然后在子进程中使用 exec()
运行一个新的程序。