【嵌入式Linux应用开发基础】进程实战开发
- 软件开发
- 2025-09-01 23:42:02

目录
一、进程基础概念回顾
1.1. 进程本质特征
1.2. 进程与程序的区别
1.3. 进程的组成
1.4. 进程 ID(PID)
1.5. 进程状态
1.6. 嵌入式系统中的特殊性
1.7. 关键命令
1.8. 示例场景
二、进程创建与终止
2.1. 进程创建 - fork()函数
2.2. 进程终止 - exit()函数
三、进程间通信(IPC)
3.1. 管道(Pipe)
3.2. 匿名管道
3.2. 命名管道(FIFO)
3.3. 信号(Signal)
四、实战案例 - 基于进程的简单监控系统
五、参考文献
在嵌入式 Linux 系统中,进程是正在执行的程序实例。每个进程都有自己独立的地址空间、系统资源(如文件描述符、信号处理等)。进程由内核进行调度和管理,是系统资源分配的基本单位。
一、进程基础概念回顾在操作系统中,进程(Process) 是计算机中正在执行的程序的实例,是操作系统进行资源分配和调度的基本单位。
1.1. 进程本质特征动态性:进程是程序的一次执行过程,具有生命周期(创建、运行、阻塞、终止)。
独立性:每个进程拥有独立的地址空间和系统资源(如内存、文件描述符、CPU时间片),彼此隔离。
并发性:多个进程可在单核CPU上通过时间片轮转并行执行,或在多核CPU上真正并行。
1.2. 进程与程序的区别 程序进程静态的代码文件(如 a.out)动态的执行实体存储在磁盘中驻留在内存中无生命周期概念有明确的创建、运行、终止过程不占用系统资源占用CPU、内存等资源 1.3. 进程的组成代码段(Text):程序的二进制指令(只读)。
数据段(Data):全局变量、静态变量。
堆(Heap):动态分配的内存(如 malloc)。
栈(Stack):函数调用时的局部变量、返回地址。
进程控制块(PCB):操作系统维护的元数据,包含:
进程ID(PID)、父进程ID(PPID)
进程状态(运行、就绪、阻塞等)
寄存器值(如程序计数器PC)
资源分配信息(打开的文件、内存映射等)
1.4. 进程 ID(PID)每个进程都有一个唯一的标识符,即进程 ID(PID)。PID 是一个非负整数,内核使用 PID 来标识和管理进程。在 Linux 系统中,init进程的 PID 始终为 1,它是所有其他进程的祖先。
1.5. 进程状态进程在其生命周期内会处于不同的状态,常见的有:
运行态(Running):进程正在 CPU 上执行或准备执行。就绪态(Ready):进程已经准备好运行,但等待 CPU 调度。阻塞态(Blocked):进程因为等待某个事件(如 I/O 操作完成、信号等)而暂时无法运行。僵死态(Zombie):进程已经终止,但父进程尚未回收其资源(如进程描述符等)。 1.6. 嵌入式系统中的特殊性轻量化设计:因资源受限,需减少进程数量(如用线程池替代频繁的进程创建)。
实时性要求:硬实时系统需确保进程调度满足截止时间(如通过实时调度策略)。
确定性回收:防止僵尸进程(通过 SIGCHLD 信号处理或显式调用 waitpid)。
1.7. 关键命令 # 查看系统中所有进程 ps aux # 实时监控进程资源占用 top # 跟踪进程的系统调用 strace -p <PID> # 终止进程 kill -9 <PID> 1.8. 示例场景在嵌入式Linux数据采集系统中:
主进程:负责资源协调,通过 fork() 创建子进程。
子进程A:通过 exec() 执行传感器数据采集程序。
子进程B:通过共享内存(Shared Memory)与子进程A交换数据。
IPC机制:进程间通过信号量(Semaphore)同步共享内存访问。
二、进程创建与终止 2.1. 进程创建 - fork()函数在嵌入式 Linux 应用开发中,最常用的创建新进程的方法是使用 fork() 函数。fork() 函数会创建一个与父进程几乎完全相同的子进程。子进程会复制父进程的地址空间、文件描述符等资源。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t pid; // 创建子进程 pid = fork(); if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程代码 printf("This is the child process. PID: %d\n", getpid()); exit(EXIT_SUCCESS); } else { // 父进程代码 printf("This is the parent process. Child PID: %d\n", pid); } return 0; } fork() 函数返回两次,一次在父进程中,返回子进程的 PID;另一次在子进程中,返回 0。通过判断 fork() 的返回值,可以区分父进程和子进程,并在不同的分支中编写各自的代码逻辑。 2.2. 进程终止 - exit()函数进程可以通过调用 exit() 函数来终止自身。exit() 函数会清理进程占用的资源,并向父进程返回一个状态码。父进程可以通过 wait() 或 waitpid() 函数获取子进程的退出状态。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid; int status; pid = fork(); if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程代码 printf("Child process is exiting with status 42\n"); exit(42); } else { // 父进程等待子进程结束 wait(&status); if (WIFEXITED(status)) { printf("Parent process: Child exited with status %d\n", WEXITSTATUS(status)); } } return 0; } 子进程通过 exit(42) 终止,并返回状态码 42。父进程通过 wait(&status) 等待子进程结束,并使用 WIFEXITED(status) 和 WEXITSTATUS(status) 宏来检查子进程是否正常退出以及获取其退出状态码。 三、进程间通信(IPC) 3.1. 管道(Pipe)管道是一种简单的进程间通信机制,它允许一个进程将数据发送到另一个进程。管道分为匿名管道和命名管道。
3.2. 匿名管道匿名管道只能用于具有亲缘关系(如父子进程)的进程之间通信。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUFFER_SIZE 1024 int main() { int pipefd[2]; pid_t pid; char buffer[BUFFER_SIZE]; // 创建管道 if (pipe(pipefd) == -1) { perror("pipe failed"); exit(EXIT_FAILURE); } pid = fork(); if (pid < 0) { perror("fork failed"); close(pipefd[0]); close(pipefd[1]); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程关闭读端 close(pipefd[0]); // 向管道写入数据 const char *message = "Hello from child process"; write(pipefd[1], message, strlen(message)); // 关闭写端 close(pipefd[1]); } else { // 父进程关闭写端 close(pipefd[1]); // 从管道读取数据 ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("Parent process received: %s\n", buffer); } // 关闭读端 close(pipefd[0]); } return 0; } pipe(pipefd) 创建一个匿名管道,pipefd[0] 用于读,pipefd[1] 用于写。子进程关闭读端 pipefd[0],向管道写入数据后关闭写端 pipefd[1]。父进程关闭写端 pipefd[1],从管道读取数据后关闭读端 pipefd[0]。 3.2. 命名管道(FIFO)命名管道可以用于不具有亲缘关系的进程之间通信。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #define FIFO_NAME "my_fifo" #define BUFFER_SIZE 1024 int main() { int fd; char buffer[BUFFER_SIZE]; // 创建命名管道 if (mkfifo(FIFO_NAME, 0666) == -1) { perror("mkfifo failed"); exit(EXIT_FAILURE); } // 打开命名管道进行读取 fd = open(FIFO_NAME, O_RDONLY); if (fd == -1) { perror("open failed"); unlink(FIFO_NAME); exit(EXIT_FAILURE); } // 从命名管道读取数据 ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("Received: %s\n", buffer); } // 关闭文件描述符并删除命名管道 close(fd); unlink(FIFO_NAME); return 0; } mkfifo(FIFO_NAME, 0666) 创建一个名为 my_fifo 的命名管道。open(FIFO_NAME, O_RDONLY) 打开命名管道进行读取。读取数据后,关闭文件描述符并使用 unlink(FIFO_NAME) 删除命名管道。 3.3. 信号(Signal)信号是一种异步通知机制,用于向进程发送事件消息。常见的信号有 SIGTERM(终止进程)、SIGINT(由用户通过 Ctrl + C 产生)等。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void signal_handler(int signum) { printf("Received signal %d. Exiting...\n", signum); exit(EXIT_SUCCESS); } int main() { // 注册信号处理函数 if (signal(SIGINT, signal_handler) == SIG_ERR) { perror("signal registration failed"); exit(EXIT_FAILURE); } printf("Press Ctrl + C to send SIGINT signal...\n"); while (1) { sleep(1); } return 0; } signal(SIGINT, signal_handler) 注册一个信号处理函数 signal_handler,用于处理 SIGINT 信号。当用户按下 Ctrl + C 时,进程会收到 SIGINT 信号,调用 signal_handler 函数,然后终止进程。 四、实战案例 - 基于进程的简单监控系统假设我们要开发一个简单的监控系统,用于监控某个特定进程的运行状态。如果该进程异常退出,我们的监控程序将重新启动它。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <signal.h> #include <string.h> #define MONITORED_PROGRAM "./target_program" void restart_monitored_process() { pid_t pid = fork(); if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程执行被监控的程序 execl(MONITORED_PROGRAM, MONITORED_PROGRAM, NULL); perror("execl failed"); exit(EXIT_FAILURE); } } void signal_handler(int signum) { if (signum == SIGCHLD) { int status; pid_t pid; // 等待子进程结束 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Monitored process exited with status %d. Restarting...\n", WEXITSTATUS(status)); restart_monitored_process(); } } } } int main() { // 启动被监控的进程 restart_monitored_process(); // 注册信号处理函数 if (signal(SIGCHLD, signal_handler) == SIG_ERR) { perror("signal registration failed"); exit(EXIT_FAILURE); } while (1) { sleep(1); } return 0; } restart_monitored_process 函数用于启动被监控的程序。signal_handler 函数处理 SIGCHLD 信号,当被监控的子进程退出时,它会重新启动该进程。在 main 函数中,首先启动被监控的进程,然后注册 SIGCHLD 信号处理函数,最后进入一个无限循环,使监控程序持续运行。综上所述,在嵌入式Linux系统中,进程是系统资源分配和调度的基本单位。每个进程都有自己的地址空间和系统资源,通过进程间的通信和同步机制,可以实现不同进程间的数据交换和协作。进程管理对于嵌入式系统的性能和稳定性至关重要。
五、参考文献
[1] 汪明虎,欧文盛。嵌入式 Linux 开发 - 基于 ARM [M]. 人民邮电出版社,2006. [2] 嵌入式 Linux 系统开发:从零基础到实战经验,一步步揭秘 [EB/OL]. [发布时间 2024-03-20]. 嵌入式Linux系统开发:从零基础到实战经验,一步步揭秘-LINUX-PHP中文网. 介绍了嵌入式 Linux 系统开发从基础认知到实战的各个环节,其中关于进程监控等 Linux 基础概念部分,对理解嵌入式 Linux 进程相关知识有一定辅助作用。 [3] 嵌入式软件设计入门:Linux 简介与实战示例 [EB/OL]. [发布时间 2024-12-01]. 嵌入式软件设计入门:Linux简介与实战示例. 文中对 Linux 在嵌入式系统中的应用进行了介绍,虽然未专门针对进程实战开发,但 Linux 在嵌入式系统中的特性与应用场景等内容,有助于从宏观角度理解进程开发所处的环境。 [4] 【嵌入式 Linux (基础篇)】嵌入式 Linux 底层系统开发流程和应用开发流程 [EB/OL]. [发布时间 2024-10-24]. 【嵌入式Linux(基础篇)】嵌入式Linux底层系统开发流程和应用开发流程_嵌入式linux uboot kernel 和根文件系统开发过程-CSDN博客. 详细阐述了嵌入式 Linux 底层系统开发流程和应用开发流程,进程开发作为应用开发的一部分,该流程介绍有助于明确进程实战开发在整个嵌入式 Linux 开发体系中的位置和作用。 [5] 嵌入式系统文献综述 [EB/OL].. 嵌入式系统文献综述 - 道客巴巴. 该文献综述介绍了嵌入式系统的相关背景及实验教学系统等内容,对了解嵌入式系统整体发展背景以及相关硬件平台等知识有帮助,而硬件平台是进程实战开发的基础支撑。 [6] 嵌入式软件工程师学习路线与书籍推荐! [发布时间 2023-07-18]. 推荐了嵌入式学习书籍,并对嵌入式 Linux 高级程序设计中进程相关概念、进程间通信等内容的学习提供了指导,为深入学习进程实战开发提供了学习资源参考 。
【嵌入式Linux应用开发基础】进程实战开发由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【嵌入式Linux应用开发基础】进程实战开发”