当前位置: 首页 > wzjs >正文

网站建站好处盐城网络

网站建站好处,盐城网络,WordPress下级,91游戏中心🚀 前言 本文是非常非常非常重要的一节,是后续进程调度的基础,对应于书中的第18回。希望各位给个三连,拜托啦,这对我真的很重要!!! 目录 🚀 前言🏆 任务状态段…

🚀 前言

    本文是非常非常非常重要的一节,是后续进程调度的基础,对应于书中的第18回。希望各位给个三连,拜托啦,这对我真的很重要!!!

目录

  • 🚀 前言
  • 🏆 任务状态段(TSS)与局部描述符(LDT)
    • 📃 什么是TSS与LDT
    • 📃 初始化TSS0与LDT0
  • 🏆 为进程调度做铺垫
    • 📃 初始化剩下的TSS与LDT
    • 📃 告诉内存TSS与LDT的位置
  • 🏆 设置时钟中断与系统调用
    • 📃 设置时钟中断
    • 📃 设置系统调用
  • 🎯总结
  • 📖参考资料

🏆 任务状态段(TSS)与局部描述符(LDT)

📃 什么是TSS与LDT

    TSS是在操作系统进程管理过程中,任务,也就是进程或线程,在切换时保存的上下文信息。其实里面存放的所谓上下文信息就是各个寄存器的情况,这样进程切换的时候,才能做到保存和恢复上下文,继续执行。具体可以看下面的结构体:

struct tss_struct {long	back_link;	/* 16 high bits zero */long	esp0;long	ss0;		/* 16 high bits zero */long	esp1;long	ss1;		/* 16 high bits zero */long	esp2;long	ss2;		/* 16 high bits zero */long	cr3;long	eip;long	eflags;long	eax,ecx,edx,ebx;long	esp;long	ebp;long	esi;long	edi;long	es;		/* 16 high bits zero */long	cs;		/* 16 high bits zero */long	ss;		/* 16 high bits zero */long	ds;		/* 16 high bits zero */long	fs;		/* 16 high bits zero */long	gs;		/* 16 high bits zero */long	ldt;		/* 16 high bits zero */long	trace_bitmap;	/* bits: trace 0, bitmap 16-31 */struct i387_struct i387;
};

    对照下面的图食用效果更好:
在这里插入图片描述

    LDT 叫局部描述符表,对应的就是全局描述符(GDT),为什么做出这样的区分呢?GDT的作用是为整个系统定义了全局的段描述符。它包含了系统中所有进程和线程所使用的公共段描述符;LDT 是进程本地的,为每个进程定义了私有的段描述符。讲人话就是, 通过LDT,每个进程可以定义自己的代码段、数据段、堆栈段等。这样,不同进程之间的内存可以隔离开来,一个进程无法直接访问其他进程的内存

📃 初始化TSS0与LDT0

    在看代码之前,先来回顾一下内存与GDT,不记得GDT与段描述符的话可以查看这篇博客:linux0.11内核源码修仙传第二章——setup.s。这里贴出目前的内存分布:

在这里插入图片描述

    来看一下进程调度算法中如何初始化TSS与LDT的,FIRST_TSS_ENTRYFIRST_LDT_ENTRY 表明其在GDT中的位置:

#define FIRST_TSS_ENTRY 4
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)void sched_init(void)
{···set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));···
}

    这两个函数的作用是初始化TSS与LDT并添加进GDT后面两个,具体函数的定义如下:

#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \"movw %%ax,%2\n\t" \"rorl $16,%%eax\n\t" \"movb %%al,%3\n\t" \"movb $" type ",%4\n\t" \"movb $0x00,%5\n\t" \"movb %%ah,%6\n\t" \"rorl $16,%%eax" \::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \"m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \)#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")

    这两个函数共同调用_set_tssldt_desc,通过参数type区分TSS和LDT。其实这两个也是段描述符,汇编所做的事情就是设置TSS与LDT对应的短描述符各个位。下面是段描述符,详情依旧可以参看linux0.11内核源码修仙传第二章——setup.s。

在这里插入图片描述

    有了这个,就可以看一看汇编具体对应的设置了:

