Linux内存
Linux --内存管理
内存分配和管理: 标识某一段内存是否空闲
地址转换:外存虚拟映射到内存
内存扩充:
内存的共享与保护: 一部分空间共享,一部分空间保护
内存映射:进程使用的内存可以映射到物理内存上,相同的进程使用内存块可以映射到不同的物理内存中
ARM Linux 虚拟空间存储分布
虚拟空间被分为内核空间(1G)和用户空间(3G)
用户可以通过API操作内核空间的地址
系统空间的一部分会映射到IO设备中,包括寄存器和外部存储器等。
虚拟存储器分页机制
将整片内存空间分为许多块,每块被称为页,当系统需要调度内存时采用页分配(4K)的方式分配内存。
物理内存管理(页管理)
为了尽量不分配不连续的内存地址,采用伙伴算法。
将整块物理内存分为1,2,4……512个页(4K)总共10种类型,每种类型用结构体保存下来,需要多少大小的内存就到指定类型的内存结构体中申请。
(一个学校的教室分为可以容纳1,2,4……512个班级的大小,而且这些同类型的班级的地址是相邻的,让班级根据需要去申请)
缺点:因为内存空间固定,所以不可避免会造成页框中一些内存的浪费
slab算法 分配的时候还是1页,将每一页(4K)又细分为100份分配给小对象使用。
Linux进程控制
进程- 资源分配和系统调度的基本单元。
进程是一段执行的程序但不单单只有程序,它还保存有进程执行时的内部资源
进程在内核的存在形式是进程控制块(PCB),一个结构体保存在双向链表中
进程分类
用户进程: 终端进程,是用户通过终端启动的进程
守护进程: 在系统引导时就启动的进程,是后台的服务进程,它在终端关闭时不会自动关闭,重启终端时仍然会运行
进程的创建
内核通过进程标识符PID来区分每一个进程,pid值保存在各自的进程描述符中。
进程创建步骤
1.初始化PCB
2.分配进程ID,优先级,CPU时间片
3.为进程分配内存空间
4.加载任务到内存空间,进程代码复制到内核空间
5.设置进程执行状态为就绪态
关于运行队列与等待队列
实际上是一个双向链表
正在运行的进程放在运行队列,在休眠的进程放在等待队列
Linux中进程的各种状态.
运行态:可以被进程调度器调度执行的进程称为运行态进程(Linux中统一称为运行态)
可中断:进程因等待事件发生而挂起而处于等待队列中时为可中断
不可中断态:不可被系统信号所中断的信号
僵尸态:进程在退出之后,进程控制块控制的所有内存资源被释放,只留下一个空的task struct结构体。父进程调用wait系列系统调度时就会将这个空的子进程结构体释放掉。如果父进程先退出了,它的子进程会被全部托管给其它进程。
停止态:进程暂停下来等待其它进程操作它
销毁状态:进程在退出过程中彻底释放,不会产生僵尸进程。
销毁态和僵尸态的区别是什么?
父进程调用wait函数后则会将僵尸态进程转变为销毁态
进程调度
PCB中有一个时间片数count,内核滴答定时每次定时中断都会减少该片数,当时间片用完时进程就会被挂起,CPU执行权给新进程。
进程在时间片结束前就结束CPU也会立即切换
多任务操作系统调度模式 :抢占式多任务和非抢占式多任务
Linux中进程调度命令
'ps' :是Linux 中最基础的浏览系统中的进程的命令。能列出系统中运行的进程,包括进程号、命令、CPU使用量、内存使用量等
ps -aux - 显示进程信息,包括无终端的(x)和针对用户(u)的进程:如USER, PID, %CPU, %MEM等
ps+aux之后的结果
top :‘top’是一个更加有用的命令,可以监视系统中不同的进程所使用的资源。它提供实时的系统状态信息。显示进程的数据包括PID、进程属主、优先级、%CPU、%memory等。可以使用这些显示指示出资源使用量
nice :用户可以设置和改变进程的优先级。提高一个进程的优先级,内核会分配更多CPU时间片给这个进程。进程优先级值的范围从-20到19。值越低,优先级越高。
nice <优先值> <进程名> - 通过给定的优先值启动一个程序
:设置top的优先级是-3
renice :renice命令类似nice命令。使用这个命令可以改变正在运行的进程优先值。注意,用户只能改变属于他们自己的进程的优先值
renice -n -p - 改变指定进程的优先值
kill :这个命令用于发送信号来结束进程。如果一个进程没有响应杀死命令,这也许就需要强制杀死,使用-9参数来执行。注意,使用强制杀死的时候一定要小心
kill <pid> :杀死指定PID下的进程
kill -9 <pid> :强制杀死该PID下的进程
bg :有时,命令需要很长的时间才能执行完成。对于这种情况,我们使用‘bg’命令可以将任务放在后台执行,而用‘fg’可以调到前台来使用。
我们可以通过‘&’在后台启动一个程序:
find . -name *iso > /tmp/res.txt &
虚拟内存
Linux的swap分区是硬盘专门为虚拟存储空间预留的空间
虚拟内存机制我不太了解,去看视频
文件锁
我对这东西没什么概念,看视频去
作用:防止多个进程同时操作一个文件
进程中,关闭一个描述符时,该进程通过描述符引用的文件上的任何锁都被释放
进程终止时,它建立的锁全部释放
fork产生的子进程不继承父进程的锁,子进程调用fcnt才能获取自己的锁
调用exec,新程序可以继续执行原程序文件锁。
LINUX如何创建进程
1系统调用system()
可以通过此命令去调用系统进程
system(“ls -l”)
2exec系列函数调用
这种调用是在原函数的基础上产生一个新的进程,进程ID之类的不会变化。相当于进程表面没有变化但内在变化了,进程“变身”了。
暂时也不知道这个函数集的应用场景
函数集合
#include <unistd.h>
extern char **environ;
1. int execl(const char *path, const char *arg, ...);
2. int execlp(const char *file, const char *arg, ...);
3. int execle(const char *path, const char *arg,..., char * const envp[]);
4. int execv(const char *path, char *const argv[]);
5. int execvp(const char *file, char *const argv[]);
6. int execvpe(const char *file, char *const argv[],char *const envp[]);
函数作用:
根据指定的文件名找到可执行文件来取代当前调用进程的内容。
3fork分叉函数调用
获取进程ID的API
包含头文件
#include <sys/types.h>
#include <unistd.h>
1获取当前进程pid
pid_t getpid()
2获取父进程pid
pid_t getppid()
创建子进程API
包含头文件:
#include <unistd.h>
#include <fcntl.h>
pid_t fork()
失败返回-1,成功返回进程ID
fork创建进程后,函数在子进程返回0,在父进程中返回子进程的pid值。一般会利用该特效来区分父子进程。
父子进程不共享存储空间和资源但代码段是共享的。
pid_t vfork()
函数在子进程返回0,在父进程中返回子进程的pid值。
和fork()类似但有区别
fork()不会对父子进程的执行次序有限制,vfork调用时子进程会先调用,父进程会挂起。它会挂起父进程直到它退出或者执行exec函数族
等待进程结束API
等待任何一个子进程结束,阻塞
pid_t wait()
等待某一个进程结束,可以选择是否阻塞
wait()
函数的功能有三个:
- 阻塞等待子进程退出
- 回收子进程的残留资源,包括PCB
- 获取子进程结束状态或异常退出原因,阻塞式回收任一(谁先结束就先回收谁)子进程退出资源
-
-
status由自己定义,用来接收子进程返回的返回值/错误码
wait函数中的宏函数
进程终止
有很多种方式,下面列举几个
exit()函数 终止进程,返回要给父进程的值
exit()和return的区别
如果main()在一个递归程序中,exit()仍然会终止程序;
但return将控制权移交给递归的前一级,直到最初的那一级,此时return才会终止程序。//exit更加直接
即使在除main()之外的函数中调用exit(),它也将终止程序。
return();是某个函数的结束,并返回结果。
return是函数级别的操作,exit是进程级别的操作
return是函数的退出(返回);exit是进程的退出。
-
非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。
abort()函数,异常终止进程
void abort(void);
该函数没有任何参数,调用该函数会立即终止程序的运行,并生成一个异常信号,通知操作系统程序已经异常终止。在程序终止之前,abort()函数会执行一些清理工作,例如关闭文件、释放内存等。
raise函数
raise 是一个C语言标准库函数,它的作用是给当前进程发送信号。它属于信号处理库(signal.h),允许程序员通过代码控制信号的发送和处理。
接到一个信号并终止,如signkill信号给信号会关闭进程。
如raise(SIGKILL)
raise 函数原型如下:
#include <signal.h> //使用这个函数需导入此头文件
int raise(int sig);
返回值:
- 如果函数成功发送信号,返回0。
- 如果出现错误,返回非0值。
atexit函数
int atexit(函数指针)
进程在结束前会执行函数指针指向的函数,这些函数会被exit自动调用,称为终止处理函数。相当于析构函数……
一、atexit函数
atexit函数是一个特殊的函数,它是在正常程序退出时调用的函数,我们把他叫为登记函数(函数原型:int atexit (void (*)(void)))
一个进程可以登记32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数
atexit函数可以登记这些函数。exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律