2026/08/21 linux嵌入式系统的进程的控制
进程的控制
进程控制指的是对系统中的所有进程实施有效的管理,其功能一般包括进程的创建、进程的撤销、进程的阻塞与唤醒等。这些功能一般是由操作系统的内核来实现的。
1)进程的创建
Linux系统中的一个进程中可以创建若干个新进程,新创建的进程中又可以创建子进程,所以一般使用进程前趋图来描述创建的进程之间的关系。进程前趋图也被称为进程树,是为了描述进程家族关系的树。

比如在进程A中创建了一个新进程B,则进程B就是进程A的子进程,进程A就是进程B的父进程。
如果在进程A中创建了2个子进程B和C,进程B中创建了2个子进程D和E,进程C中创建了1个子进程F,则进程A就是该进程家族的祖先。
在Linux系统中运行的所有进程也可以构成一个进程树,并且该进程树也有一个祖先,用户可以通过Linux系统提供的shell命令:pstree 来打印进程关系。

可以发现,Linux系统中的所有进程都和一个进程有关系,那就是systemd进程,该进程是Linux系统运行的第一个进程,英文全称是system daemon,中文翻译为系统守护进程,系统中其他进程都是该进程的子进程。
守护进程也被翻译为精灵进程或者后台进程,是一种旨在运行于相对干净环境、不受终端影响的、常驻内存的进程,就像神话中的精灵拥有不死的特性,长期稳定提供某种功能或服务。
systemd其实是一个 Linux 系统基础组件的集合,它提供了一个系统和服务管理器,然后运行PID 为 1的进程,负责在系统启动或运行时激活系统资源,并且管理服务器进程和其它进程。

注意:Linux系统中只有进程才可以在系统运行,所以一个程序想要运行,就必须为该程序创建进程。linux内核提供了一个名字叫做fork()的系统调用接口,该接口可以在进程中创建一个子进程。


可以看到fork函数的返回值对于父进程和子进程而言是不同的,fork函数在父进程中返回的是创建成功的子进程的PID,fork函数在子进程中的返回值是0,当然,如果子进程失败则返回-1。
通过fork函数的返回值来区分父进程和子进程,父进程和子进程的执行顺序是由操作系统的调度器决定的。也可以选择利用某些机制来调整执行顺序。
例:设计一个程序,要求在进程中创建一个子进程,并区分父进程和子进程,并在父进程中输出进程的PID,在子进程中输出子进程的PID和子进程的父进程的PID。提示:获取当前进程PID的函数接口getpid(),获取当前进程的父进程PID的函数接口是getppid()
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int fd = 3;
pid_t child_pid = fork();
if (child_pid > 0)
{
//说明是父进程的进程空间
printf("I am parent,my pid = %d,my child's pid = %d\n",getpid(),child_pid);
wait(NULL);
}
else if (child_pid == 0)
{
//说明是子进程的进程空间
printf("I am child,my pid = %d,my parent's pid = %d\n",getpid(),getppid());
}
else
{
printf("child process fork error\n");
return -1;
}
return 0;
}

调用fork的时候子进程已经拷贝了父进程的代码段、数据段、堆栈段,所以fork函数还没有执行完成,就会在两个进程空间继续执行,就会得到两个不同的返回值。
2)进程的撤销
一个进程在完成自身任务之后应该及时撤销,这样就可以及时的释放进程的资源,此时可以分为两种情况:一种是撤销指定进程,另一种是撤销指定进程以及该进程的所有子孙进程。不管是哪种情况,都应该及时回收进程占用的资源。
Linux系统中提供了一个名称叫做wait()的系统调用接口,该接口用于让父进程等待子进程的状态改变并获取已经改变状态的子进程的信息。

