书接上回,上回书我们说到,time_init 要领通过与 CMOS 端口进行读写交互,获取到了年月日时辰秒等数据,并通过这些策划出了开机期间 startup_time 变量,是从 1970 年 1 月 1 日 0 时起到开机其时经过的秒数。 我们络续往下看,大名鼎鼎的程度鼎新开动化,shed_init。 void 威尼斯人体育main(void) { ... mem_init(main_memory_start,memory_end); trap_init(); blk_dev_init(); chr_dev_init(); tty_init(); time_init(); sched_init(); buffer_init(buffer_memory_end); hd_init(); floppy_init(); sti(); move_to_user_mode(); if (!fork()) {init();} for(;;) pause(); } 这要领可了不得,因为它便是多程度的基石! 终于来到了重生的时刻,是不是很慷慨?不外先别慷慨,这里仅仅程度鼎新的开动化,也便是为程度鼎新所需要用到的数据结构作念个准备,信得过的程度鼎新还需要鼎新算法、时钟中断等机制的和洽。 天然,关于清爽操作系统,经由和数据结构最为贫瘠了,而这一段行为通盘经由的最先,以及开采数据结构的所在,就显得格外贫瘠了。 我们干涉这个要领,少许点往后看。 ![]() void sched_init(void) { set_tss_desc(gdt+4, &(init_task.task.tss)); set_ldt_desc(gdt+5, &(init_task.task.ldt)); ... } 两行代码开动化了下 TSS 和 LDT。 皇冠体育进不去先别急问这俩结构是啥。还铭刻之前讲的全局边幅符表 gdt 么?它在内存的这个位置,何况被成立成了这个形势。 忘了的看一下等八回 | 烦死了又要再行成立一遍 idt 和 gdt,这就阐扬之前看似没用的细节有多贫瘠了,公共一定要有耐性。 说回这两行代码,其实便是往后又加了两项,分袂是 TSS 和 LDT。 好,那再说说这俩结构是干嘛的,不外本篇先浅薄清爽,背面会小心讲到。 TSS 叫任务景色段,便是保存和复原程度的高下文的,所谓高下文,其实便是各个寄存器的信息费事,这么程度切换的时候,才略作念到保存和复原高下文,络续履行。 由它的数据结构你应该不错看出点兴致。 皇冠盘口瀚希体育struct tss_struct{ long back_link; long esp0; long ss0; long esp1; long ss1; long esp2; long ss2; long cr3; long eip; long eflags; long eax, ecx, edx, ebx; long esp; long ebp; long esi; long edi; long es; long cs; long ss; long ds; long fs; long gs; long ldt; long trace_bitmap; struct i387_struct i387; }; 而 LDT 叫局部边幅符表,是与 GDT 全局边幅符表相对应的,内核态的代码用 GDT 里的数据段和代码段,而用户程度的代码用每个用户程度我方的 LDT 里得数据段和代码段。 先无论它,我这里放一张超纲的图,你先找找嗅觉。 我们接着往下看。 struct desc_struct { unsigned long a,b; } struct task_struct * task[64] = {&(init_task.task), }; void sched_init(void) { ... int i; struct desc_struct * p; p = gdt+6; for(i=1;i<64;i++) { task[i] = NULL; p->a=p->b=0; p++; p->a=p->b=0; p++; } ... }
这段代码有个轮回,干了两件事。 一个是给一个长度为 64,结构为 task_struct 的数组 task 附上开动值。 据悉,“三亚—首尔”航线每周执行两班。进港航班号为TW621,每周三、周六从首尔起飞。出港航班号为TW622,每周四、周日从三亚起飞。(陈璐 制作 李宇凡 视频来源 三亚凤凰国际机场) 据悉,“三亚—首尔”航线每周执行两班。进港航班号为TW621,每周三、周六从首尔起飞。出港航班号为TW622,每周四、周日从三亚起飞。(陈璐 制作 李宇凡 视频来源 三亚凤凰国际机场) 这个 task_struct 结构便是代表每一个程度的信息,这关联词个至极至极贫瘠的结构了,把它放在心里。 struct task_struct { /* these are hardcoded - don't touch */ long state; /* -1 unrunnable, 0 runnable, >0 stopped */ long counter; long priority; long signal; struct sigaction sigaction[32]; long blocked; /* bitmap of masked signals */ /* various fields */ int exit_code; unsigned long start_code,end_code,end_data,brk,start_stack; long pid,father,pgrp,session,leader; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; long alarm; long utime,stime,cutime,cstime,start_time; unsigned short used_math; /* file system info */ int tty; /* -1 if no tty, so it must be signed */ unsigned short umask; struct m_inode * pwd; struct m_inode * root; struct m_inode * executable; unsigned long close_on_exec; struct file * filp[NR_OPEN]; /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ struct desc_struct ldt[3]; /* tss for this task */ struct tss_struct tss; }; 这个轮回作念的另一件事,是给 gdt 剩下的位置填充上 0,也便是把剩下留给 TSS 和 LDT 的边幅符齐先附上空值。 zh皇冠体育接口往后瞻望一下的话,便是以后每创建一个新程度,就会在背面添加一组 TSS 和 LDT 暗示这个程度的任务景色段以及局部边幅符表信息。 皇冠hga还铭刻刚刚的超纲图吧,异日通盘内存的策划便是这么的,不外你先无须清爽得很细。 那为什么一脱手就先有了一组 TSS 和 LDT 呢?当今也没创建程度呀。错了,当今天然我们还莫得开采上路度鼎新的机制,但我们正在运行的代码便是会行为异日的一个程度的教唆流。 也便是当异日程度鼎新机制一开采起来,正在履行的代码就会化身成为程度 0 的代码。是以我们需要提前把这些异日会行为程度 0 的信息写好。 若是你合计很狐疑,别急,等背面通盘程度鼎新机制开采起来,何况让你亲眼看到程度 0 以及程度 1 的创建,以及它们背面因为程度鼎新机制而切换,你就昭着这一切的兴致了。 好,收回首,开动化了一组 TSS 和 LDT 后,再往下看两行。 #define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))) #define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n))) void sched_init(void) { ... ltr(0); lldt(0); ... } 这又波及到之前的常识咯。 还铭刻 lidt 和 lgdt 教唆么?一个是给 idtr 寄存器赋值,皇冠体育官网以告诉 CPU 中断边幅符表 idt 在内存的位置;一个是给 gdtr 寄存器赋值,以告诉 CPU 全局边幅符表 gdt 在内存的位置。 那这两行和刚刚的相通,ltr 是给 tr 寄存器赋值,以告诉 CPU 任务景色段 TSS 在内存的位置;lldt 一个是给 ldt 寄存器赋值,以告诉 CPU 局部边幅符 LDT 在内存的位置。 这么,CPU 之后就能通过 tr 寄存器找到刻下景度的任务景色段信息,也便是高下文信息,以及通过 ldt 寄存器找到刻下景度在用的局部边幅符表信息。 2023年欧洲杯将于5月5日至6月3日在英国举行。各大球队已经开始了紧张的备战工作,球迷们也在为他们的偶像和心爱的球队打气。这场盛宴的到来,必将掀起足球运动的一股新浪潮。我们络续看。 void sched_init(void) { ... outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH >> 8 , 0x40); /* MSB */ set_intr_gate(0x20,&timer_interrupt); outb(inb_p(0x21)&~0x01,0x21); set_system_gate(0x80,&system_call); ... } 四行端口读写代码,两行成立中断代码。 端口读写我们也曾很持重了,便是 CPU 与外设交互的一种花样,之前讲硬盘读写以及 CMOS 读写时,也曾战役过了。 而此次交互的外设是一个可编程定时器的芯片,这四行代码就开启了这个定时器,之后这个定时器变会捏续的、以一定频率的向 CPU 发出中断信号。 而这段代码中成立的两个中断,第一个便是时钟中断,中断号为 0x20,中断处理时事为 timer_interrupt。那么每次定时器向 CPU 发出中断后,便会履行这个函数。 这个定时器的触发,以实时钟中断函数的成立,是操作系统主导程度鼎新的一个要道!莫得他们这么的外部信号不断触发中断,操作系统就莫得目标行为程度科罚的主东谈主,通过强制的本事收回程度的 CPU 履行权限。 第二个成立的中断叫系统调用 system_call,中断号是 0x80,这个中断又是个相等相等相等相等相等相等相等贫瘠的中断,统共效户态时事思要调用内核提供的要领,齐需要基于这个系统调用来进行。 比如 Java 时事员写一个 read,底层会履行汇编教唆 int 0x80,这就会触发系统调用这个中断,最终调用到 Linux 里的 sys_read 要领。 这个过程之后会重心叙述,当今只需要知谈,在这个所在,悄悄把这个极为贫瘠的中断,成立好了。 博彩平台游戏是以你看这一章的内容,悄悄成立了影响程度和影响用户时事调用系统要领的两个分量级中断处理函数,不浅薄呀~ 到面前为止,中断也曾成立了不少了,我们当今望望所成立好的中断有哪些。 中断号 中断处理函数 0 ~ 0x10 trap_init 里成立的一堆 0x20 timer_interrupt 0x21 keyboard_interrupt 0x80 system_call 其中 0-0x10 这 17 个中断是 trap_init 里开动化成立的,是一些基本的中断,比如除零额外等。这个在 第14回 中断开动化 trap_init 有讲到。 皇冠客服飞机:@seo3687之后,在罢休台开动化 con_init 里,我们又成立了 0x21 键盘中断,这么按下键盘就有反馈了。这个在 第16回 罢休台开动化 tty_init 有讲到。 当今,我们又成立了 0x20 时钟中断,何况开启定时器。临了又悄悄成立了一个极为贫瘠的 0x80 系统调用中断。 找到些嗅觉没,有莫得越来越发现,操作系统有点靠中断驱动的兴致,各个模块不断开动化多样中断处理函数,何况开启指定的外设开关,让操作系统我方渐渐“活”了起来,渐渐通过中断勤劳于多样事情中,无法自拔。 恭喜你,我们也曾渐渐在接近操作系统的执行了。 思考追忆一下我们今天干了什么,就三件事。 第一,我们往全局边幅符表写了两个结构,TSS 和 LDT,行为异日程度 0 的任务景色段和局部边幅符表信息。 第二,我们开动化了一个结构为 task_struct 的数组,异日这里会存放统共程度的信息,何况我们给数组的第一个位置附上了 init_task.init 这个具体值,亦然行为异日程度 0 的信息。 第三,成立了时钟中断 0x20 和系统调用 0x80,一个行为程度鼎新的最先,一个行为用户时事调用操作系统功能的桥梁,相等之贫瘠。 背面,我们将会渐渐看到,这些贫瘠的事情,是怎么细巧且精妙地聚拢在一谈,弘扬特别妙的作用。 欲知后事怎么,且听下回理会。 本文转载自微信公众号「低并发编程」,不错通过以下二维码暖和。转载本文请关连低并发编程公众号。本网站已取得低并发编程的授权
|