fork和exec函数族都是Unix/Linux系统中常用的进程管理函数。
fork函数的作用是复制调用进程来创建一个新进程。新进程称为子进程。调用进程称为父进程,子进程和父进程在不同的内存空间中运行。子进程在父进程的基础上复制了所有的资源,包括代码段、数据段、堆栈段、打开的文件等,但是进程ID和父进程ID不同。调用fork函数会返回两个值:子进程返回0,父进程返回子进程的进程ID。父子进程之间是完全独立的,它们共享的只有内核中的一些数据结构。
pid_t fork(void);
返回值:
1.父进程返回子进程PID(非负);
2.子进程返回0;
3.创建失败返回 < 0;
注意:返回值不是fork函数返回两次,而是fork之后两个进程各自返回。返回后进程的执行顺序依赖于进程调度,没有绝对的先后之分。
示例:
#include<stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(pid > 0) // parent
{
printf("parent = %d ",getpid());//获取当前进程编号
}
else if(pid == 0) //child
{
printf("child = %d ",getpid());
}
else if(pid < 0)
{
perror("fork error ");
}
return 0;
}
exec函数族用于启动一个新程序来替换当前进程的映像。它有多种变体,例如execlp、execvp等。这些函数需要指定新程序的名称、命令行参数以及环境变量等信息。调用exec函数成功后,当前进程的所有资源都会被新程序替换掉,但是进程ID和父进程ID不变。
其中比较常用的exec函数族包括:
int execl(const char *pathname, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *pathname, const char *arg, ..., char *const envp[] );
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
返回值:exec函数一旦调用成功即执行新的程序,不返回。只有失败才返回,错误值-1。
示例:
#include<stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(pid > 0) // parent
{
printf("parent = %d ",getpid());//获取当前进程编号
}
else if(pid == 0) //child
{
// printf("child = %d ",getpid());
execl("/bin/ls","ls",NULL);
}
else if(pid < 0)
{
perror("fork error ");
}
while(1);
return 0;
}
fork和exec函数族的联系是它们都可以用来创建新的进程。fork函数可以用来创建与当前进程完全相同的子进程,而进程调用了exec()后,将在同一块进程内存里用一个新程序来代替调用 exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”, “堆栈段”和“代码段”会被新程序改写。 新程序会保持调用exec()进程的ID不变。通常可以将fork和exec组合起来使用,先使用fork创建子进程,然后在子进程中使用exec函数族启动新程序。这种方式可以避免在父进程中阻塞,同时也可以更灵活地控制子进程的行为。