首页 > 系统相关 >Linux 内核0.11 系统调用详解(上)

Linux 内核0.11 系统调用详解(上)

时间:2023-07-10 20:31:46浏览次数:55  
标签:set 操作系统 system 调用 0.11 内核 Linux gate


备注:本文通过三个问题,引出Linux 内核0.11的系统调用。


操作系统为什么要引出系统调用?

回答这个问题前,请先参看如下图:

Linux 内核0.11 系统调用详解(上)_特权级

由图可以看出,从操作系统的角度来看,一台计算机主要分为两级:用户级以及内核级,系统调用主要作用就是连接用户级和内核级的“插座”。上层用户的许多对计算机硬件的操作,如读写磁盘文件,让显示器输出字符等,都通过接口来完成。那再思考一个问题,不用接口直接操作计算机不可以嘛?答案当然是可以,可是这样带来的后果是什么?我把它总结为两点:

  • 底层封装繁杂的硬件操作始终需要有人完成,遵循软件设计的原则,我们不能向用户层暴露太多的底层实现细节,否则会加大应用层编写的复杂性。
  • 对底层的操作,如果不通过系统调用限制,会发生用户应用程序修改系统内核等误操作,造成操作系统运行瞬间奔溃,考虑到系统的稳定性、安全性等问题,我们需要向上提供接口,限制应用层连入内核的权限。

好了,系统调用既然非存在不可,那接下来,我们就探究下,它具体是怎么实现的呢?请看下个问题。o(∩_∩)o

操作系统如何做到用户态数据与核心态数据隔离?

请看此图:

Linux 内核0.11 系统调用详解(上)_linux_02


这里为什么要引出一张内存图,我们首先要建立起操作系统内存是如何使用的,由图可以看出,在内存的低地址处,放置了真正的操作系统内核代码,而在高地址处才放置了我们的应用程序的代码。因此,自然而然的一个想法就是,通过对与内核模块代码段,数据段和对用户区的代码数据段做区分来阻止用户直接访问内核模块。Linux内核通过建立段级保护机制来完成上述区分核心与用户态区域的功能。好,请看下图:

Linux 内核0.11 系统调用详解(上)_计算机_03


由图可以总结出以下几点:

  • 越处于核心地带,特权级越高,对应的数字越小;相反,用户态的特权级越低,对应的数字越大。
  • 用户程序的特权级通过段寄存器cs的低两位来描述,CPL=3。(CPL表示当前特权级,current privilege level)
  • 核心态的特权级,DPL =0。(DPL 为 描述符特权级,descriptor privilege level)

因此,当执行应用程序时,cs段寄存器为当前代码段,此时的特权级CPL为3,将和想要跳转的目标段的DPL特权级进行比较,当且仅当DPL<=CPL时,当前代码段才能跳转到目标代码段。这显然是可以通过硬件检查特权级的方法来阻止此种越界访问的违法操作。

既然操作系统能阻止这种违法操作,那用户程序怎么进入核心态呢?这就引出了我们接下来的一个话题。

操作系统通过什么方式进入核心态,开始它的系统调用之旅呢?

对于Intel x86系统,那就是通过中断调用号 int 0x80来完成。上段代码:

void sched_init(void)  
{  
    int i;  
    struct desc_struct * p;  

    if (sizeof(struct sigaction) != 16)  
        panic("Struct sigaction MUST be 16 bytes");  
    set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));  
    set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));  
    p = gdt+2+FIRST_TSS_ENTRY;  
    for(i=1;i<NR_TASKS;i++) {  
        task[i] = NULL;  
        p->a=p->b=0;  
        p++;  
        p->a=p->b=0;  
        p++;  
    }  
/* Clear NT, so that we won't have troubles with that later on */  
    __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");  
    ltr(0);  
    lldt(0);  
    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);  
}

这段代码位于/linux/kernel下,当操作系统运行起来后,将会进行一系列的初始化,注意代码段的最后:set_system_gate(0x80,&system_call);这是操作系统初始化IDT表后,将0x80中断号,和system_call函数进行了绑定。即以后当有应用程序执行了0x80中断,请记得它一定会执行system_call函数。再看(linux/include/asm/system.h):

#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr);
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__("movw %%dx,%%ax\n\t" "movw %0,%%dx\n\t"\
"movl %%eax,%1\n\t" "movel %%edx,%2":\
:"i"((short)(0x8000+(dpl<<13)+type<<8))),"o"(*(( \
char*)(gate_addr))),"o"(*4+(char*)(gate_addr))), \
"d"((char*)(addr),"a"(0x00080000))

这段代码,内含内嵌汇编,这里不做解释,直接给出结果。内嵌汇编的详细知识点

回归正题,根据如下图给出的目的段描述符:

Linux 内核0.11 系统调用详解(上)_linux_04


