文章目录
- 一、进程和线程
- 二、Linux的虚拟内存管理和stm32的真实物理内存
- **Linux虚拟内存管理**
- STM32物理内存映射
- 2. 主要区别
- 三、Linux系统调用函数 fork()、wait()、exec()
- 1. fork():创建子进程
- 2. wait():等待子进程状态改变
- 3. exec():替换进程映像
- 综合应用
- 四、总结
一、进程和线程
进程:进程是程序的执行实例,是操作系统分配资源(CPU、内存、文件等)的基本单位。每个进程拥有独立的地址空间和系统资源。一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
线程:线程是进程内的执行单元,是CPU调度的基本单位。一个进程可包含多个线程,所有线程共享进程的资源(如内存、文件)。如浏览器的一个标签页可能用一个线程加载页面,另一个线程处理用户输入。
在Linux虚拟机下查看进程
使用命令:ps -a
终止进程:kill 进程号
二、Linux的虚拟内存管理和stm32的真实物理内存
Linux虚拟内存管理
虚拟地址空间:每个进程拥有独立的虚拟地址空间(通常为4GB或更大),由**内存管理单元(MMU)**动态映射到物理内存或磁盘交换空间。
分页机制:物理内存被划分为固定大小的页(如4KB),虚拟地址通过页表转换到物理页帧。未使用的页可被换出到磁盘(Swap)。
内存保护:不同进程的内存空间隔离,防止非法访问(如写只读页会触发段错误)。
按需分配:内存分配延迟到实际使用时(如malloc申请内存后,物理页在首次访问时才分配)。
共享内存:多个进程可通过虚拟内存映射共享同一物理内存(如动态库)。
STM32物理内存映射
直接访问物理地址:程序直接操作物理内存和外设寄存器,无地址转换层(通常无MMU)。
静态内存布局:内存和外设的地址在芯片设计时固定(如Flash、SRAM、GPIO寄存器的地址在数据手册中定义)。
确定性访问:无分页或交换机制,内存访问延迟固定,适合实时性要求高的场景。
手动管理:开发者需自行规划内存使用(如静态分配或简单动态分配)。
2. 主要区别
特性 | Linux虚拟内存 | STM32物理内存映射 |
---|---|---|
地址空间 | 虚拟地址(由MMU转换) | 直接使用物理地址 |
硬件支持 | 依赖MMU实现地址转换和保护 | 无MMU,可能支持MPU(仅内存保护) |
内存隔离 | 进程间隔离,防止越界访问 | 无隔离,需开发者保证正确性 |
多任务支持 | 通过虚拟内存实现进程独立地址空间 | 需RTOS配合(如FreeRTOS)或裸机调度 |
动态内存分配 | 灵活(如malloc +缺页中断) | 通常静态分配或简单堆管理(易碎片化) |
交换空间 | 支持磁盘交换扩展可用内存 | 无交换,受限于物理内存大小 |
外设访问 | 通过内核驱动(如mmap 映射到用户空间) | 直接读写外设寄存器地址 |
实时性 | 不确定(因页错误或交换延迟) | 确定性的低延迟访问 |
三、Linux系统调用函数 fork()、wait()、exec()
1. fork():创建子进程
功能:复制当前进程,生成一个几乎完全相同的子进程(包括代码、数据、堆栈、文件描述符等)。
子进程从 fork() 的返回处开始执行,与父进程并发运行。
关键特性
返回值 | 含义 |
---|---|
>0 | 父进程中返回子进程PID |
0 | 子进程中返回0 |
-1 | 调用失败 |
写时复制(Copy-On-Write, COW)
父子进程共享物理内存,直到任一进程尝试修改数据时才会复制内存页,提高效率。
示例代码
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
return 1;
} else if (pid == 0) {
printf("子进程 (PID: %d)\n", getpid());
} else {
printf("父进程 (Child PID: %d)\n", pid);
}
return 0;
}
2. wait():等待子进程状态改变
功能
阻塞父进程,直到某个子进程终止或收到信号。
回收子进程资源(避免僵尸进程)。
常用函数
wait(int *status):等待任意子进程结束,状态存入 status。
waitpid(pid_t pid, int *status, int options):
可指定等待的子进程PID(pid=-1 表示任意子进程),支持非阻塞选项(如 WNOHANG)。
状态宏(检查子进程退出原因):
WIFEXITED(status):子进程正常退出。
WEXITSTATUS(status):获取子进程退出码。
WIFSIGNALED(status):子进程被信号终止。
示例代码
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("子进程存在...\n");
_exit(42); // 子进程退出码42
} else {
int status;
wait(&status);
if (WIFEXITED(status)) {
printf("子进程存在码 %d\n", WEXITSTATUS(status));
}
}
return 0;
}
3. exec():替换进程映像
功能
加载新程序到当前进程的内存空间,替换原有代码、数据、堆栈等。
调用成功后,原进程的代码不再执行(除非 exec() 失败)。
常用函数(参数传递方式不同):
函数 | 参数格式 | 示例 |
---|---|---|
execl() | 可变参数列表(NULL结尾) | execl(“/bin/ls”, “ls”, “-l”, NULL) |
execv() | 字符串数组(argv风格) | char *args[] = {“ls”, “-l”, NULL}; execv(“/bin/ls”, args) |
execvp() | 自动搜索 PATH 环境变量 | execvp(“ls”, args) |
关键特性
无返回值:成功时不返回,失败时返回 -1(需检查错误)。
保留的文件描述符:默认保持打开(可通过 fcntl 设置 FD_CLOEXEC 关闭)。
示例代码
#include <unistd.h>
#include <stdio.h>
int main() {
execl("/bin/ls", "ls", "-l", NULL); // 替换为执行 `ls -l`
perror("exec 失败"); // 仅当exec失败时执行
return 1;
}
综合应用
//C程序来说明fork()的用法&
//exec()系统调用以创建进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int ret = 1;
int status;
pid = fork();
if (pid == -1){
// pid == -1 表示发生了错误
printf("can't fork, error occured\n");
exit(EXIT_FAILURE);
}
else if (pid == 0){
// pid == 0 表示创建了子进程
// getpid() 返回调用进程的进程id
// 返回子进程的进程id
printf("child process, pid = %u\n",getpid());
// 返回子进程的父进程,即父进程本身
printf("parent of child process, pid = %u\n",getppid());
// argv list第一个参数应该指向
// 与正在执行的文件关联的文件名
// 数组指针必须以NULL结尾
// 指针
char * argv_list[] = {"ls","-lart","/home",NULL};
// execv()仅在发生错误时返回。
// 返回值为-1
execv("ls",argv_list);
exit(0);
}
else{
// 为pid返回一个正数
// 父进程
// getppid()返回进程的父进程id
// 调用进程
// 返回父进程ID的父进程
printf("Parent Of parent process, pid = %u\n",getppid());
printf("parent process, pid = %u\n",getpid());
// 父进程对子进程调用waitpid()
// waitpid()系统调用暂停
// 调用进程直到pid指定的子进程
// 状态改变
// 有关所有标志或选项,请参见wait()手册
if (waitpid(pid, &status, 0) > 0) {
if (WIFEXITED(status) && !WEXITSTATUS(status))
printf("program execution successful\n");
else if (WIFEXITED(status) && WEXITSTATUS(status)) {
if (WEXITSTATUS(status) == 127) {
// execv failed
printf("execv failed\n");
}
else
printf("program terminated normally,"
" but returned a non-zero status\n");
}
else
printf("program didn't terminate normally\n");
}
else {
// waitpid() failed
printf("waitpid() failed\n");
}
exit(0);
}
return 0;
}
运行效果:
四、总结
本次学习了进程和线程、虚拟内存管理、系统函数fork()、wait()、exec()的相关知识以及在Linux虚拟机上的基本使用。
参考链接:1.Linux下的fork和exec函数
2.探索 Linux 编程基础 深入理解 fork 函数的实现原理与实践应用
3.详解Linux中的fork,exec,wait
4.添加进程和线程的区别(超详细)