// 操作数约束:输入和内存操作数
::"a" (addr),               // 输入:addr存入eax寄存器"m" (*(n)),               // 内存操作数:n对应%1(Limit低16位)"m" (*(n+2)),             // 内存操作数:n+2对应%2(Base低16位)"m" (*(n+4)),             // 内存操作数:n+4对应%3(Base中间8位)"m" (*(n+5)),             // 内存操作数:n+5对应%4(Type)"m" (*(n+6)),             // 内存操作数:n+6对应%5(Limit高4位 + Flags)"m" (*(n+7))              // 内存操作数:n+7对应%6(Base高8位)
指令含义
movw $104, %1设置段限长(0~16位)
movw %%ax, %2Base低16位
rorl $16, %%eax     movb %%al, %3Base中间8位
movb $" type ", %4设置Type至P的8位,type参数指定了描述符类型(0x89对应TSS)
movb $0x00, %5设置Limit往前共8位
movb %%ah, %6设置Base高8位

    设置完成之后,内存如下所示(主要看右侧的GDT分布变化,多了TSS与LDT):

在这里插入图片描述

🏆 为进程调度做铺垫

📃 初始化剩下的TSS与LDT

    接着往下看sched_init函数:

typedef struct desc_struct {unsigned long a,b;
} desc_table[256];struct task_struct * task[64] = {&(init_task.task), };void sched_init(void)
{···struct desc_struct * p;p = gdt+2+FIRST_TSS_ENTRY;	// 在TSS基础上再往后挪两个单位,即GDT下一个空白处for(i=1;i<64;i++) {task[i] = NULL;p->a=p->b=0;p++;p->a=p->b=0;p++;}···
}

    这里做的事情还是比较好懂的,其实就是初始化了一个64长度的task_struct数组并赋上初值,同时给GDT剩下的位置填充上0。先来看看task_struct数组,初始化后数组如下:

在这里插入图片描述
    这个数组里面每个都是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;
};

    这个结构体可以先看着,后面等进程调度实际应用时可以再细看。

    接下来上上强度,下面图是未来整个内存的规划,目前还没有涉及到进程,下面的图可以先不用理解的很细。每个进程都有自己的LDT,每个LDT都有对应的数据段,代码段等,和GDT最开始的一样。

在这里插入图片描述
    这里还有一个很重要的东西,就是最开始循环之前两行代码设置的TSS0和LDT0。为什么说很重要呢,那是因为虽然目前还没有创建进程,但是我们目前正在运行的代码就是未来的一个进程的指令流 ,也就是在后面进程调度建立起来后,当前代码就会变为进程0。因此需要TSS0和LDT0将这些信息提前存好。等到后面进程调度建立后会更加直观。

📃 告诉内存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指令是给idtr寄存器赋值,告诉CPU中断描述符IDT在内存的位置。详情可以见linux0.11内核源码修仙传第二章——setup.s。这里的ltrlldt与之前的类似,分别给tr寄存器和ldt寄存器赋值,告诉CPU,LDT与TSS在内存中的位置。如下所示:

在这里插入图片描述

    这样,CPU 之后就能通过 tr 寄存器找到当前进程的任务状态段信息,也就是上下文信息,以及通过 ldt 寄存器找到当前进程在用的局部描述符表信息。至于这么多LDT与TSS,放哪个,那自然是最开始的LDT0和TSS0,之后的只需要依据指针加减即可获得。

🏆 设置时钟中断与系统调用

📃 设置时钟中断

    在接下来的代码中,还需要设置时钟中断。所谓时钟中断就是一个定时器会持续以一定频率向CPU发送中断信号:

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);···
}

    好了,上面解释了时钟中断的来源,既然是外界的信息,那自然又涉及到了CPU交互。这个中断信号是一个可编程定时芯片,CPU要与其进行交互,就需要先写入指定端口对应的指令,具体设置方式需要参考芯片手册。

    之后就需要开启一个中断处理函数,给的中断号是0x20,中断处理函数为timer_interrupt,每次定时器向CPU发送中断后便会执行这个函数。 这个定时器的触发,以及时钟中断函数的设置,是操作系统主导进程调度的一个关键! 没有他们这样的外部信号不断触发中断,操作系统就没有办法作为进程管理的主人,通过强制的手段收回进程的 CPU 执行权限。

