目录
一、XTerminal下的Linux系统调用编程
1.1理解进程和线程的概念并在Linux系统下完成相应操作
(1) 进程
(2)线程
(3) 进程 vs 线程
(4)Linux 下的实践操作
1.2Linux的“虚拟内存管理”和stm32正式物理内存(内存映射)的区别
(1)Linux虚拟内存管理
(2)STM32物理内存映射
(3)主要区别
1.3理解 Linux系统调用函数 fork()、wait()、exec() 等并通过vi 编辑一个c程序
(1)系统调用函数介绍
fork()
wait()
exec()
(2)创建syscall_demo.c
(3)编写示例程序
(4)编译运行
二、在树莓派中创建多个账号并完成Linux系统调用函数联系
1.1组员账号创建
1.2在树莓派环境中学习并调用fork()、wait()和exec()函数
(1)创建文件 syscall_demo.c
(2)编写示例程序
(3)编译运行
一、XTerminal下的Linux系统调用编程
1.1理解进程和线程的概念并在Linux系统下完成相应操作
(1) 进程
定义:进程是程序的一次执行实例,拥有独立的内存空间、文件描述符和系统资源。
特点:
-
每个进程有唯一的 PID(进程ID)。
-
进程间相互隔离,通信需通过 IPC(进程间通信) 机制(如管道、共享内存等)。
-
创建进程通过
fork()
或exec()
系统调用。
(2)线程
定义:线程是进程内的执行单元,共享同一进程的内存和资源。
特点:
-
线程有独立的 栈,但共享堆、全局变量和文件描述符。
-
创建线程通过
pthread_create()
(POSIX 线程库)。 -
轻量级,切换开销比进程小。
(3) 进程 vs 线程
特性 | 进程 | 线程 |
---|---|---|
独立性 | 完全隔离 | 共享同一进程资源 |
创建开销 | 大(需复制内存) | 小(共享内存) |
通信方式 | IPC(管道、信号等) | 直接读写共享变量 |
崩溃影响 | 不影响其他进程 | 导致整个进程终止 |
(4)Linux 下的实践操作
ps -a
通过kill命令尝试终止该进程
kill -9 PID
我们发现提示没有那个进程。这是因为该进程为临时进程,执行完毕后已自动退出,因此报错 。
我们可以通过下面命令查找系统中所有的进程及其对应的PID
ps -aux
我们可以对应选择一个进程进行结束
1.2Linux的“虚拟内存管理”和stm32正式物理内存(内存映射)的区别
(1)Linux虚拟内存管理
核心机制: Linux通过虚拟内存抽象物理内存,为每个进程提供独立的、连续的虚拟地址空间(通常为4GB,32位系统),由MMU(内存管理单元)动态映射到物理内存或磁盘交换空间。
其工作流程为:
进程访问虚拟地址 → MMU查页表 → 若页在物理内存则访问,否则触发缺页异常 → 内核加载缺失页或终止进程
(2)STM32物理内存映射
核心机制: STM32等嵌入式MCU通常直接操作物理内存,通过内存映射(将外设寄存器、Flash、RAM等硬件资源分配到固定的物理地址。
其典型内存布局为:
0x00000000 - 0x1FFFFFFF: Flash(代码存储) 0x20000000 - 0x2001FFFF: SRAM(数据) 0x40000000 - 0x5FFFFFFF: 外设寄存器
(3)主要区别
特性 | Linux虚拟内存 | STM32物理内存映射 |
---|---|---|
地址空间 | 虚拟地址(进程独立) | 物理地址(全局唯一) |
硬件支持 | 依赖MMU实现地址转换 | 无MMU,直接访问物理地址 |
内存扩展 | 支持Swap扩展虚拟内存 | 仅限芯片内置的物理内存 |
内存保护 | 通过页表实现权限控制 | 无保护,需开发者谨慎操作 |
外设访问 | 通过/dev/mem 或驱动间接访问 | 直接读写内存映射的外设寄存器 |
使用场景 | 通用计算(多任务、复杂应用) | 实时嵌入式系统(确定性、低延迟) |
存在差异的原因:
Linux需要支持多进程、大内存应用,虚拟内存提供灵活性和安全性。
STM32:追求实时性和确定性,省去MMU降低开销,适合裸机或RTOS(如FreeRTOS)。
1.3理解 Linux系统调用函数 fork()、wait()、exec() 等并通过vi 编辑一个c程序
(1)系统调用函数介绍
fork()
功能:创建一个新的进程(子进程),子进程是父进程的副本。
返回值:
-
父进程中返回子进程的PID(>0)。
-
子进程中返回0。
-
失败时返回-1。
头文件:<unistd.h>
wait()
功能:父进程等待子进程结束,并回收子进程的资源(防止僵尸进程)。
参数:int *status
(存储子进程的退出状态)。
返回值:成功时返回子进程PID,失败时返回-1。
头文件:<sys/wait.h>
exec()
功能:替换当前进程的映像为新的程序(如运行另一个可执行文件)。
常用变体:
-
execl()
:参数列表形式。 -
execv()
:参数数组形式。 -
execvp()
:自动搜索PATH
环境变量。
返回值:成功时不返回,失败时返回-1。
头文件:<unistd.h>
(2)创建syscall_demo.c
vi syscall_demo.c
(3)编写示例程序
进入vi编译器后,按"i"进入插入模式
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid;int status;pid = fork();if (pid < 0) {fprintf(stderr, "Fork failed!\n");return 1;} else if (pid == 0) {printf("Child Process (PID=%d): Hello from fork()!\n", getpid());execlp("ls", "ls", "-l", NULL);perror("exec failed");return 1;} else {printf("Parent Process (PID=%d): Waiting for child...\n", getpid());wait(&status);printf("Parent: Child exited with status %d\n", WEXITSTATUS(status));}return 0;
}
编写完成后按Esc退出插入模式,输入“:wq”保存并退出
注:
保存文件 | Esc → :w → 回车 | 保存但不退出 |
---|---|---|
保存并退出 | Esc → :wq → 回车 | 保存并退出 |
强制退出不保存 | Esc → :q! → 回车 | 丢弃所有修改 |
(4)编译运行
输入下面代码进行编译运行:
#编译
gcc syscall_demo.c -o syscall_demo
#添加权限
chmod +x syscall_demo
# 运行
./syscall_demo
结果解释:
Parent Process (PID=960685): Waiting for child
#父进程(PID=960685)打印信息,表示已通过 fork() 创建子进程,并调用 wait() 进入阻塞状态,等待子进程结束。Child Process (PID=960686): Hello from fork()!
#子进程(PID=960686)被创建后,打印自己的 PID 和消息,随后调用execlp("ls", "ls", "-l", NULL)。
子进程的代码映像被替换为 ls -l 命令,原程序的后续代码(如 perror)不再执行。-rwxr-xr-x 1 zhangzy group2 17056 Apr 3 23:23 a.out
-rwxr-xr-x 1 zhangzy group2 17056 Apr 3 23:25 syscall_demo
-rw-r--r-- 1 zhangzy group2 610 Apr 3 23:23 syscall_demo.c
-rwxr-xr-x 1 zhangzy group2 65 Apr 3 22:12 test.sh
#ls -l 命令的输出,显示当前目录下的文件详情:
总用量 48Parent: Child exited with status
#子进程(ls -l)执行完毕后,父进程的 wait(&status) 返回。
WEXITSTATUS(status) 提取子进程的退出状态码 0,表示 ls 命令成功执行。
二、在树莓派中创建多个账号并完成Linux系统调用函数联系
1.1组员账号创建
首先要进行树莓派的VNC远程登录,具体步骤可以看我前面的博客:树莓派3b:环境配置,VNC远程控制并进行简单代码运行_树莓派vnc-CSDN博客
先进入VNC命令行
(1)创建用户
为每个组员创建一个独立的系统账号,并生成各自的目录
sudo adduser user1
sudo adduser user2
(2)配置用户权限
确保用户有基本的开发权限,(如sudo权限)
# 将用户添加到sudo组(允许执行管理员命令)
sudo usermod -aG sudo username1# 验证用户组
groups username1
我们通过命令行测试,发现新用户可以进行登录(后续代码也可以通过电脑命令行实现)
1.2在树莓派环境中学习并调用fork()、wait()和exec()函数
(1)创建文件 syscall_demo.c
nano syscall_demo.c
(2)编写示例程序
该程序展示了Linux系统调用fork()、exec()、和wait()函数的工作过程
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid;int status;// 1. fork() 示例pid = fork();if (pid < 0) {fprintf(stderr, "Fork failed!\n");return 1;} else if (pid == 0) {// 子进程printf("Child Process (PID=%d): Hello from fork()!\n", getpid());// 2. exec() 示例:替换为执行 'ls' 命令execlp("ls", "ls", "-l", NULL);// 如果exec失败,才会执行到这里perror("exec failed");return 1;} else {// 父进程printf("Parent Process (PID=%d): Waiting for child...\n", getpid());// 3. wait() 示例wait(&status);printf("Parent: Child exited with status %d\n", WEXITSTATUS(status));}return 0;
}
(3)编译运行
编译:
gcc syscall_demo.c -o syscall_demo
运行:
./syscall_demo