当前位置: 首页 > article >正文

# 嵌入式基础学习|C语言——进程篇综合(含进阶)

进程与多任务编程

进程的概念

进程是进行中的程序,即程序的一次执行过程。它包含了程序在内存中的代码、数据以及CPU的执行状态。进程是程序的实例,是操作系统进行资源分配和调度的基本单位。

程序:静态的代码文件,存储在硬盘上。
进程:动态的执行实体,加载到内存中运行。
示例:
当我们运行一个程序时,比如 ./a.out,操作系统会将程序加载到内存中,创建一个进程来执行它。每个进程都有独立的内存空间和资源。

为什么需要进程?

早期的计算机一次只能运行一个程序,随着CPU性能的提升,现代操作系统支持多任务处理,即同时运行多个进程。进程的概念使得操作系统能够有效地管理多个程序的并发执行。

并发:多个进程在同一时间段内交替执行。
并行:多个进程在同一时刻同时执行(需要多核CPU)。

进程的组成

进程由以下几部分组成:

代码段:存放程序的执行代码。
数据段:存放全局变量和静态变量。
堆:动态分配的内存区域。
栈:存放函数调用的局部变量和返回地址。
PCB(进程控制块):操作系统用于管理进程的数据结构,包含进程的PID、PPID、状态等信息。
示例:
使用 size ./a.out 命令可以查看进程的内存布局:

bash

$ size ./a.out
text data bss dec hex filename
1515 600 8 2123 84b ./a.out
text:代码段
data:已初始化的全局变量
bss:未初始化的全局变量

进程的状态

进程在执行过程中会处于不同的状态,常见的状态包括:

R(运行态):进程正在执行或等待CPU调度。
S(可中断睡眠态):进程等待某个事件完成(如I/O操作)。
D(不可中断睡眠态):进程等待不可中断的事件(如磁盘I/O)。
T(暂停态):进程被暂停执行。
Z(僵尸态):进程已终止,但父进程尚未回收其资源。
示例:
使用 ps -aux 命令可以查看进程的状态:

bash

$ ps -aux | grep a.out

进程管理命令

top:实时查看系统进程状态。
ps:查看当前进程信息。
pstree:以树状结构显示进程关系。
kill:向进程发送信号,终止进程。
示例:
终止进程:

bash

$ kill -9 5266

进程的创建与结束

在Linux下,进程的创建通过 fork() 系统调用实现。

fork():创建一个子进程,子进程是父进程的副本。
父进程返回子进程的PID。
子进程返回0。
示例:
创建子进程并打印信息:

代码示例

#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 {
perror(“fork失败”);
}
return 0;
}
输出:

父进程: PID = 1234, 子进程PID = 1235
子进程: PID = 1235

进程间的关系

孤儿进程:父进程终止后,子进程由 init 进程收养。
僵尸进程:子进程终止后,父进程未回收其资源。
示例:
查看进程树:

bash

$ pstree -sp 1234

进程的应用场景

并发执行:通过创建多个子进程,实现并发处理任务。
执行不同程序:通过 fork() 和 exec() 函数族,子进程可以执行与父进程不同的程序。
示例:
使用 exec() 执行新程序:

c

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

int main() {
pid_t pid = fork();
if (pid == 0) {
execl("/bin/ls", “ls”, NULL);
} else if (pid > 0) {
printf(“父进程: PID = %d\n”, getpid());
} else {
perror(“fork失败”);
}
return 0;
}
输出:

父进程: PID = 1234
file1.txt file2.txt

进程间的数据独立性

父子进程拥有独立的内存空间,对数据的修改不会相互影响。

示例:
父子进程修改全局变量:

c

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

int global_var = 0;

int main() {
pid_t pid = fork();
if (pid == 0) {
global_var += 2;
printf(“子进程: global_var = %d\n”, global_var);
} else if (pid > 0) {
global_var += 1;
printf(“父进程: global_var = %d\n”, global_var);
} else {
perror(“fork失败”);
}
return 0;
}
输出:

父进程: global_var = 1
子进程: global_var = 2

总结

进程是操作系统进行资源分配和调度的基本单位,通过 fork() 系统调用可以创建子进程,实现并发执行。父子进程拥有独立的内存空间,数据修改不会相互影响。进程的状态和关系可以通过 ps、pstree 等命令查看,进程的终止可以通过 kill 命令实现。

拓展:
在多核CPU环境下,进程可以并行执行,进一步提升系统性能。此外,进程间通信(IPC)机制如管道、消息队列、共享内存等,可以实现进程间的数据交换和协作。

代码示例:
以下是一个简单的多进程并发处理任务的示例:

c

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

void task(int id) {
printf(“任务 %d 开始执行\n”, id);
sleep(2); // 模拟任务执行时间
printf(“任务 %d 执行完成\n”, id);
}

int main() {
for (int i = 0; i < 3; i++) {
pid_t pid = fork();
if (pid == 0) {
task(i);
exit(0);
} else if (pid < 0) {
perror(“fork失败”);
exit(1);
}
}
for (int i = 0; i < 3; i++) {
wait(NULL); // 等待所有子进程结束
}
printf(“所有任务执行完成\n”);
return 0;
}
输出:
任务 0 开始执行
任务 1 开始执行
任务 2 开始执行
任务 0 执行完成
任务 1 执行完成
任务 2 执行完成
所有任务执行完成
通过 fork() 创建多个子进程,每个子进程执行不同的任务,父进程等待所有子进程结束后再继续执行。


http://www.kler.cn/a/557022.html

相关文章:

  • 【Mamba和Transformer的关系】
  • 【排版教程】如何在Word/WPS中优雅的插入参考文献
  • 一篇文章理解常用的前端设计模式
  • Spring Boot 中多线程工具类的配置与使用:基于 YAML 配置文件
  • 脚本实战第一发:所有的请求都可以被 Python 模拟
  • 论文笔记-WSDM2025-ColdLLM
  • 外盘期货数据分析新视角:分钟级高频数据解析
  • 七、敏捷开发工具:持续集成与部署工具
  • 使用Python PyTorch框架+卷积神经网络(CNN)构造基于超图的综合立体交通超网络模型
  • 获取钉钉OA审批数据
  • C++——AVL平衡二叉树
  • AI大模型-提示工程学习笔记15—主动提示 (Active Prompt)
  • 1.21作业
  • 汽车自动驾驶辅助L2++是什么?
  • 数字人面试
  • STM32 HAL库USART串口DMA IDLE中断编程:避坑指南
  • 机试刷题_编辑距离(二)【python】
  • 【Flink 实战】Flink 中 Akka 通信与内存占用分析
  • 【设计模式】【创建型模式】单例模式(Singleton)
  • golang时间相关函数总结