僵尸态指的是进程终止但是并未释放相关资源的一种状态,此时由系统内核对处于僵尸态的进程进行维护,处于僵尸态的进程会占用创建进程的资源和数量,如果僵尸态进程数量太多,则会导致系统无法创建新进程。
所以父进程应该及时的对终止的子进程进行等待,这样就可以回收子进程占用的资源,当然,如果父进程终止,则处于僵尸态的子进程会由系统内核完成资源的回收。
注意:如果当前进程没有子进程,则wait函数立即返回,如果当前进程有很多个子进程则wait函数会回收第一个变为僵尸态的子进程资源。
wait函数的参数是一个指针,用于记录子进程的退出状态,如果该参数为NULL,则表示当前进程放弃子进程的退出状态。对于该指针中记录的值,用户可以通过系统提供的宏定义来分析子进程的退出状态。****

通过man手册可以发现Linux内核还提供了另一个叫做waitpid()的系统调用函数,该函数也可以等待子进程状态改变并回收子进程的系统资源,只不过该函数的针对性更强,可以指定回收某个子进程的系统资源。

3)进程的执行
在一个进程中可以创建多个子进程,并且子进程和父进程的数据段和代码段是相同的,也就是一份程序会执行两次,一般情况下是没有意义和必要的,所以一般需要让子进程单独的加载新的程序的代码段和数据段,Linux系统标准库中提供了一组函数接口来实现在进程中加载和执行指定的程序。


这组接口可以把exec作为前缀,然后以exec后面的字符进行分类,比如l指的是list的缩写,p指的是path的缩写,e指的是environment的缩写,v指的是vector的缩写。
(1)l : 以列表的方式来组织指定程序的参数
(2)v: 以数组的方式来组织指定程序的参数
(3)e: 执行指定程序前顺便设置环境变量
(4)p: 执行程序时可自动搜索环境变量PATH的路径

以 execl(const char *pathname, const char *arg, ...) 为例,参数pathname是需要加载的指定程序,而arg则是该程序运行时的命令行参数,命令行参数包括程序名本身,并且全部是字符串,其实和带参数的main函数通过命令行传参的流程类似,但是函数的参数列表必须以NULL结束,也就是函数的最后一个参数填NULL即可。
其实,linux系统还提供一个函数,名称叫做system(),这个函数也可以让新进程执行shell命令。

例:编写一个程序,使之产生一个子进程,在子进程中执行系统命令ls -l去查看某个文件的信息,父进程判断子进程是否执行成功。要求父进程等待子进程结束之后再结束。提示:Linux系统中的shell命令属于可执行文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork fail");
return -1;
}
else if (pid == 0)
{
// ========== 子进程 ==========
printf("child process: pid=%d, will run ls -l\n", getpid());
// execvp:第一个参数程序名,第二个参数argv数组,NULL标记数组结尾
char *argv[] = {"ls", "-l", "fork.c", NULL};
// 执行ls -l fork.c 查看fork.c文件信息
execvp(argv[0], argv);
// 如果execvp返回,代表执行出错!只有失败才会走到这里
perror("execvp ls failed");
exit(EXIT_FAILURE);
}
else
{
// ========== 父进程 ==========
int status;
printf("parent process: pid=%d, wait child(pid=%d) finish\n", getpid(), pid);
// 父进程阻塞,等待子进程结束,子进程退出状态存入status
wait(&status);
// WIFEXITED 判断子进程是否正常退出
if (WIFEXITED(status))
{
// WEXITSTATUS 获取子进程退出返回码
int ret_code = WEXITSTATUS(status);
if(ret_code == 0)
{
printf("parent: child execute ls -l SUCCESS, child exit code:%d\n", ret_code);
}
else
{
printf("parent: child execute ls -l FAILED, child exit code:%d\n", ret_code);
}
}
else
{
printf("parent: child process abnormal exit\n");
}
printf("parent process exit\n");
}
return 0;
}

作者:bbzhu04@gmail.com ,多有不妥之处,望批评指正
本文原创,欢迎转载;转载请在文章显著位置标明原文链接: href="https://www.cnblogs.com/zhubb/p/22624048"
保留所有权限,未经许可禁止篡改、商用发布。

浙公网安备 33010602011771号