本文链接
Linux中的轻量级进程
- Linux中,轻量级进程可以是进程,也可以是线程。
- 我们所说的线程,在Linux中,
- 轻量级进程之间共享代码段,文件描述符,信号处理,全局变量时;
- 如果不共享,就是我们所说的进程。
- 进程是资源管理的最小单位,
- 线程是程序执行的最小单位。
- OS设计上,从进程演化出线程,最主要的目的就是减小多进程上下文切换开销。
- 最初的进程定义都包含程序、资源及其执行三部分,
- 程序通常指代码,
- 资源在操作系统层面上通常包括内存资源、IO资源、信号处理等部分,
- 执行通常理解为执行上下文,包括对CPU的占用,后来发展为线程。
- 在线程概念出现以前,为了减小进程切换的开销,操作系统设计者逐渐修正进程的概念,逐渐允许将进程所占有的资源从其主体剥离出来,允许某些进程共享一部分资源,例如文件、信号,数据内存,甚至代码,这就发展出轻量进程
- Linux内核在2.0.x就已实现轻量进程,
- 应用程序通过clone()系统调用接口,
- 用不同的参数指定创建轻量进程还是普通进程。
canci
SS寄存器和ESP寄存器
SS寄存器:包含当前程序栈的段
- CS:代码段寄存器
- DS:数据段寄存器;
- SS:堆栈段寄存器。
- CS:存放当前正在运行的程序代码所在段的段基值。
- DS:存放数据段的段基值。
- SS:存放堆栈段的段基值。
- cs代码段地址,联合ip作为cpu指向当前正在执行的那条指令所使用,不能随意修改它。
- ss堆栈度段地址问联合sp定义一个答堆栈,一旦你确定了堆栈地址,ss也不能随便改变了。
- ds数据段地址定义一个数据段。
- 进程切换只发生在内核态。
- 切换前,用户态进程用的所有寄存器都已保存在内核态堆栈(见四章)
- 这也包括ss和esp这对寄存器的内容(存储用户态堆栈指针的地址)
怎么理解linux内核栈?
- linux内核栈是所有进程共享的吗,每个进程都有一个单独的内核栈?
- 从内核模块编程的角度看(不涉及用户态进程),
- 内核栈该怎么理解?
- 和用户进程进行系统调用使用的栈空间有什么不同?
- linux内核栈空间只有4KB或8KB,linux内核编程中的堆(heap)和栈(stack)有什么区别?
答案1
- 1、每个进程被创时,
- 在生成task_struct的同时,生成两个栈,
- 用户栈,位于用户地址空间;
- 内核栈,位于内核空间。
- 当进程在用户地址空间中执行的时候,使用的是用户栈,CPU堆栈指针寄存器中存的是用户栈的地址;
- 当进程在内核空间执行时,CPU堆栈指针寄存器中放的是内核栈的地址。
- 2、当位于用户空间的进程系统调用时,它会陷入内核,让内核代其执行。
- 进程用户栈的地址会被存进内核栈中,CPU堆栈指针寄存器中的内容也会变为内核栈的地址。
- 系统调用执行完,进程从内核栈找到用户栈地址,继续在用户空间中执行,此时CPU堆栈指针寄存器就变为了用户栈的地址。
- 3、我的理解
- 一个进程就对应着一个内核栈,系统一般默认可同时存在的进程数目是32768,如果按每个内核栈空间4KB,32768个进程就已经占用了128MB内存,且单个内存栈过大也容易造成内存空间浪费的结果。
答案2
-
- 读Linux内核以及相关的资料时,清醒认识到它说的是内核态还是用户态的东西。
- 一个用户态进程/线程在内核中都是用一个task_struct的描述,这个有点类似设计模式里面的桥接模式(handle-body), 用户态看到的进程PID,线程TID都是handle, task_struct是body。
- C语言书里面讲的堆、栈大部分都是用户态的概念,用户态的堆、栈对应用户进程虚拟地址空间里的一个区域,栈向下增长,堆用malloc分配,向上增长。
- 用户空间的堆栈,在task_struct->mm->vm_area里面描述,都是属于进程虚拟地址空间的一个区域。
- 而内核态的栈在tsak_struct->stack里描述,其底部是thread_info对象,thread_info可以用来快速获取task_struct对象。整个stack区域一般只有一个内存页(可配置),32位机器也就是4KB。
- 一个进程的内核栈,也是进程私有的,只是在task_struct->stack里面获取。
- 内核态没有进程堆的概念,用kmalloc()分配内存,实际上是Linux内核统一管理的,一般用slab分配器,也就是一个内存缓存池,管理所有可以kmalloc()分配的内存。所以从原理上看,在Linux内核态,kmalloc分配的所有的内存,都是可以被所有运行在Linux内核态的task访问到的。
cacni
用户态/内核态、用户栈/内核栈
用户态和内核态
- 内核态和用户态是OS两种运行级别,
- 区分不同程序的不同权利
- 内核态也称特权态。
- 用户态就是非特权态,访问的而资源受到限制
- 程序运行在特权态,该程序就可以访问计算机的任何资源
- 运行在用户态,其资源需求将受到各种限制。
- 要访问操作系统的内核数据结构,如进程表,则需要在特选态下才能办到。
- 如果要访问用户程序里的数据,在用户态即可。
用户栈和内核栈
- 内核创建进程时,
- 创建task_struct的同时,会为进程创建相应的堆栈。
- 每一个进程都一个用户栈,存在于用户空间;一个内核栈,存在于内核空间。
- 当进程在用户空间运行时,CPU堆栈指针寄存器里面的内容都是用户栈地址,用用户栈
- 当进程在内核空间时,CPU堆栈指针寄存器里面的内容是内核栈空间地址,用内核栈
- 进程因为中断或者系统调用陷入到内核态时,
- 进程所用的堆栈也要从用户栈转到内核栈。
- 进程陷入到内核态后,先把用户态堆栈的地址保存在内核栈中,
- 然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈向内核栈的转换;
- 当进程从内核态恢复到用户态之后时,在内核态之后的最后将保存在内核栈里面的用户栈的地址恢复到堆栈指针寄存器即可。
- 这就实现用户栈和内核栈的互转。
- 陷入内核的时候,如何知道内核栈地址?
- 关键在进程从用户态转到内核态的时候,进程的内核栈总是空。
- 因为当进程在用户态运行时,使用的用户栈,当进程陷入到内核态时,内核保存进程在内核态运行的相关信息,但是一旦进程返回到用户态后,内核栈中保存的信息无效,会全部恢复,因此每次进程从用户态陷入内核的时候得到的内核栈都是空的。
- 所以进程陷入内核时,直接把内核栈的栈顶地址给堆栈指针寄存器就可