进程控制是操作系统中一个非常重要的概念,它涉及到创建、管理和终止进程的能力。进程控制包括一系列操作,如创建新进程、等待进程结束、发送信号给进程等。下面是进程控制中一些常见的操作及其相关API:
进程控制概述
进程控制是指操作系统提供的用于管理进程生命周期的功能。这包括创建进程、获取进程状态、等待进程结束、发送信号等操作。这些功能对于编写多进程程序和系统管理工具尤为重要。
进程控制的常见操作
创建进程
-
fork():
pid_t fork(void)
: 创建一个新的进程,该进程是当前进程的一个副本。- 返回值:在父进程中返回子进程的PID,在子进程中返回0。
-
vfork():
pid_t vfork(void)
: 类似于fork()
,但在子进程中执行完_exit()
或longjmp()
前,父进程会被阻塞。- 返回值:同
fork()
。
-
posix_spawn():
int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[])
: 创建一个新的进程,执行指定路径的程序。- 参数
argv
和envp
分别指定了程序的参数列表和环境变量。
-
exec系列函数:
execl(const char *path, const char *arg, ...)
: 替换当前进程映像,执行新的程序。execle(const char *path, const char *arg, ..., char *const envp[])
: 替换当前进程映像,执行新的程序,并设置环境变量。execlp(const char *file, const char *arg, ...)
: 替换当前进程映像,执行新的程序,使用PATH环境变量查找文件。execv(const char *path, char *const argv[])
: 替换当前进程映像,执行新的程序。execvp(const char *file, char *const argv[])
: 替换当前进程映像,执行新的程序,使用PATH环境变量查找文件。execvpe(const char *file, char *const argv[], char *const envp[])
: 替换当前进程映像,执行新的程序,使用PATH环境变量查找文件,并设置环境变量。- 返回值:这些函数在失败时返回-1,在成功时则不会返回。
获取进程状态
-
wait():
pid_t wait(int *status)
: 等待任意一个子进程结束,并获取其状态。- 参数
status
可以通过宏如WIFEXITED()
、WEXITSTATUS()
等解析进程的状态。
-
waitpid():
pid_t waitpid(pid_t pid, int *status, int options)
: 等待指定的子进程结束,并获取其状态。- 参数
pid
可以指定等待哪个子进程;options
可以指定等待的行为,如WNOHANG
表示非阻塞等待。
-
waitid():
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options)
: 等待一个指定的进程或线程结束,并获取其状态。- 参数
idtype
可以是P_PID
(进程ID)、P_PGID
(进程组ID)或P_PID
(线程ID)。
进程终止
-
_exit():
_exit(int status)
: 使进程正常终止,并给出一个退出状态码。- 这个函数是进程终止时使用的,而不是用于线程。
-
exit():
void exit(int status)
: 使进程正常终止,并给出一个退出状态码。- 这个函数会先调用注册的终止处理函数。
-
abort():
void abort(void)
: 异常终止进程,并产生一个核心转储文件(如果启用了core dump)。
进程属性
-
getpid():
pid_t getpid(void)
: 获取当前进程的PID。
-
getppid():
pid_t getppid(void)
: 获取当前进程的父进程PID。
-
setsid():
pid_t setsid(void)
: 创建一个新的会话,并使其成为会话领导者。- 通常用于创建后台进程。
-
getpgid():
pid_t getpgid(pid_t pid)
: 获取进程组ID。
-
setpgid():
int setpgid(pid_t pid, pid_t pgid)
: 设置进程组ID。
进程资源控制
-
nice():
int nice(int inc)
: 改变进程的调度优先级。inc
表示优先级的变化值。
-
setpriority():
int setpriority(int which, id_t who, int prio)
: 改变进程或进程组的优先级。
发送信号
-
kill():
int kill(pid_t pid, int sig)
: 发送信号给指定的进程。- 参数
sig
指定要发送的信号类型。
-
raise():
int raise(int sig)
: 发送信号给当前进程。- 参数
sig
指定要发送的信号类型。
示例代码
以下是一个简单的示例,展示如何在 Linux 系统上使用 C 语言创建子进程、等待子进程结束、并获取子进程的退出状态:
1#include <stdio.h>
2#include <unistd.h>
3#include <sys/wait.h>
4
5int main() {
6 pid_t pid;
7
8 // 创建子进程
9 pid = fork();
10 if (pid < 0) {
11 fprintf(stderr, "fork failed\n");
12 return 1;
13 } else if (pid > 0) {
14 // 父进程
15 int status;
16
17 // 等待子进程结束
18 if (waitpid(pid, &status, 0) != pid) {
19 fprintf(stderr, "waitpid failed\n");
20 return 1;
21 }
22
23 // 检查子进程的退出状态
24 if (WIFEXITED(status)) {
25 printf("子进程正常退出,状态码为 %d\n", WEXITSTATUS(status));
26 } else {
27 printf("子进程异常退出\n");
28 }
29 } else {
30 // 子进程
31 printf("子进程 PID: %d\n", getpid());
32 _exit(0); // 子进程正常退出
33 }
34
35 return 0;
36}
注意事项
- 在创建子进程后,务必使用
wait()
或waitpid()
等待子进程结束,否则会产生僵尸进程。 - 使用
_exit()
来终止子进程,而不要使用exit()
,以避免执行不必要的清理工作。 - 调用
fork()
之后,父进程和子进程中的程序计数器指向同一位置,因此通常需要立即添加条件分支来区分父进程和子进程的代码路径。 - 在使用
exec
系列函数替换进程映像时,如果失败,需要检查错误并采取适当的措施。
进程控制是操作系统中的重要概念,理解这些操作对于编写多进程程序和系统管理工具至关重要。