Linux系统调用编程

发布于:2025-04-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

Linux系统调用编程

进程和线程

进程和线程的概念

操作进程查询以及终止

Linux的“虚拟内存管理”

“虚拟内存管理”的解释

Linux系统调用函数

目录创建

创建C代码并运行

分析结果:

总结


Linux系统调用编程

在学习Linux系统调用编程之前,理解进程和线程是至关重要的。它们是操作系统中两个核心的执行单元,共同构成了现代多任务操作系统的基础。为了更好地理解Linux系统调用的原理和应用,我们需要先从这两个概念入手,明确它们的定义、特点以及它们在操作系统中的作用。

进程和线程

进程和线程的概念

进程(Process):

进程是操作系统中一个独立的执行单元,拥有自己的内存空间、文件描述符、环境变量等资源。它是资源分配和调度的基本单位。

特点:

  1. 每个进程有独立的内存空间,彼此隔离。
  2. 进程之间的通信需要通过特定的机制(如管道、消息队列等)。
  3. 创建和销毁进程的开销较大。

一个进程就像一个独立的房间,房间里有自己的家具(内存)、门窗(文件描述符)等,每个房间都是独立的,互不干扰。

线程(Thread):

线程是进程中的一个执行单元,多个线程可以共享进程的内存空间和资源。它是CPU调度的最小单位。

特点:

  1. 线程共享进程的内存空间,通信效率高。
  2. 线程的创建和切换开销较小。
  3. 一个进程可以有多个线程,这些线程并发执行。

线程就像是房间里的不同活动(比如一个人在看书,另一个人在听音乐),它们共享房间的资源,但可以同时进行。

操作进程查询以及终止

这里使用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 字段:

  1. pgd 指向页全局目录(Page Global Directory,PGD)的基址,这是分页机制的第一级页表。
  2. 当内核运行该进程时,会将 pgd 的值存入 CR3 控制寄存器,以便处理器能够访问该进程的虚拟内存。

mmap 字段:

  1. mmap 指向一个 vm_area_struct(虚拟内存区域结构)的链表。
  2. 每个 vm_area_struct 描述了进程虚拟地址空间的一个连续区域,例如代码区、数据区、共享库等。
  3. 每个 vm_area_struct 包含以下字段:
  4. vm_start 和 vm_end:表示该虚拟内存区域的起始和结束地址。
  5. vm_prot 和 vm_flags:描述该区域的访问权限和标志。
  6. 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程序,并通过编译和运行验证了其功能。