目录
一、Linux的进程、线程概念
(一)命令控制进程
1、命令查看各进程的编号pid
2、命令终止一个进程pid
二、初识Linux系统的虚拟机内存管理
(一)虚拟机内存管理
(二)与STM32内存管理对比
三、Linux调用函数编程
具体操作:
四、总结
一、Linux的进程、线程概念
Linux中的进程(Process)和线程(Thread)是操作系统进行任务调度的核心概念。
- 进程是资源分配的基本单位,每个进程拥有独立的内存空间、文件描述符等系统资源。进程之间相互隔离,需通过进程间通信(IPC)交换数据。
- 线程是进程内的执行单元,属于同一进程的多个线程共享进程的内存和资源(如代码段、全局变量)。线程独立调度,切换开销小,适合需要并发执行的场景。
在Linux中,线程通过轻量级进程(Lightweight Process, LWP)实现,底层由clone()
系统调用创建。内核使用task_struct
结构统一管理进程和线程,区别在于资源共享程度:线程属于同一线程组(TGID相同),共享内存;而不同进程的TGID不同,资源独立。
(一)命令控制进程
如表为部分控制进程二点命令,但是此处我们着重操作查看进程编号和终止进程
分类 | 命令 | 功能描述 |
---|---|---|
查看进程信息 | ps | 列出当前进程快照 |
top / htop | 动态监控进程资源占用 | |
终止进程 | kill | 通过PID发送信息终止进程 |
killall | 通过进程名批量终止 | |
调整优先级 | nice | 启动新进程时设置优先级(范围:-20~19) |
renice | 修改已运行进程的优先级 | |
后台进程管理 | nohup | 忽略挂断信号,使进程在终端关闭后仍运行 |
jobs/fg/bg | 管理当前终端的后台作业 |
1、命令查看各进程的编号pid
进程编号(PID)是Linux内核为每个运行中的进程分配的唯一数字标识。通过PID,用户可以精准定位目标进程并对其进行管理。以下命令可用于快速获取当前系统中的进程信息:
查看进程此处我用的指令是:
ps -a
则会显示出如下图所示结果:
2、命令终止一个进程pid
当进程出现异常或需要主动释放资源时,可通过发送终止信号(Signal)强制结束进程。Linux提供多种信号类型,默认使用SIGTERM
(15)请求进程正常退出,若未响应可升级至SIGKILL
(9)强制终止。
此处要达到终止一个进程的效果,我们可以先创建一个新的进程,再强制终止,步骤如下:
- 启动测试进程:
sleep 300 & # 后台运行一个休眠300秒的进程
- 查找其PID:
ps -a | grep sleep
- 终止进程
kill 1936
二、初识Linux系统的虚拟机内存管理
(一)虚拟机内存管理
Linux内存管理通过虚拟内存和分页技术为进程提供独立地址空间,由MMU通过页表映射至物理内存或Swap。采用按需分页,访问时触发缺页中断分配内存;内存不足时通过LRU等算法换出非活跃页,由kswapd回收资源,极端时OOM Killer终止进程。写时复制优化进程创建效率。该机制确保进程隔离、内存高效利用及动态扩展能力
(二)与STM32内存管理对比
Linux的虚拟内存管理与STM32的真实物理内存映射在设计理念和应用场景上有显著区别,主要体现在以下几个方面:
-
地址隔离与抽象
Linux通过MMU将虚拟地址映射到物理内存,实现进程间内存隔离;STM32直接操作物理地址,无隔离,需手动管理布局,易因越界操作崩溃。 -
保护与扩展能力
Linux利用页表权限和Swap扩展内存,防御攻击;STM32依赖有限MPU保护关键区,无动态扩展,内存严格受限。 -
实时性与开销
STM32物理内存访问确定、无转换延迟,适合实时控制;Linux可能因页错误或Swap引入延迟,且MMU管理消耗资源。 -
开发模式
Linux自动管理内存,简化开发;STM32需手动分配内存/外设,底层控制强但易出错。
三、Linux调用函数编程
接下来我们将熟悉通过虚拟机在Linux系统中编写c语言程序,熟练调用 fork()、wait()、exec() 等函数。首先我们了解一下上述几个函数的含义:
1、fork( ):创建子进程
-
功能:复制当前进程(父进程),生成一个几乎完全相同的子进程。
-
特点:
-
调用一次,返回两次:父进程返回子进程的 PID(进程标识符),子进程返回
0
。 -
子进程继承父进程的代码、数据段、堆栈和文件描述符等资源。
-
2、wait( ) :回收子进程资源
- 功能:父进程阻塞等待子进程终止,并回收其资源
-
特点:
-
wait(NULL) 等待任意子进程结束;waitpid( ) 可指定等待特定子进程。
-
通过参数获取子进程退出状态。
-
3、exec( )执行新程序
-
功能:加载并运行一个新的可执行程序,替换当前进程的代码和数据。
-
特点:
-
属于函数族,参数传递方式不同。
-
调用成功后,原进程的代码段、数据段等被新程序完全覆盖,但进程 PID 不变。
-
具体操作:
1、打开XTerminal
登录我们老师分配的阿里云服务器Ubuntu系统的账号,进入终端
2、通过命令创建Homework文件夹
mkdir ~/homework && cd ~/homework
3、在homework文件夹中通过vi命令创建C语言文件并写入测试代码
vi example.c
测试代码:
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>int main() {pid_t pid = fork();if (pid == -1) {perror("fork失败");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程执行ls -lprintf("子进程PID: %d\n", getpid());execl("/bin/ls", "ls", "-l", NULL);// 若execl失败,执行以下代码perror("execl失败");exit(EXIT_FAILURE);} else {// 父进程等待子进程printf("父进程,等待子进程PID: %d\n", pid);int status;wait(&status);if (WIFEXITED(status)) {printf("子进程退出码: %d\n", WEXITSTATUS(status));}printf("父进程结束\n");}return 0;
}
代码分析:
(1)使用 fork( ) 创建子进程
pid_t pid = fork();
- 调用fork( )创建一个新进程(子进程)。
(2)错误处理:fork失败
if (pid == -1) {perror("fork失败");exit(EXIT_FAILURE);
}
-
如果fork( ) 失败(返回
-1
),打印错误信息并终止程序。
(3)子进程逻辑
else if (pid == 0) {// 子进程代码printf("子进程PID: %d\n", getpid());execl("/bin/ls", "ls", "-l", NULL);perror("execl失败");exit(EXIT_FAILURE);
}
- 子进程通过getid( )获取自身PID并打印,随后调用execl("/bin/ls","ls","-l",NULL)执行ls -1命令,若路径或参数错误则通过perror( )输出错误信息并调用exit(1)终止自身。
(4)父进程逻辑
else {// 父进程代码printf("父进程,等待子进程PID: %d\n", pid);int status;wait(&status);if (WIFEXITED(status)) {printf("子进程退出码: %d\n", WEXITSTATUS(status));}printf("父进程结束\n");
}
- 父进程通过
wait(&status)
阻塞等待子进程终止,检查其是否正常退出(WIFEXITED
),获取退出码(WEXITSTATUS
),最后打印父进程结束信息。
4、对编辑好的文件保存并退出
在下方的对话框中输入如下指令则可进行对应操作
(1)保存文件:
:w
- :表示进入命令输入状态。
- w 表示写入(write),即保存文件。
(2)退出 vi
:q
- q表示退出(quit)。
- 按 ctrl+enter 退出 vi。
5、编译并运行
回到终端界面输入以下命令即可编译并运行
gcc example.c -o example
./example
结果:
四、总结
在本次学习中,我深入理解了Linux的进程与线程概念,掌握了通过ps、kill等命令查看和终止进程的操作,并通过fork()、wait()、exec()函数实现父子进程协作的编程实践。对比Linux虚拟内存与STM32物理内存管理机制,我认识到前者通过隔离与动态扩展提升安全性与灵活性,而后者以实时性和确定性服务于嵌入式场景。通过编写C程序调用系统函数,我进一步熟悉了多进程资源管理及错误处理流程,巩固了理论与实践的衔接能力,为后续复杂系统开发奠定了基础。