跳到主要内容

C 语言进程控制

在操作系统中,进程是程序执行的一个实例。C语言提供了一些标准库函数,允许我们创建、管理和终止进程。理解进程控制对于编写高效、多任务的程序至关重要。本文将逐步介绍C语言中的进程控制,并通过代码示例和实际案例帮助你掌握这一概念。

什么是进程控制?

进程控制是指操作系统对进程的管理,包括创建、调度、同步和终止等操作。在C语言中,我们可以使用标准库中的函数来控制进程的行为。这些函数通常定义在 unistd.hsys/wait.h 头文件中。

创建进程:fork()

fork() 是C语言中用于创建新进程的系统调用。调用 fork() 后,操作系统会创建一个与当前进程几乎完全相同的子进程。子进程从 fork() 调用处开始执行,并且拥有与父进程相同的代码、数据和堆栈。

c
#include <stdio.h>
#include <unistd.h>

int main() {
pid_t pid = fork();

if (pid == 0) {
// 子进程
printf("这是子进程,PID: %d\n", getpid());
} else if (pid > 0) {
// 父进程
printf("这是父进程,PID: %d, 子进程PID: %d\n", getpid(), pid);
} else {
// fork失败
printf("fork失败\n");
}

return 0;
}

输出示例:

这是父进程,PID: 1234, 子进程PID: 1235
这是子进程,PID: 1235
备注

fork() 调用成功后,父进程和子进程会同时执行。子进程的 fork() 返回值为0,而父进程的 fork() 返回值是子进程的PID。

等待子进程:wait()

父进程可以使用 wait() 系统调用来等待子进程结束。wait() 会阻塞父进程,直到某个子进程终止。

c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
pid_t pid = fork();

if (pid == 0) {
// 子进程
printf("子进程开始执行\n");
sleep(2); // 模拟子进程执行任务
printf("子进程结束\n");
} else if (pid > 0) {
// 父进程
printf("父进程等待子进程结束\n");
wait(NULL); // 等待子进程结束
printf("子进程已结束,父进程继续执行\n");
} else {
printf("fork失败\n");
}

return 0;
}

输出示例:

父进程等待子进程结束
子进程开始执行
子进程结束
子进程已结束,父进程继续执行
提示

wait(NULL) 会等待任意一个子进程结束。如果你需要等待特定的子进程,可以使用 waitpid()

替换进程映像:exec()

exec() 系列函数用于替换当前进程的映像。调用 exec() 后,当前进程的代码、数据和堆栈会被新程序的代码、数据和堆栈替换。

c
#include <stdio.h>
#include <unistd.h>

int main() {
printf("当前进程开始执行\n");

// 替换进程映像
execl("/bin/ls", "ls", "-l", NULL);

// 如果execl成功,以下代码不会执行
printf("这行代码不会执行\n");

return 0;
}

输出示例:

当前进程开始执行
total 8
-rwxr-xr-x 1 user user 12345 Oct 1 12:34 a.out
警告

exec() 系列函数成功调用后,当前进程的代码将被替换,因此 exec() 之后的代码不会执行。

实际案例:简单的Shell实现

下面是一个简单的Shell实现,展示了如何使用 fork()exec() 来执行用户输入的命令。

c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define MAX_INPUT 100

int main() {
char input[MAX_INPUT];

while (1) {
printf("myshell> ");
fgets(input, MAX_INPUT, stdin);

// 去掉换行符
input[strcspn(input, "\n")] = 0;

if (strcmp(input, "exit") == 0) {
break;
}

pid_t pid = fork();

if (pid == 0) {
// 子进程
execlp(input, input, NULL);
printf("命令未找到: %s\n", input);
return 1;
} else if (pid > 0) {
// 父进程
wait(NULL);
} else {
printf("fork失败\n");
}
}

return 0;
}

输出示例:

myshell> ls
a.out main.c
myshell> exit
注意

这个简单的Shell实现没有处理复杂的命令参数和错误情况。实际应用中需要更复杂的逻辑来处理这些情况。

总结

C语言中的进程控制是编写多任务程序的基础。通过 fork()wait()exec() 等系统调用,我们可以创建、管理和替换进程。理解这些概念对于编写高效、可靠的程序至关重要。

附加资源与练习

  • 练习1:修改上面的简单Shell实现,使其能够处理带参数的命令。
  • 练习2:编写一个程序,创建多个子进程,并使用 waitpid() 等待特定子进程结束。
  • 附加资源:阅读 man 手册页,了解更多关于 fork()wait()exec() 的详细信息。

通过不断练习和探索,你将能够熟练掌握C语言中的进程控制,并编写出更复杂的多任务程序。