用大白话说操作系统(十)

从内核态到用户态

void main(void) {
    ...    
    move_to_user_mode();
    if (!fork()) {
        init();
    }
    for(;;) pause();
}

之前我们已经把初始化的部分都解决了。现在看main函数中剩下的一些代码,可以看到,下一步是切换到用户模式。之前我们的代码都是在内核态,其实就是特权级很高的东西,我们可以随意的定义中断,改变分页机制,我们可以修改内核代码并且占据内存的一些地方,但是这怎么可以呢?有些地方是绝对不能让用户碰到的,所有我们需要进行限制。
一旦转变为用户态,之后的代码就会一直运行在用户态,除非我们使用系统调用的中断指令,此时的用户态会陷入内核态,当中断处理程序执行完毕,就会从内核态切换到用户态。

内核态和用户态的本质是特权级不同。这源于CPU的保护机制,有分段保护机制和分页保护机制。有关特权级的保护属于分段保护机制的一种。

代码跳转只能同特权级,数据访问只能高特权级访问低特权级。
Intel设计了许多特权级转换的方式,中断和中断返回就是其中一种。
没有中断也可以中断返回

void main(void) {
    ...    
    move_to_user_mode();
    ...
}

#define move_to_user_mode() \
_asm { \
    _asm mov eax,esp \
    _asm push 00000017h \
    _asm push eax \
    _asm pushfd \
    _asm push 0000000fh \
    _asm push offset l1 \
    _asm iretd /* 执行中断返回指令*/ \
_asm l1: mov eax,17h \
    _asm mov ds,ax \
    _asm mov es,ax \
    _asm mov fs,ax \
    _asm mov gs,ax \
}

这里的iretd就是中断返回指令。
理论上中断返回是配合中断使用的,但是这里并不是真的发生了中断,所以我们要假装发生中断。所以之前进行了五次的压栈操作。
在这里插入图片描述
压入栈的CS EIP 就是中断发生前代码所处的位置,这样中断返回后继续到那里执行。
压入栈的SS ESP就是中断发生前栈的位置,这样中断返回后恢复原来的栈。
特权级的转换就体现在CS和SS寄存器的值里面。
在这里插入图片描述
我们可以看到CS和SS都是段寄存器,段寄存器中存放的是段选择子,段选择子的结构如上图所示。最后两位表示特权级
代码中给CS赋值0000000fh----->0000000000001111
最后两位11表示特权级是3 ,用户态。CS寄存器里的特权级,表示CPL【当前处理器的特权级】
倒数第3位是TI,表示前面的描述符索引,是从GDT中还是从LDT中取,1表示LDT,也就是局部描述符表中取。
之前将0号LDT作为当前的LDT索引,记录在CPU的lldt寄存器中。

下面我们来看进程创建,我们建立一个数据结构叫做task_struct.
1、上下文环境
程序的最终的本质是执行指令,涉及寄存器、内存和外设端口
不过寄存器的数量很少,如果说所有的进程都用寄存器那么肯定不够用,最稳妥的方式就是每次切换进程就把这个进程的寄存器的值存放在一个地方,之后切换的时候再返回。所以结构体里面就有一个tss结构,存储CPU的寄存器的信息。

struct task_struct {
    ...
    struct tss_struct 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;
};

有了cr3字段,指向不同的页目录表,整个页表结构就是完全不同的一套,那么线性地址到物理地址的映射关系就有能力做到不同。

运行时间信息
如何判断一个进程该结束了,切换到下一个进程。
给进程一个属性,叫剩余时间片,每次时钟中断来了都-1,如果减到0了,就触发进程切换的操作。counter进行记录。

优先级
这个就涉及到counter的初始化问题。priority这个字段就是这个事,每次counter初始化成priority。

进程状态

标记进程运行的状态,如果进程处在读硬盘状态进入中断阻塞,那么就进行标记。state字段就是这个功能。

#define TASK_RUNNING          0
#define TASK_INTERRUPTIBLE    1
#define TASK_UNINTERRUPTIBLE  2
#define TASK_ZOMBIE           3
#define TASK_STOPPED          4
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;
};

好的上面就是所有的进程数据结构的字段。
好的,OK,今天就说到这里吧,有问题欢迎评论区留言讨论!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神仙诙谐代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值