2022-2023-1 20222814 《Linux内核原理与分析》第五周作业
实验四 使用库函数和C代码中嵌入汇编代码实现系统调用
前置知识
系统调用的三层机制:
API(应用程序编程接口)
中断向量(系统调用处理入口)
服务程序(系统调用内核处理系统)
内核态&用户态:
计算机的硬件资源是有限的,为了减少有限资源的访问和使用冲突,CPU和操作系统必须提供一些机制对用户程序进行权限划分,Linux系统采用了Intel x86 CPU的0和3两个特权级别,分别对应内核态和用户态。当CPU的执行级别对应的是内核态时,代码可以执行特权指令,访问任意的物理内存。相应的,在用户态,代码能够掌控的范围会受到限制。
用户态和内核态的区分方法:
CS:EIP的指向范围(对于32位的X86机器):
内核态下可以访问0x00000000~0xffffffff的地址空间,共4GB。
用户态下只能访问0x00000000~0xbfffffff的地址空间,不能访问从0xc0000000开始的地址空间。
中断:
一般来说,进入内核态是由中断触发的,系统调用是一种特殊的中断。
中断的类别:
硬件中断:在用户态进程执行时,硬件中断信号到来,进入内核态,就会执行这个中断对应的中断服务例程。
软中断(trap的方式):用户态程序执行的过程中,调用了一个系统调用,陷入了内核态。
1、进入/LinuxKernel/linux-3.18.6/arch/x86/syscalls目录,查看syscall_32.tbl文件,获取系统调用号信息。此次实验选择write,其系统调用号为4,即0x4
函数定义:ssize_t write (int fd, const void * buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。
返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入erro中。
2、使用库函数API的方式利用系统调用,编写20222814write.c文件,通过write()返回输入的字符串。
代码如下:
#include<unistd.h>//unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。
int main(){
char *m = "20222814\n";
write(1,m,9);
return 0;
}
结果如下:
3、在C代码中嵌入汇编使用系统调用,修改20222814write.c文件,嵌入汇编代码。
代码如下:
#include<unistd.h>
int main(){
char *m = "20222814\n";
int len = 9;
int r;
asm volatile(
movl $0x4, %%eax\n\t" //write系统调用号为4
"movl $0x1, %%ebx\n\t" //文件描述符1:标准输出stdout
"movl %1, %%ecx\n\t" //要输出的信息
"movl %2, %%edx\n\t" //要输出的长度
"movl %%eax, %0\n\t"
:"=m"(r)
:"c"(m),"d"(len)
:
);
}
结果如下: