陈三县+SA18225041+原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
一、编译内核5.0
下载Linux内核源代码
在源代码压缩包的目录下执行以下命令:
xz -d linux-5.0.1.tar.xz
tar -xvf linux-5.0.1.tar
cd linux-5.0.1
安装内核编译工具
sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev
make menuconfig//找到kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info
扩大虚拟机中内容容量
make -j8 # 多线程去编译
编译完成
运行如下代码:
qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
出现如下错误
重新设置配置信息,输入如下命令:
make i386_defconfig
重新编译内核。启动qemu。
二、使用GDB跟踪调试内核
设置断点,打开MenuOS
qemu-system-x86_64 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
另外打开一个terminal
cd /usr/src/linux-5.0.1/
gdb
(gdb) file vmlinux
(gdb) target remote:1234
(gdb) break start_kernel
start_kernel函数作为内核的入口函数,定义在init/main.c文件中。它主要是初始化系统相关的内容,以便系统进入一种服务状态,提供各种API调用的服务。至此,使用Ubuntu编译Linux内核5.0以及使用gdb跟踪调试内核基本成功。
本人学号SA18225041,第41号系统调用为dup,内核源文件中的调用是#define __NR_dup 41。dup用来复制oldfd所指的文件描述符。但复制成功时返回最小的尚未被使用的文件描述符。若有错误则返回-1,错误代码存入errno中。返回的新文件描述符和参数oldfd指向同一个文件,共享所有的锁定,读写指针,和各项权限或标志位。修改test.c,增加如下代码:
int duptest(int argc, char *argv[]){
int fd;
int new_fd;
fd = open("./hello.c", O_RDWR | O_CREAT | O_TRUNC, 0666);
printf("fd = %d\n", fd);
new_fd = dup(fd); //复制文件描述符,把fd复制给new_fd
printf("new_fd = %d\n", new_fd);
write(new_fd, "hello", 5);
close(fd);
close(new_fd);
return 0;
}
int main()
{
PrintMenuOS();
SetPrompt("MenuOS>>");
MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
MenuConfig("quit","Quit from MenuOS",Quit);
MenuConfig("time","Show System Time",Time);
MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
MenuConfig("duptest","Show duptest",duptest);
ExecuteMenu();
}
//41.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
int fd;
int new_fd;
fd = open("./hello.c", O_RDWR | O_CREAT | O_TRUNC, 0666);
printf("fd = %d\n", fd);
new_fd = dup(fd); //复制文件描述符,把fd复制给new_fd
printf("new_fd = %d\n", new_fd);
write(new_fd, "hello", 5);
close(fd);
close(new_fd);
return 0;
}
编译调试
gcc -g 41.c -o 41 -m32
gdb -q
(gdb) file 41
三、实验代码以及过程分析
系统调用:
操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用 :
- 把用户从底层的硬件编程中解放出来
- 极大的提高了系统的安全性
- 使用户程序具有可移植性
API调用:
应用编程接口(Application program interface,API)和系统调用是不同的,API只是一个函数定义系统调用通过软中断向内核发出一个明确的请求。Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用)一般每个系统调用对应一个封装例程库再用这些封装例程定义出给用户的API不是每个API都对应一个特定的系统调用API可能直接提供用户态的服务(如,一些数学函数)。一个单独的API可能调用了几个系用调用,不同的API可能调用同一个系统调用。
实验总结
Linux系统就是通过内核发出的系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口。计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同一时候执行的多个进程都须要訪问这些资源,为了更好的管理这些资源进程是不同意直接操作的,全部对这些资源的訪问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)。在linux中系统调用是用户空间訪问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口。
普通情况下应用程序通过应用编程接口API,而不是直接通过系统调用来编程。
操作系统通常是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。
比方。在x86机器上能够通过int指令进行软件中断。而在磁盘完毕读写操作后会向CPU发起硬件中断。
中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table)。这个数组存储了全部中断处理程序的地址,而中断号就是对应中断在中断向量表中的偏移量。
一般地,系统调用都是通过软件中断实现的,x86系统上的软件中断由int $0x80指令产生,而128号异常处理程序就是系统调用处理程序system_call()。