学号:476
原创作品转载请注明出处
实验来源:https://github.com/mengning/linuxkernel/
一、实验要求
二、实验环境准备
1、编译Linux-5.0
1)下载Linux-5.0内核源码并解压;
2)cd linux-5.0 ; make menuconfig
(可能缺少依赖bision和flex,按照提示安装即可)
在随后出现的图形化菜单界面中找到kernel hacking–>compile-time checks and compiler options—>选择compile the kernel with debug info;(为后面实验过程使用gdb调试作准备)
3)make bzImage;
2、制作根文件系统
cd ..
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
gcc -pthread -o init linktable.c menu.c test.c -m32 -static
(这里可能出现64位编译32位程序出错的情况,执行了下面两条命令完成,根据自己的gcc版本安装一下库,我这里安装的是gcc-7...)
sudo apt-get install libc6-dev-i386
sudo apt install gcc-7 gcc-7-multilib g++-7 g++-7-multilib
接下来继续执行:
cd ../rootfs/
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
3、启动MenuOS
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd rootfs.img(根据自己是64位还是32位选择相应的qemu)
4、跟踪调试内核启动
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
启动qemu之后,开一个新的终端:
cd linux-5.0
gdb vmlinux
(gdb)target remote:1234
(gdb)b start_kernel
(gdb)c
还是因为使用的是64位调试32位程序的原因会出现以下情况:
这里重新下载了gdb源码并安装还是出现类似问题,最终在网上找到解决方案,修改了gdb/remote.c的部分代码:
if (buf_len > 2 * rsa->sizeof_g_packet)
error (_(“Remote ‘g’ packet reply is too long: %s”), rs->buf);
修改为:
if (buf_len > 2 * rsa->sizeof_g_packet) {
rsa->sizeof_g_packet = buf_len ;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
{
if (rsa->regs[i].pnum == -1)
continue;
if (rsa->regs[i].offset >= rsa->sizeof_g_packet)
rsa->regs[i].in_g_packet = 0;
else
rsa->regs[i].in_g_packet = 1;
}
}
最终可以实现gdb调试代码:
三、实验内容
跟踪系统调用
学号后两位为76,vim /usr/include/asm/unistd_32.h 查看对应的系统调用函数是getrlimit,为获取系统资源上限函数。
该函数的调用形式为:
int getrlimit(int resource, struct rlimit *rptr);
如果成功则返回0,不成功返回非0,所以如果调用成功结果为0即可;
进程的资源是在系统初始化时由进程0建立的,然后由每个后续进程继承,每种实现都可以用自己的方法对各种限制做出调整。
这个函数的每一次调用都会制定一个资源以及一个指向下列结构的指针:
struct rlimit{
rlim_t rlim_cur; //soft limit: current limit
rlim_t rlim_max; //hard limit: maximum value for rlim_cur
}
函数的参数resource可以从以下值中取一个:
RLIMIT_AS:进程可用存储区的最大总长度(字节)。
RLIMIT_CORE:core文件的最大字节数,若取值为0则阻止创建core文件。
RLIMIT_CPU:CPU时间的最大值(秒),当超过此软限制时,向该进程发送SIGXCPU信号。
RLIMIT_DATA:数据段的最大字节长度。
RLIMIT_FSIZE:可以创建的文件的最大字节长度。当超过此软限制时,则向该进程发送SIGXFSZ信号。
RLIMIT_LOCKS:一个进程可持有的文件锁的最大数。
RLIMIT_MEMLOCK:一个进程使用mlock能够锁定在存储器中的最大字节长度。
RLIMIT_NOFILE:每个进程能打开最大文件数。更改此限制将影响到sysconf函数在参数_SC_OPEN_MAX返回的值。
RLIMIT_NPROC:每个实际用户ID可拥有的最大子进程数。更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX返回的值。
RLIMIT_RSS:最大驻内存集的字节长度。(resident set size in bytes,RSS)
RLIMIT_SBSIZE:用户在任一给定时刻可以占用的套接字缓冲区的最大长度。
RLIMIT_STACK:栈的最大字节长度。
RLIMIT_VMEM:这是RLIMT_AS的同义词。
实验过程中只将resource的值确定为第一个RLIMIT_AS:进程可用存储区的最大总长度(字节)进行实验。
下面进入linux-5.0中的menu中,编辑test.c文件:
使用c语言调用API的方法代码:
使用c代码中嵌入汇编代码方式触发系统调用:
通过0x80执行中断进行系统调用,从eax寄存器获得返回值,eax传递系统调用号(76),ebx和ecx传递函数需要的参数。
在main函数中加入调用两个函数的命令:
最后按照之前的方式重新编译MenuOS:
gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs/
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd rootfs.img
运行结果如下所示,返回操作成功且得到进程资源上限大小:
查找sys.c文件的宏定义部分找到getrlimit函数的系统调用函数为old_prlimit(),如同实验开始准备实验环境时的方法一样,在该函数处打断点,可以用来分析系统调用函数的内核代码。
四、总结
-
操作系统分为用户态和内核态,操作系统为用户态运行的进程和硬件之间进行交互提供了一组接口,通过调用这些接口函数向内核发出系统调用请求,由操作系统代为完成;
-
在进入中断处理程序之前需要保护现场,也就是保存进程必须的寄存器,恢复现场就是将这些 实现系统调用首先需要使用int
-
$0x80汇编指令形成中断并且进行系统调用。这条汇编指令就是实现了从用户态到内核态的转换,将cpu的控制权交给系统调用处理函数system_call();
-
首先将系统调用号和可能用到的寄存器保存在堆栈中,对用户态进程传递过来的系统调用号进行有效性检查,如果是合法的系统调用,根据eax中的系统调用号,内核进程查看系统调用表(sys_call_table),并且找到相应的服务例程。服务例程结束以后,从eax获得系统调用的返回值,并且把这个返回值存放在曾经保存用户态eax寄存器栈单元的那个位置上。跳转到ret_from_sys_call(),终止系统调用程序的执行。当进程恢复它在用户态的执行之前,RESTORE_ALL宏定义会恢复用户进入内核前被保存到堆栈中的寄存器的值。