欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > 【嵌入式Linux应用开发基础】进程实战开发

【嵌入式Linux应用开发基础】进程实战开发

2025/2/21 4:32:32 来源:https://blog.csdn.net/weixin_37800531/article/details/145657408  浏览:    关键词:【嵌入式Linux应用开发基础】进程实战开发

目录

一、进程基础概念回顾

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 1024int 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 1024int 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 高级程序设计中进程相关概念、进程间通信等内容的学习提供了指导,为深入学习进程实战开发提供了学习资源参考 。


版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词