1. 进程与线程概念及Linux操作
(1) 进程与线程概念
进程:资源分配的基本单位,拥有独立的地址空间、文件描述符、环境变量等。不同进程间内存隔离。
线程:CPU调度的基本单位,同一进程内的线程共享内存空间和资源(如全局变量),但拥有独立的栈和寄存器。
(2) Linux操作示例
查看进程PID:(PID:进程的唯一标识符。)
使用 ps -a 命令查看系统中正在运行的进程及其PID;
终止进程:
使用 kill 命令终止指定PID的进程:
二、stm32物理内存与Linux虚拟内存管理
(1) Linux虚拟内存管理
核心机制:
每个进程拥有独立的虚拟地址空间(通常4GB,32位系统),通过页表映射到物理内存。
支持内存分页、交换空间(Swap)、内存保护(防止越界访问)和共享内存。由MMU(内存管理单元)实现虚拟地址到物理地址的转换。
优势:
进程间内存隔离,提高系统稳定性。允许使用比物理内存更大的地址空间。支持动态内存分配和内存复用。
(2) STM32真实物理内存(内存映射)
核心机制:
直接操作物理地址,无虚拟内存概念。外设(如GPIO、UART)通过内存映射I/O访问,寄存器地址固定。内存管理由开发者手动控制(如静态分配或简单动态分配)。
对比差异:
三、Linux系统调用:fork()、wait()、exec()
(1)函数含义
fork():
作用:创建子进程,子进程复制父进程的代码、数据和堆栈。
返回值:父进程返回子进程的PID。子进程返回0。失败返回-1。
wait():
作用:父进程阻塞等待子进程终止,并回收其资源(避免僵尸进程)。
参数:int *status,用于保存子进程退出状态。
exec() 函数族:
作用:加载新程序替换当前进程的地址空间(如 execvp, execl)。
特点:调用成功后,原进程代码不再执行。
(2)调用示例
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid == 0) { // 子进程printf("Child PID: %d\n", getpid());execlp("/bin/ls", "ls", "-l", NULL); // 执行 ls -l} else if (pid > 0) { // 父进程int status;wait(&status); // 等待子进程结束printf("Child exit status: %d\n", WEXITSTATUS(status));} else {perror("fork failed");}return 0;
}
(3)执行流程
1、fork() 创建子进程。
2、子进程调用 execlp() 执行 ls -l 命令。
3、父进程通过 wait() 等待子进程结束,并获取其退出状态。
(4)常见问题
僵尸进程:子进程终止但父进程未调用 wait(),进程表中仍保留其信息。
解决:父进程必须调用 wait() 或设置信号处理函数(SIGCHLD)。
孤儿进程:父进程先于子进程终止,子进程由 init 进程(PID=1)接管。
四、Ubuntu系统调用函数
(1)创建目录
在终端中执行以下命令,在用户 home 目录下创建子目录 :
cd ~ # 进入用户主目录
mkdir 作业 # 创建名为 "作业" 的目录
cd 作业 # 进入该目录
(2)vi编写C代码
vi 程序名.c
//按 i 进入插入模式,输入以下代码(实现通过 write 系统调用输出信息):
#include <unistd.h> // 包含系统调用相关头文件int main() {// 使用 write 系统调用向标准输出(文件描述符 1)写入数据write(1, "Hello, System Call!\n", 20); // 20 是字符串长度(含结尾的换行符)return 0;
}//保存并退出 vi; 按 ESC 退出插入模式。输入 :wq 保存文件并退出。
编译运行:
1、gcc -o 程序名 程序名.c
2、./程序名
树莓派实现:
要创建一个新用户并授予sudo权限,可以按照以下步骤进行:
创建新账号后会让你输入一些账号个人信息,可以不用管全部Enter跳过: