⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 index.html

📁 这是一个介绍 linux 编程知识的文章。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<p>mov $256,%ecx
<p>rp_sidt:
<p>movl %eax,(%edi)
<p>movl %edx,4(%edi)
<p>addl $8,%edi
<p>dec %ecx
<p>jne rp_sidt
<p>ret
<p>selector = __KERNEL_CS, DPL = 0, TYPE = E, P = 1<font face="宋体">)</font>;
<p><font size=+0>2.Start_kernel()(linux/init/main.c)<font face="宋体">调用</font>trap_init()(linux/arch/i386/kernel/trap.c)<font face="宋体">函数设置中断描述符表。在该函数里,实际上是通过调用函数</font>set_system_gate(SYSCALL_VECTOR,&amp;system_call)<font face="宋体">来完成该项的设置的。其中的</font>SYSCALL_VECTOR<font face="宋体">就是</font>0x80<font face="宋体">,而</font>system_call<font face="宋体">则是一个汇编子函数,它即是中断</font>0x80<font face="宋体">的处理函数,主要完成两项工作:</font>a.
<font face="宋体">寄存器上下文的保存;</font>b.
<font face="宋体">跳转到系统调用处理函数。在后面会详细介绍这些内容。</font></font></ol>
</ol>
<font face="宋体"><font size=+0> </font></font>
<dir>
<dir>
<dir>
<dir><font face="宋体">(补充说明:门描述符</font>
<p>&nbsp;&nbsp;&nbsp; set_system_gate()<font face="宋体">是在</font>linux/arch/i386/kernel/trap.S<font face="宋体">中定义的,在该文件中还定义了几个类似的函数</font>set_intr_gate(),
set_trap_gate, set_call_gate()<font face="宋体">。这些函数都调用了同一个汇编子函数</font>__set_gate()<font face="宋体">,该函数的作用是设置门描述符。</font>IDT<font face="宋体">中的每一项都是一个门描述符。</font>
<p>#define _set_gate(gate_addr,type,dpl,addr)
<p>set_gate(idt_table+n,15,3,addr);
<p><font face="宋体">&nbsp;&nbsp;&nbsp; 门描述符的作用是用于控制转移,其中会包括选择子,这里总是为</font>__KERNEL_CS<font face="宋体">(指向</font>GDT<font face="宋体">中的一项段描述符)、入口函数偏移地址、门访问特权级(</font>DPL<font face="宋体">)以及类型标识(</font>TYPE<font face="宋体">)。</font>Set_system_gate<font face="宋体">的</font>DPL<font face="宋体">为</font>3<font face="宋体">,表示从特权级</font>3<font face="宋体">(最低特权级)也可以访问该门,</font>type<font face="宋体">为</font>15<font face="宋体">,表示为</font>386<font face="宋体">中断门。)</font>
<p><img SRC="a2.gif" height=326 width=577><font face="宋体"><font size=+0> </font></font>
<br><font face="宋体"><font size=+0> </font></font></dir>
</dir>
</dir>
</dir>

<ol>
<ol><b><font face="宋体"><font size=+0>B.与系统调用相关的数据结构</font></font></b>
<p><font face="宋体"><font size=+0>1.系统调用处理函数的函数名的约定</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 函数名都以“</font>sys_<font face="宋体">”开头,后面跟该系统调用的名字。例如,系统调用</font>fork()<font face="宋体">的处理函数名是</font>sys_fork()<font face="宋体">。</font></font>
<p><font size=+0>asmlinkage int sys_fork(struct pt_regs regs);</font>
<p><font size=+0><font face="宋体">(补充关于</font>asmlinkage<font face="宋体">的说明)</font></font>
<p><font face="宋体"><font size=+0> </font></font>
<br><font size=+0><font face="宋体">2.系统调用号(</font>System Call Number<font face="宋体">)</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 核心中为每个系统调用定义了一个唯一的编号,这个编号的定义在</font>linux/include/asm/unistd.h<font face="宋体">中,编号的定义方式如下所示:</font></font>
<p><font size=+0>#define __NR_exit 1</font>
<p><font size=+0>#define __NR_fork 2</font>
<p><font size=+0>#define __NR_read 3</font>
<p><font size=+0>#define __NR_write 4</font>
<p><font size=+0>. . . . . .</font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 用户在调用一个系统调用时,系统调用号号作为参数传递给中断</font>0x80<font face="宋体">,而该标号实际上是后面将要提到的系统调用表</font>(sys_call_table)<font face="宋体">的下标,通过该值可以找到相映系统调用的处理函数地址。</font></font>
<p><font face="宋体"><font size=+0> </font></font>
<br><font face="宋体"><font size=+0>3.系统调用表</font></font></ol>
</ol>

<dir>
<dir>
<dir>
<dir><font size=+0><font face="宋体">系统调用表的定义方式如下:(</font>linux/arch/i386/kernel/entry.S<font face="宋体">)</font></font>
<p><font size=+0>ENTRY(sys_call_table)</font>
<dir><font size=+0>.long SYMBOL_NAME(sys_ni_syscall)</font>
<p><font size=+0>.long SYMBOL_NAME(sys_exit)</font>
<p><font size=+0>.long SYMBOL_NAME(sys_fork)</font>
<p><font size=+0>.long SYMBOL_NAME(sys_read)</font>
<p><font size=+0>.long SYMBOL_NAME(sys_write)</font>
<p><font size=+0>. . . . . .</font></dir>
<font size=+0><font face="宋体">系统调用表记录了各个系统调用处理函数的入口地址,以系统调用号为偏移量很容易的能够在该表中找到对应处理函数地址。在</font>linux/include/linux/sys.h<font face="宋体">中定义的</font>NR_syscalls<font face="宋体">表示该表能容纳的最大系统调用数,</font>NR_syscalls
= 256<font face="宋体">。</font></font>
<p><font face="宋体"><font size=+0> </font></font>
<br><b><font face="宋体"><font size=+0>C.系统调用函数接口是如何转化为陷入命令</font></font></b>
<br>&nbsp;</dir>
</dir>
</dir>
</dir>

<ol TYPE="A">
<ol TYPE="A">&nbsp;
<br><font face="宋体"><font size=+0>&nbsp;&nbsp;&nbsp; 如前面提到的,系统调用是通过一条陷入指令进入核心态,然后根据传给核心的系统调用号为索引在系统调用表中找到相映的处理函数入口地址。这里将详细介绍这一过程。</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 我们还是以</font>x86<font face="宋体">为例说明:</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 由于陷入指令是一条特殊指令,而且依赖与操作系统实现的平台,如在</font>x86<font face="宋体">中,这条指令是</font>int
0x80<font face="宋体">,这显然不是用户在编程时应该使用的语句,因为这将使得用户程序难于移植。所以在操作系统的上层需要实现一个对应的系统调用库,每个系统调用都在该库中包含了一个入口点(如我们看到的</font>fork,
open, close<font face="宋体">等等),这些函数对程序员是可见的,而这些库函数的工作是以对应系统调用号作为参数,执行陷入指令</font>int
0x80<font face="宋体">,以陷入核心执行真正的系统调用处理函数。当一个进程调用一个特定的系统调用库的入口点,正如同它调用任何函数一样,对于库函数也要创建一个栈帧。而当进程执行陷入指令时,它将处理机状态转换到核心态,并且在核心栈执行核心代码。</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 这里给出一个示例(</font>linux/include/asm/unistd.h<font face="宋体">):</font></font>
<p><font size=+0>#define _syscallN(type, name, type1, arg1, type2, arg2,
. . . ) \</font>
<p><font size=+0>type name(type1 arg1,type2 arg2) \</font>
<p><font size=+0>{ \</font>
<p><font size=+0>long __res; \</font>
<p><font size=+0>__asm__ volatile ("int $0x80" \</font>
<p><font size=+0>: "=a" (__res) \</font>
<p><font size=+0>: "" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)));
\</font>
<p><font size=+0>. . . . . .</font>
<p><font size=+0>__syscall_return(type,__res); \</font>
<p><font face="宋体"><font size=+0>}</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 在执行一个系统调用库中定义的系统调用入口函数时,实际执行的是类似如上的一段代码。这里牵涉到一些</font>gcc<font face="宋体">的嵌入式汇编语言,不做详细的介绍,只简单说明其意义:</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 其中</font>__NR_##name<font face="宋体">是系统调用号,如</font>name
== ioctl<font face="宋体">,则为</font>__NR_ioctl<font face="宋体">,它将被放在寄存器</font>eax<font face="宋体">中作为参数传递给中断</font>0x80<font face="宋体">的处理函数。而系统调用的其它参数</font>arg1,
arg2, …<font face="宋体">则依次被放入</font>ebx, ecx, . . .<font face="宋体">等通用寄存器中,并作为系统调用处理函数的参数,这些参数是怎样传入核心的将会在后面介绍。</font></font>
<p><font face="宋体"><font size=+0>&nbsp;&nbsp;&nbsp; 下面将示例说明:</font></font>
<p><font size=+0>int func1()</font>
<p><font size=+0>{</font>
<p><font size=+0>int fd, retval;</font>
<p><font size=+0>fd = open(filename, ……);</font>
<p><font size=+0>……</font>
<p><font size=+0>ioctl(fd, cmd, arg);</font>
<p><font size=+0>. . .</font>
<p><font size=+0>}</font>
<p><font face="宋体"><font size=+0> </font></font>
<p><font size=+0>func2()</font>
<p><font size=+0>{</font>
<p><font size=+0>int fd, retval;</font>
<p><font size=+0>fd = open(filename, ……);</font>
<p><font size=+0>……</font>
<p><font size=+0>__asm__ __volatile__(\</font>
<p><font size=+0>"int $0x80\n\t"\</font>
<p><font size=+0>:"=a"(retval)\</font>
<p><font size=+0>:"0"(__NR_ioctl),\</font>
<p><font size=+0>"b"(fd),\</font>
<p><font size=+0>"c"(cmd),\</font>
<p><font size=+0>"d"(arg));</font>
<p><font face="宋体"><font size=+0>}</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 这两个函数在</font>Linux/x86<font face="宋体">上运行的结果应该是一样的。</font></font>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -