目录
Linux系统调用编程
在学习Linux系统调用编程之前,理解进程和线程是至关重要的。它们是操作系统中两个核心的执行单元,共同构成了现代多任务操作系统的基础。为了更好地理解Linux系统调用的原理和应用,我们需要先从这两个概念入手,明确它们的定义、特点以及它们在操作系统中的作用。
进程和线程
进程和线程的概念
进程(Process):
进程是操作系统中一个独立的执行单元,拥有自己的内存空间、文件描述符、环境变量等资源。它是资源分配和调度的基本单位。
特点:
- 每个进程有独立的内存空间,彼此隔离。
- 进程之间的通信需要通过特定的机制(如管道、消息队列等)。
- 创建和销毁进程的开销较大。
一个进程就像一个独立的房间,房间里有自己的家具(内存)、门窗(文件描述符)等,每个房间都是独立的,互不干扰。
线程(Thread):
线程是进程中的一个执行单元,多个线程可以共享进程的内存空间和资源。它是CPU调度的最小单位。
特点:
- 线程共享进程的内存空间,通信效率高。
- 线程的创建和切换开销较小。
- 一个进程可以有多个线程,这些线程并发执行。
线程就像是房间里的不同活动(比如一个人在看书,另一个人在听音乐),它们共享房间的资源,但可以同时进行。
操作进程查询以及终止
这里使用XTerminal虚拟机为例,点开自己的设备,进入命令行,输入合适的命令进行查询、终止等操作。
部分命令及其讲解:
ps -a
ps:用于查看当前系统的进程状态
-a:显示所有用户的进程(不仅仅是当前终端的进程)
kill -9 <PID>
kill:用于发送信号给进程,默认发送的是SIGTERM(终止信号)
-9:发送SIGKILL信号,强制终止进程(进程无法捕获或忽略这个信号)
<PID>:需要终止的进程ID
输入查询命令,得到以下结果:
PID (Process ID):表示进程的唯一标识符。
在截图中,PID 是 1233019,表示当前运行的进程的 ID
TTY (Teletype):表示进程运行的终端设备名称。
在截图中,TTY 是 pts/3,表示进程运行在一个伪终端上
TIME (CPU Time):表示进程占用 CPU 的时间。
在截图中,TIME 是 00:00:00,表示该进程刚刚启动,还没有占用 CPU 时间
CMD (Command):表示进程的名称或执行的命令。
在截图中,CMD 是 ps,表示当前运行的命令是 ps
查询的进程即为我们刚才运行的命令pa -a;下面添加一个进程:sleep 100 &,表示一个sleep的任务将在后台运行100秒,再次查询则得到下图所示:
可以看到已经多了一个sleep的进程,最后我们用kill 命令终止它,再查询:
这样就成功实现了我们进程的查询以及终止。接下来我们来看Linux的“虚拟内存管理”。
Linux的“虚拟内存管理”
“虚拟内存管理”的解释
Linux的虚拟内存管理是一种操作系统的核心功能,它通过以下机制实现:
虚拟地址空间:每个进程都有独立的虚拟地址空间,通常为4GB(32位系统)或更大(64位系统)。虚拟地址通过内存管理单元(MMU)映射到物理地址。
页表:虚拟地址通过页表映射到物理地址。页表由操作系统维护,存储在物理内存中。
内存隔离:每个进程的虚拟地址空间是独立的,避免了进程间的内存冲突。
内存扩展:通过交换(swap)或分页(paging)机制,将暂时不用的内存页写入磁盘,从而扩展物理内存的使用。
内存保护:虚拟内存提供了访问权限检查,确保进程只能访问授权的内存区域。
虚拟内存的管理是以进程为基础,每个进程都有各自的虚拟地址空间,每个进程的内核空间是所有进程所共享的。 在Linux中虚拟地址空间主要是由最高层的 mm_struct 和 较高层次的 vm_ area_ struct 结构来描述的(如图)。
在 Linux 内核中,每个进程由一个 task_struct 描述符表示。task_struct 包含一个指向 mm_struct 的指针(mm),用于管理进程的虚拟内存状态。mm_struct 是进程虚拟内存的核心管理结构,包含两个关键字段:pgd 和 mmap。
pgd 字段:
- pgd 指向页全局目录(Page Global Directory,PGD)的基址,这是分页机制的第一级页表。
- 当内核运行该进程时,会将 pgd 的值存入 CR3 控制寄存器,以便处理器能够访问该进程的虚拟内存。
mmap 字段:
- mmap 指向一个 vm_area_struct(虚拟内存区域结构)的链表。
- 每个 vm_area_struct 描述了进程虚拟地址空间的一个连续区域,例如代码区、数据区、共享库等。
- 每个 vm_area_struct 包含以下字段:
- vm_start 和 vm_end:表示该虚拟内存区域的起始和结束地址。
- vm_prot 和 vm_flags:描述该区域的访问权限和标志。
- vm_next:指向链表中的下一个 vm_area_struct。
而STM32的真实物理内存(内存映射)是一种嵌入式处理器,其内存管理基于真实的物理地址;采用统一编址方式,将程序存储器、数据存储器和外设寄存器组织在4GB的线性地址空间内。
其内存地址空间被划分为多个固定区域,如Flash存储区、SRAM区和外设寄存器区,这些区域的起始地址和大小由硬件设计决定。程序直接使用物理地址访问内存和外设,无需虚拟地址到物理地址的映射。此外,所有程序共享同一物理地址空间,没有内存隔离机制。
特性 |
Linux虚拟内存管理 |
STM32真实物理内存 |
地址空间 |
每个进程有独立的虚拟地址空间 |
所有程序共享同一物理地址空间 |
内存隔离 |
支持内存隔离,避免进程间冲突 |
没有内存隔离,容易冲突 |
内存扩展 |
支持内存交换(Swap),扩展物理内存 |
没有内存交换功能,所有数据必须在物理内存中 |
地址映射 |
虚拟地址通过页表映射到物理地址 |
直接使用物理地址,没有虚拟地址 |
适用场景 |
适用于多任务操作系统,强调安全性和灵活性 |
适用于嵌入式系统,强调硬件直接访问和实时性 |
相较而言,Linux的虚拟内存管理和STM32的真实物理内存映射在设计目标和实现方式上有显著不同,分别适用于不同的应用场景。Linux的虚拟内存管理提供了内存隔离和扩展能力,而STM32的真实物理内存映射则强调硬件直接访问和实时性。
了解完这些过后,我们就可以开始Linux系统编程了。
Linux系统调用函数
目录创建
以下图目录为例,下图已经是建立好的样式,可以看到homework的目录,下面开始讲解实操步骤。
cd ~ # 进入home目录
mkdir homework # 创建子目录
cd homework # 进入新创建的目录
ls # 验证目录创建成功
创建C代码并运行
首先,我们要通过命令进入创建的homework文件,然后在文件下新建项目,随后保存编译运行即可。
cd ~/homework # 进入homework目录
ls # 确认目录内容
vi test001.c # 创建并编辑test.c文件
# 按Esc,输入:wq保存并退出
ls # 确认test.c文件已创建
gcc test001.c -o test001 # 编译代码
./test001 # 运行程序
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
// 错误处理
fprintf(stderr, "Fork failed\n");
return 1;
} else if (pid == 0) {
// 子进程执行的代码
printf("子进程: PID=%d, PPID=%d\n", getpid(), getppid());
execl("/bin/ls", "ls", "-l", NULL); // 使用execl替换子进程的执行环境
} else {
// 父进程执行的代码
printf("父进程: PID=%d, 子进程PID=%d\n", getpid(), pid);
wait(NULL); // 等待子进程结束
printf("子进程已结束\n");
}
return 0;
}
运行截图如下:
分析结果:
进程输出:
父进程: PID=1254192, 子进程PID=1254193
父进程的PID是1254192。
子进程的PID是1254193。
子进程输出:
子进程: PID=1254193, PPID=1254192
子进程的PID是1254193。
子进程的父进程PID(PPID)是1254192,与父进程的PID一致。
ls -l命令的输出:
总用量 44
-rwxr-xr-x 1 chenzy group17 17040 апр 4 17:40 test
-rwxr-xr-x 1 chenzy group17 17040 апр 4 17:41 test001
-rwxr-xr-x 1 chenzy group17 530 апр 4 17:40 test001.c
ls -l命令列出了当前目录下的文件及其详细信息。
文件列表包括test、test001和test001.c。
每个文件的权限、所有者、组、大小和修改时间都显示出来。
子进程结束:
子进程已结束
父进程通过wait()函数等待子进程结束,并打印了子进程结束的消息。
这个结果表明程序运行正常,符合预期
总结
本次学习,从进程和线程的基本概念入手,掌握它们在操作系统中的作用和特点。通过实践操作,使用ps -a和kill命令查询和终止进程,理解Linux虚拟内存管理和STM32真实物理内存的区别。在编程实践中,创了一个使用fork()、wait()和exec()的C程序,并通过编译和运行验证了其功能。