📃 设置系统调用

    第二个设置的中断叫系统调用 system_call,中断号是 0x80,这个中断又是个非常非常非常非常非常非常非常重要的中断,所有用户态程序想要调用内核提供的方法,都需要基于这个系统调用来进行。比如 Java 程序员写一个 read,底层会执行汇编指令 int 0x80,这就会触发系统调用这个中断,最终调用到 Linux 里的 sys_read 方法。

void sched_init(void)
{···set_system_gate(0x80,&system_call);
}

    截止目前为止,设置的中断如下:

中断号中断处理函数
0 ~ 0x10trap_init 里设置的一堆
0x20timer_interrupt
0x21keyboard_interrupt
0x80system_call

    0-0x10 这 17 个中断是 trap_init 里初始化设置的,是一些基本的中断,比如除零异常等。设置见linux0.11内核源码修仙传第六章——中断初始化。 0x21是键盘中断,设置见linux0.11内核源码修仙传第八章——控制台初始化。0x20是定时器中断,0x80是系统调用中断。

🎯总结

    本文就干了三件事:第一,我们往全局描述符表写了两个结构,TSS 和 LDT,作为未来进程 0 的任务状态段和局部描述符表信息。第二,我们初始化了一个结构为 task_struct 的数组,未来这里会存放所有进程的信息,并且我们给数组的第一个位置附上了 init_task.init 这个具体值,也是作为未来进程 0 的信息。第三,设置了时钟中断 0x20 和系统调用 0x80,一个作为进程调度的起点,一个作为用户程序调用操作系统功能的桥梁。

    其实整个操作系统就是靠中断驱动,各个模块不断初始化各种中断处理函数,并且开启指定的外设开关,让操作系统自己慢慢“活”了起来,逐渐通过中断忙碌于各种事情中。


📖参考资料

[1] linux源码趣读
[2] 一个64位操作系统的设计与实现
[3] 任务状态段(Task State Segment)
[4] Linux 内存管理(二)之GDT与LDT

http://www.dtcms.com/wzjs/616337.html

相关文章:

  • 网站icp备案证明纪检网站建设
  • 聊城网站推广的公司seo广州工作好吗
  • 企业网站界面风格设计描述网站链接推广怎么赚钱
  • 怎么测网站流量吗wordpress 代码样式
  • 免费网站建设步骤手把手教你优化网站
  • 兰州网站建设论坛建网站步骤
  • 58同城湛江网站建设商业网站大全
  • 硅藻泥网站怎么做vultr服务器做网站
  • 罗湖做网站的公司哪家好开发公司工程部管理流程
  • 东台做网站的公司wordpress查询收录
  • 临河做网站清远市建设工程交易中心网站
  • 网站备案座机网络专业的网站建设价格低
  • 建站公司都是用什么建站工具南京做微网站
  • 服装企业网站建设现状竞价网站托管
  • 成都服装网站建设电子商务网站建设与维护 答案
  • 最新某地方装修门户源码 php装饰公司程序 dede行业网站模板视频网站开发
  • 定制网站前准备网站seo策划方案设计
  • 网站关键词排名不稳定wordpress install.php 500
  • php做的网站收录百度官网首页官网
  • 广东手机网站建设哪家专业搜索引擎有哪些?
  • 推广网站怎么做能增加咨询做一个响应式网站价格
  • 做牛仔裤的小视频网站网站推广和精准seo
  • 网站建设系统改版wordpress 当前页面登录密码
  • 单页面网站制作教程做网站端口无法清除
  • 网站怎么制作视频教程做网站阿里云买哪个服务器好点
  • 网站静态和动态区别是什么华联股份股票
  • 邯郸做网站多少钱网站怎么做的支付宝
  • 网站制作的步骤男女做羞羞羞的事视频网站
  • 公司网站升级改版方案竞价移动网站
  • 那些网站反爬做的好电子商务平台定制开发