GDT,LDT,GDTR,LDTR 前言 全局描述符表GDT 局部描述符表LDT 中断描述符表IDT 段选择子 任务寄存器TR 实例 1:访问GDT 2:访问LDT ---- 前言 所谓工作模式,是指CPU...我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。...LDT中是不会起作用的....选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。...③以这个位置索引在GDT中得到LDT段描述符从而得到LDT段基址。 ④用段选择器高13位位置索引值从LDT段中得到段描述符。
我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图 ? 局部描述符表LDT LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。...如上图,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。 1....通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为...2:访问LDT ? 段描述符在LDT中 当TI=1时表示段描述符在LDT中,如上图所示: ①还是先从GDTR寄存器中获得GDT基址。...②从LDTR寄存器中获取LDT所在段的位置索引(LDTR高13位)。 ③以这个位置索引在GDT中得到LDT段描述符从而得到LDT段基址。 ④用段选择器高13位位置索引值从LDT段中得到段描述符。
内存中只能有一个 GDT,但却可以存在多个 LDT,如上图所示,每个 LDT 作为 GDT 中一个描述符描述的内存段。 通常,一个 LDT 用于划分一个特定任务执行过程中需要使用的内存分段。 3....; LDT [SECTION .ldt] ALIGN 32 LABEL_LDT: ; 段基址 段界限 属性 LABEL_LDT_DESC_CODEA...LABEL_LDT_DESC_CODEA - LABEL_LDT + 4 ; 置位 TI 位,表示是 LDT 4.3....初始化 GDT 中指向 LDT 的描述符的段基址及 LDT 代码描述符段基址 ; 初始化 LDT 在 GDT 中的描述符 xor eax, eax mov ax, ds shl eax, 4 add eax..., LABEL_LDT mov word [LABEL_DESC_LDT + 2], ax shr eax, 16 mov byte [LABEL_DESC_LDT + 4], al mov byte
除了全局描述符表(GDT)外,X86还提供了另一种数据结构叫局部描述符表(LDT),局部描述符表的结构跟全局描述符表一模一样。不同的是,全局描述符表只能存在一份,而局部描述符表可以是每个进程一份。...//change here add stack record int cons_stack; //change here struct SEGMENT_DESCRIPTOR ldt...[2]; }; 最末尾的ldt就是进程对应的局部描述符表,显然它只含有两个描述符,目前我们的进程只含有数据段和代码段,因此两个描述符足够了。...语句: set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15, (int)taskctl->tasks0[i].ldt, AR_LDT); 它的作用是将局部描述符表的起始地址放置到全局描述符表对应的描述符中...,AR_LDT的值是0x0082,用来表示当前描述符是专门指向一个局部描述符表的描述符。
学习过操作系统的都了解一个多任务的操作系统中是通过时间轮盘算法来对程序进行调度,使得CPU在不同周期执行着不同的指令,通过汇编代码可以看到每个程序都有着自己的描述符号即LDT局部描述符表来组建对变量和代码之间的符号描述
在 LDT 中,存放着当前应用程序自己的段描述符信息,例如:代码段、数据段、栈段。...在 Linux 应用层,我们会严格的区分进程、线程,但是在系统的底层,这样的区分界限已经比较模糊了,用任务 task 来称呼更通用些。...,映射的都是操作系统的内存空间,按照 Linux 中的做法,3G ~ 4G 空间被操作系统使用。...看一下 Linux 2.6 内核代码中的结构体:struct task_struct{ ... },就知道 TCB 有多复杂了,有些书籍上也称之为 PCB(Process Control Block,进程控制块...当然,Linux 系统中的处理过程更为复杂,它把每一个任务按照优先级放在不同的等待队列中,然后利用哈系桶算法来查找任务。 ------ End ------
本文以linux0.11版本为基础,分析进程的内存布局,现代版本已经发生比较大的变化,都是很多原理都是类似的。...选择子的偏移是5<<3,5乘以8,等于40,即从GDT的偏移为40开始算,第一个进程的n是0,ldt是40 #define _LDT(n) ((((unsigned long) n)ldt项中取得进程的信息。...= new_code_base = nr * 0x4000000; p->start_code = new_code_base; // 设置线性地址到ldt的描述符中 set_base(p->ldt[...(gdt+(nrtss)); set_ldt_desc(gdt+(nrldt)); ?
从2.2 版开始,Linux 让所有的进程(或叫任务)都使用相同的逻辑地址空间,因此就 没有必要使用局部描述符表LDT。...但内核中也用到LDT,那只是在VM86 模式中运行Wine 时, 即在Linux 上模拟运行Windows 软件或DOS 软件的程序时才使用。...因为没有使用LDT,因此,TI=0,并把这4 个段都放在GDT 中, index 就是某个段在GDT 表中的下标。...按Intel 的规定,每个进程有一个任务状态段(TSS)和局部描述符表LDT,但Linux 也 没有完全遵循Intel 的设计思路。...如前所述,Linux 的进程没有使用LDT,而对TSS 的使用也 非常有限,每个CPU 仅使用一个TSS。
这一篇大致说一下进程的创建,有兴趣的可以参考之前的一些文章或者直接上代码https://github.com/theanarkh/read-linux-0.11。 系统有一个GDT表。...的索引,切换任务的时候, 这个索引会被加载到ldt寄存器,cpu会自动根据ldt的值,把 GDT中相应位置的段描述符加载到ldt寄存器(共16+32+16位) *...tss信息中的ldt索引首先从gdt找到进程ldt 结构体数据的首地址,然后根据当前段的属性,比如代码段, 则从cs中取得选择子,系统从ldt表中取得进程线性空间 的首地址、限长...<<3)) _LDT是计算出进程ldt在GDT表的索引。...然后找到tss中的ldt选择子,把ldt选择子加载到ldtr寄存器,然后根据ldt选择子到gdt表中可以找到对应的ldt描述符。根据cs:ip的值。cs寄存器里存的是代码段的选择子。是0x17。
在保护模式下,段寄存器保存的是段选择子,当进程被系统选中执行时,会把tss和ldt等信息加载到寄存器中,tss是保存进程上下文的,ldt是保存进程代码和数据段的首地址偏移以及权限等信息的。...的索引,切换任务的时候, 这个索引会被加载到ldt寄存器,cpu会自动根据ldt的值,把 GDT中相应位置的段描述符加载到ldt寄存器(共16+32+16位) *...选择子的偏移是5<<3,5乘以8,等于40,即从GDT的偏移为40开始算,第一个进程的n是0,ldt是40 #define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY...,base) _set_base( ((char *)&(ldt)) , base ) #define set_limit(ldt,limit) _set_limit( ((char *)&(ldt))...这就是linux0.11版本中进程地址管理的实现。下面是fork后的结构图。 ?
Linux 在初始化的过程中会进行 0 号进程的创建,fork main.c sched.c—>sched_init—>gdt linux系统级别 GDT sched_init(...= _LDT(nr); p->tss.trace_bitmap = 0x80000000; 如果当前进程使用了协处理器,那就设置当前创建进程的协处理器 if (last_task_used_math...== current) __asm__("clts ; fnsave %0"::"m" (p->tss.i387)); 进行老进程向新进程代码段、数据段(LDT)的拷贝 int copy_mem(int...(gdt+(nrldt)); 给程序的状态标志为可运行状态 p->state = TASK_RUNNING; /* do this last, just...内核完全注释:基于0.11内核(修正版V3.0).pdf P281~P302 链接:Linux内核完全注释:基于0.11内核(修正版V3.0).pdf 提取码:ygz8 四、进程的退出 linux
3.局部描述符表(LDT) 局部描述符表LDT(Local Descriptor Table),包含了与一个给定任务有关的描述符,每个任务各自有一个的LDT。...每一个任务的局部描述符表LDT 本身也用一个描述符来表示,称为LDT 描述符,它包含了有关局部描述符表的信息,被放在全局描述符表GDT 中,使用LDTR进行索引。...5、linux中的段机制 从2.2 版开始,Linux 让所有的进程(或叫任务)都使用相同的逻辑地址空间,因此就没有必要使用局部描述符表LDT。...按Intel 的规定,每个进程有一个任务状态段(TSS)和局部描述符表LDT,但Linux 也没有完全遵循Intel 的设计思路。...如前所述,Linux 的进程没有使用LDT,而对TSS 的使用也非常有限,每个CPU 仅使用一个TSS。
进程2的分析同理 ---- 因为一个CPU同时只能处理一个进程,那么当CPU处理当前进程1时,需要知道当前进程1对应的LDT表在何处,因此就需要一个寄存器来记录当前正在被处理的进程的LDT表位置,该寄存器为...---- 但是因为每个进程都需要一个LDT表,进程数量又是在动态变化的,因此为了统计和管理LDT表,就利用GDT表来存放所有存在的LDT表,GDT表整个系统只有一份: GDT,LDT,GDTR,LDTR...,那么LDTR就指向了当前进程的LDT表。...---- 可以看出来,每个进程关联的LDT表,其实就是我们之前说的,用来完成进程间内存隔离用到的映射表,因此,再进行进程切换时,需要切换LDT表,即处理器会把新任务LDT的段选择符和段描述符自动地加载进...不需要内存紧缩,因为内存分配最小单元为页 最大浪费内存为4k,例如: 有个段需要3页多一丢丢的内存,此时还是需要分配给他四页内存,相当于浪费了接近一页内存,而一页内存在linux 0.11中的大小为4k
指向LDT中描述符的选择子的T1 位必须置1,在运用它时,需要先用lldt指令加载ldtr,其操作数是GDT中用来描述LDT的描述符。LDT相当于是GDT的二级目录,增加了一个层次。...; END of [SECTION .s32] ; LDT [SECTION .ldt] LABEL_LDT: ; 段基址...equ $ - LABEL_LDT ; LDT 选择子 SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL ; 选择子是通过地址相减得到的...mov [gs:edi], ax CodeALen equ $ - LABEL_CODE_A ; END of [SECTION .la] ---- 特权级概述–保护的意义 具体可以看《linux...Linux内核中并没有用到调用门。 门调用过程: ? 通过调用门进行控制转移的特权级检查: ?
作 者:道哥,10+年的嵌入式开发老兵,专注于:C/C++、嵌入式、Linux。...关注下方公众号,回复【书籍】,获取 Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章( PDF 格式)。...当请求访问一个段时(不论是数据段,还是代码段),处理器在GDT或者LDT中找到段描述符之后,就会把CPL、RPL与描述符中的 DPL 进行比较。...类似于 Linux 中的 sudo 指令 如果一条指令需要root权限,我们可以使用su -指令,把身份转换到 root,然后再去执行。 此时所有的身份、环境变量等信息,都是root用户的。...在Linux系统中,只用了0 和 3这两个特权级,因此每一个用户程序只需要提前准备好0特权级下使用的栈就可以了。
大家好,又见面了,我是全栈君 Linux 0.12 内核管理存储器 其分段,用分段的机制把进程间的虚拟地址分隔开。 每一个进程都有一张段表LDT。整个系统有一张GDT表。...(32位系统一个虚拟段的最大长度,理论上为4G) Linux 0.12内核人工定义的最大任务数为64个。...LDT段表中的段是一个任务自己的段(代码段、数据段等) GDT段表中的段是系统中全部任务共同拥有的(操作系统代码段等),还有系统中全部任务的LDT段表段(任务的LDT段表存储在一个段中) 当某任务在执行时...LDT表存放在LDT类型的系统段中。此时GDT必须含有LDT的段描写叙述符。 【内存管理寄存器】 处理器提供了4个内存管理寄存器(GDTR、LDTR、IDTR、TR)。...2、局部描写叙述符表寄存器LDTR LDT表是当前进程的段的段表。 包括LDT表的段必须在GDT表中有一个段描写叙述符项。
ldt4 = ld2.minusMonths(2); System.out.println(ldt4); System.out.println(ldt.getYear...()); System.out.println(ldt.getMonthValue()); System.out.println(ldt.getDayOfMonth())...(); System.out.println(ldt); LocalDateTime ldt2 = ldt.withDayOfMonth(10); System.out.println...(ldt2); LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println...(ldt3); //自定义:下一个工作日 LocalDateTime ldt5 = ldt.with((l) -> { LocalDateTime
LocalDate ld3 = LocalDate.now(); LocalTime lt = LocalTime.of(12,12,0); LocalDateTime ldt...= ld3.atTime(lt); System.out.println("ldt="+ldt); LocalDateTime ldt2 = ld3.atTime(12,20,20...); System.out.println("ldt2="+ldt2); //初始化LocalDateTime LocalDateTime ldt3 =...LocalDateTime.of(2023,12,12,12,20,20); System.out.println("ldt3="+ldt3); output: lt2=2023-08...-14 ldt=2023-08-14T12:12 ldt2=2023-08-14T12:20:20 ldt3=2023-12-12T12:20:20
记录管理着文件的信息,当可执行文件被加载到内存当作进程执行后,也有类似的数据结构来记录管理进程的执行情况,这个数据结构就是 $PCB(Process\ Control\ Block)$,进程控制块,$Linux...$LDT$,$TSS$ 的属性字段中就包括 $LDT$,诸位可能对 $GDT$(全局描述符表) 比较熟悉,这在前文也提到过很多次,那 $LDT$ 是什么呢?...每个 $LDT$ 都需要在 $GDT$ 中注册,也就是说对于每个 $LDT$,$GDT$ 中都要有相应的 $LDT$ 描述符,这也证明了 $GDT$ 才是老大哥。...上述关于 $LDT$ 说了那么多,但实际情况中根本用不上 $LDT$,想想段描述符具体有什么用处?...准确点来说应该是下图这样: 图中有鲜明显色的区域都是实际映射到了物理内存的区域,其中蓝色部分才是 $malloc\ free$ 作用的区域,在 $Linux$ 中程序在内存中的映像有个属性叫做 $break
领取专属 10元无门槛券
手把手带您无忧上云