进程的创建是Linux系统编程中的重要概念之一。在本节中,我们将介绍进程的创建、获取进程ID和父进程ID、进程共享、exec函数族、wait和waitpid等相关内容。
在Linux系统中,进程的创建使用fork()
系统调用。fork()
系统调用会创建一个与当前进程相同的子进程,子进程会复制父进程的所有资源,包括代码、数据和文件描述符等。
fork()
函数的原型如下:
#include
pid_t fork(void);
fork()
函数 没有任何参数 ,它的返回值是一个pid_t
类型的整数。具体解释如下:
fork()
函数会在父进程中返回子进程的PID(子进程ID),在子进程中返回0。fork()
函数会返回-1,并设置errno
来指示错误类型。代码示例如下:
#include
#include
#include
int main() {
pid_t pid = fork();
if (pid == -1) {
printf("Failed to fork a new process.\n");
return 1;
} else if (pid == 0) {
printf("This is the child process.\n");
} else {
printf("This is the parent process.\n");
}
return 0;
}
上述代码中,fork()
系统调用会返回两次,分别在父进程和子进程中返回。通过判断返回值,我们可以区分父进程和子进程,并执行不同的代码逻辑。
在Linux系统中,可以使用getpid()
和getppid()
系统调用来获取当前进程的ID和父进程的ID。
getpid()
和getppid()
函数的原型如下:
#include
#include
pid_t getpid(void);
pid_t getppid(void);
这两个函数都 没有任何参数 ,它们的返回值都是一个pid_t
类型的整数。具体解释如下:
getpid()
函数返回调用进程的进程ID(PID)。getppid()
函数返回调用进程的父进程的进程ID(PPID)。代码示例如下:
#include
#include
#include
int main() {
pid_t pid = getpid();
pid_t ppid = getppid();
printf("Process ID: %d\n", pid);
printf("Parent Process ID: %d\n", ppid);
return 0;
}
在Linux系统中,exec()
函数族可以用于将当前进程替换为新的程序。exec()
函数族包括execl()
、execv()
、execle()
、execve()
等函数。这些函数可以根据不同的参数形式来执行不同的替换方式。
exec()
函数族的常见成员:int execl(const char *path, const char *arg, ...);
path
是要执行的新程序的路径。arg
是一个字符串,表示新程序的第一个参数。NULL
结束。int execv(const char *path, char *const argv[]);
path
是要执行的新程序的路径。argv
是一个字符串数组,表示新程序的参数列表,最后一个元素必须是 NULL
。int execle(const char *path, const char *arg, ..., char *const envp[]);
path
是要执行的新程序的路径。arg
是一个字符串,表示新程序的第一个参数。NULL
结束。envp
是一个字符串数组,表示新程序的环境变量列表,最后一个元素必须是 NULL
。int execvp(const char *file, char *const argv[]);
file
是要执行的新程序的文件名。argv
是一个字符串数组,表示新程序的参数列表,最后一个元素必须是 NULL
。这些函数在执行成功时不会返回,而是直接将当前进程替换为新程序。如果返回,则表示执行失败,可以根据返回值来判断错误类型。
exec()
函数族可以用于在当前进程中加载和执行新程序,可以实现程序的动态切换和功能扩展。一般情况下,exec()
函数族会在调用fork()
函数创建子进程后使用,以替换子进程的代码和数据。
代码示例如下:
#include
#include
int main() {
printf("Before exec()\n");
execl("/bin/ls", "ls", "-l", NULL);
printf("After exec()\n");
return 0;
}
上述代码中,execl()
函数会将当前进程替换为ls -l
命令。execl()
函数的第一个参数是要执行的程序路径,后续参数是传递给新程序的命令行参数。
在Linux系统中,父进程可以使用wait()
或waitpid()
系统调用等待子进程的结束。这些系统调用会阻塞父进程的执行,直到子进程结束。
wait()
和waitpid()
是用于等待子进程结束并获取子进程的退出状态的函数。
pid_t wait(int *status);
status
是一个指向整型的指针,用于存储子进程的退出状态信息。pid_t waitpid(pid_t pid, int *status, int options);
pid
指定要等待的子进程的进程ID。status
是一个指向整型的指针,用于存储子进程的退出状态信息。options
是一个整型值,用于指定等待的选项。wait()
和waitpid()
函数的返回值是子进程的进程ID,如果调用失败,则返回-1。通过参数 status
可以获取子进程的退出状态信息,包括退出码、终止信号等。
waitpid()
函数相比于wait()
函数更加灵活,可以通过参数 pid
和 options
控制等待的子进程。
其中,pid
的取值可以是:
-1
:等待任意子进程。0
:等待与当前进程组ID相同的子进程。options
参数可以通过位掩码的方式指定多个选项,常用的选项有:
WNOHANG
:非阻塞方式,如果没有子进程结束,立即返回。WUNTRACED
:也会返回已经停止的子进程的状态。WCONTINUED
:也会返回已经继续运行的子进程的状态。wait()
和waitpid()
函数可以用于处理子进程的退出状态,释放子进程的资源,并进行进程间的同步。在使用这两个函数时,需要注意处理错误情况和避免僵尸进程的产生。
代码示例如下:
#include
#include
#include
#include
int main() {
pid_t pid = fork();
if (pid == -1) {
printf("Failed to fork a new process.\n");
return 1;
} else if (pid == 0) {
printf("This is the child process.\n");
} else {
wait(NULL);
printf("This is the parent process.\n");
}
return 0;
}
上述代码中,父进程使用wait(NULL)
系统调用等待子进程的结束。wait()
系统调用会阻塞父进程的执行,直到子进程结束。
fork()
函数:用于创建子进程,返回值不同表示在不同的进程中执行。exec()
函数族:用于在当前进程中加载和执行新程序,可以实现程序的动态切换和功能扩展。
execl()
:接受可变参数的形式,参数以字符串形式传递。execle()
:接受可变参数的形式,同时传递环境变量。execvp()
:接受参数数组的形式,参数以字符串数组形式传递。wait()
和waitpid()
函数:用于等待子进程结束并获取子进程的退出状态。
wait()
:等待任意子进程结束。waitpid()
:可以指定等待的子进程。status
可以获取子进程的退出状态信息。options
参数控制等待的选项,如非阻塞方式等。这些函数和系统调用可以用于进程的创建、执行和等待,实现进程间的同步和协作。通过这些函数,可以实现进程的动态切换、功能扩展和资源释放。同时,需要注意处理错误情况,避免产生僵尸进程和资源泄漏的问题。