在执行汇编程序之前,operation constraint对寄存器做出了约束,%0、%1被指向了idt[0x80]的低四个字节,以及高四个字节的内存地址。并将addr的地址赋给edx(32位)。将0x00080000赋给了寄存eax(32位)。输入完成之后,执行汇编程序,将dx的低16位传给eax,由eax组装成了idt中断向量表的低四个字节。将dx的高16位和%0所代表的立即数组装成了idt中断向量表的高四个字节,最终交由硬件进行,地址跳转,特权级判断处理。值得注意的是:

  • 此时的目标段描述符特权级DPL变成了3,意思就是可以让用户应用程序访问此目标代码段,但好景不长,这只是程序跳转的中转站,它将进一步跳转到system_call对应的段去执行。
  • 那它能跳嘛?哈哈,来看看低四字节的值,在16~31位中为段选择符,即当前的cs为0x0008,而CPL为cs寄存器的低两位,刚好都是0,即当前CPL的值等于system_call代码段DPL的值。这就顺理成章的跳了过去!

系统调用,基本结束了,剩下的即是内核代码的编写。在下一节中,我们将在实际的操作系统编写两段内核代码函数,让用户程序能调用系统函数。尽请期待!o(∩_∩)o


标签:set,操作系统,system,调用,0.11,内核,Linux,gate
From: https://blog.51cto.com/u_16184402/6680342

相关文章

  • Linux部分常用零碎命令
    1:开放一具体端口firewall-cmd--zone=public--add-port=8888/tcp--permanent #开放8888端口2:关闭一具体端口firewall-cmd--zone=public--remove-port=8888/tcp--permanent #关闭8888端口3:重启后防火墙生效firewall-cmd--reload #配置立即生效......
  • Vsphere esxi 虚拟机资源管理,linux Ubuntu 系统分区方案
    说到分区方案,不得不提虚拟机的资源分配特性VMwarevSphere管理虚拟机资源特性:1.cpu和内存可以随意调整大小,只要关机调整即可2.硬盘容量只能往大了调整,不能缩小(有缩容方案,不过操作起来比较复杂)3.慎用精简置备(thin)硬盘模式:因为他的容量增长并不是线性的,比如你设置3TB,他一开始占......
  • OSLUI:摆脱Shell,通过自然语言操作Linux
    Linuxshell命令是强大的,但也是复杂的。不光是新手记不住,往往很多老手也不一定能很好地掌握一些shell命令。最近开发了一款小工具:OSLUI,目标是做操作系统的自然语言界面,让人们可以摆脱Shell,直接通过自然语言和计算机进行交互。github地址:https://github.com/BalianWang/OSLUI。欢......
  • Linux_硬盘和文件系统_操作步骤和排查方式
    -技术背景DRIVEOrin™的AI计算与完整传感器套件-提供每秒254TOPS(万亿次运算)DRIVEAtlanDRIVEThor-计划于2025年开始量产2000万亿次浮点运算性-MobileyeEyeQ51.操作系统车端--EXTLINUXisaSyslinuxvariantwhichbootsfromaLinuxfilesystem.###操作......
  • Linux 系统资源管理 | 资源配置文件
    新Linux系统中,使用systemd管理所有系统资源所有资源成为unit,每一个unit都有一个配置文件有时候我们需要创建或修改unit的配置文件配置文件可以用文本编辑器打开,比如vim。也可以用命令systemctlcatnginx.service#systemctlcatnginx.service[Unit]Descript......
  • [Linux][报错解决] linux发行版无法运行systemctl和cron
    报错信息运行cron时显示了"newcrontabisinstalling",然而事实是根本没有运行crontab里的命令*/1****date>>/tmp/mydate查找解决方法时发现有两个可能的原因1.未添加必要的环境变量cron跑指令和在shell里直接写是不一样的,cron并不知道哪个路径是他需要用来跑指令的......
  • Linux系统编程笔记
    系统调用open函数文件打开函数函数原型:intopen(constchar*pathname,intflags);intopen(constchar*pathname,intflags,mode_tmode)返回值为一个文件描述符参数列表:pathname:文件的完整路径flags:打开文件的模式,常用的模式包括:O_WRONLY:只写模式O_RD......
  • 如何安装 Arch Linux 操作系统?
    ArchLinuxInstall安装到使用Arch说明前面或多或少已经接触过Debian系列和RedHat系列相关Linux发行版,对于虚拟化软件VirtualBox如何创建虚拟机和加载ISO启动盘就不过多赘述。除非你从未使用过VirtualBox或者其他虚拟化软件。Live环境推荐资料:官方指南......
  • Arch Linux 安装完成后配置声音
    安装完ArchLinux后,虽然已经装了 alsa-utils,但是仍然可能出现无法播放声音的情况,这里记录了一种解决方案,在我的Dell上成功。如果使用alsamixer解除静音后还是无法播放声音尝试:1amixerssetMasterunmute如果出现error:amixer:Unabletofindsimpleco......
  • udev 入门:管理设备事件的 Linux 子系统
    转载:udev入门:管理设备事件的Linux子系统-知乎(zhihu.com)创建这样一个脚本,当指定的设备插入时触发你的计算机去做一个指定动作。udev是一个为你的计算机提供设备事件的Linux子系统。通俗来讲就是,当你的计算机上插入了像网卡、外置硬盘(包括U盘)、鼠标、键盘、游戏操纵......