Linux内核分析之跟踪分析Linux内核的启动过程

实验过程

学号后三位:492
转载请注明出处 + https://github.com/mengning/linuxkernel/

一、下载linux-5.0内核并编译

cd linux_os/linux-5.0
make i386_defconfig
make -j8

编译完成会在arch/x86/boot/下产生bzImage

二、制作根文件系统

1.从孟宁老师的github上下载menu
flg.1
2.建立脚本文件,用于编译menu

root@ubuntu:~/hellotest/menu# gedit mygcc.sh
root@ubuntu:~/hellotest/menu# chmod 777 mygcc.sh 

在mygcc.sh中添加:

gcc -pthread -o Myinit linktable.c menu.c test.c -m32 

3.在menu/test.c加入自己学号尾号有两位-92号系统调用
实现向文件写入数据方法:

int write_in_file(void)
{
	FILE *fp;
	if ((fp = fopen("test.txt", "w")) == NULL);
	{
		printf("File start write\n");
	}
	int i;
	
	int arr[7];
	printf("Please enter an array (less than 7 elements):\n");
	for (i = 0; i < 7; i++)
	{
		scanf("%d", &arr[i]);
	}	
	fp = fopen("test.txt", "w");
	for ( i = 0; i < 7; i++)
	{
		fprintf(fp, "%d ", arr[i]);
	}
	fclose(fp);
	return 0;
}

(1)C语言实现系统调用:

int Truncate_92(void)
{
	int length;
     write_in_file();
	printf("Please modify the file length:\n");
	scanf("%d", &length);
	truncate("test.txt", length*2);
	printf("After sorting the files, please look at the file\n");
}

(2)C语言嵌入汇编实现系统调用:

int Truncate_92_asm(void)
{
	int length;
	int ret;
	write_in_file();
	printf("Please modify the file length:\n");
	scanf("%d", &length);
	length*=2;
	asm volatile(
		"movl %2,%%ecx\n\t"
		"movl %1,%%ebx\n\t"
		"movl $0x5c,%%eax\n\t"
		"int $0x80"
		:"=a"(ret)
		:"b"("test.txt"),"c"(length)
	);
	if(ret==0){
		printf("Resize successfully\n");
		printf("After sorting the files, please look at the file\n");
	}
	else{
		printf("Resize fail\n");
	}
	return 0;
}

由于truncate函数原型是int truncate(const char *path, off_t length);包含两个参数,它的功能是将参数path指定的文件大小改为参数length指定的大小。 如果原来的文件大小比参数length大,则超过的部分会被删除。执行成功则返回0, 失败返回-1。
现在有两个参数path和length,所以需要考虑该往哪个寄存器里传值,参数按顺序依次赋给ebx、ecx、edx、esi、edi、ebp,所以path是第一个传给ebx寄存器的参数,length是第二个传给ecx寄存器的参数。把系统调用号92(0X5C)存入eax寄存器,通过执行int $0X80来执行系统调用陷入内核态。system_call根据传入的系统调用号在系统调用列表中查找到对应的系统调用内核函数,然后根据ebx,ecx寄存器中保存的参数调用内核函数sys_truncate,执行完后将执行结果存放到eax寄存器中,将eax寄存器的值传给ret。
生成Myinit文件:

root@ubuntu:~/hellotest/menu# ./mygcc.sh 

2.制作根文件系统
进入busybox-1.30.1目录下,对busybox进行配置编译。

make defconfig

make menuconfig

这里要修改如下配置:

将busybox settings -> build options -> build busybox as a static binary这一项选上。
make
然后准备根目录映像,并安装busybox到根目录映像中。

dd if=/dev/zero of=busyboxmyinitrd12M.img bs=4096 count=3072

mkfs.ext3 busyboxmyinitrd12M.img

mkdir rootfs

sudo mount -o loop busyboxmyinitrd12M.img rootfs

cd busybox-1.30.1

sudo make CONFIG_PREFIX=../rootfs/  install

将第一步编译好的可执行文件Myinitrw.sh放入rootfs/bin/
然后卸载umount rootfs
这样我们的根文件系统就做好了。

三、Qemu启动内核

1.新建启动脚本

root@ubuntu:~/linux_OS/linux-5.0# gedit start.sh
root@ubuntu:~/linux_OS/linux-5.0# chmod 777 start.sh

在脚本中添加一下代码:

qemu -kernel arch/x86/boot/bzImage -initrd ~/hellotest/busyboxmyinitrd12M.img -append "root=/dev/ram init=/bin/ash"

2.执行脚本
flg.2
note:
由于需要对文件进行读写,需要修改文件系统权限

mount -o remount rw /

flg.3
flg.4
3.从MenuOS中选择Truncate_92,测试C语言调用系统函数
flg.5
4.从MenuOS中选择Truncate_92_asm,测试汇编代码调用系统函数
flg.6

四、使用gdb跟踪系统调用内核函数sys_truncate

1.新建脚本文件start_gdb.sh
里面内容是:

qemu -kernel arch/x86/boot/bzImage -initrd ~/hellotest/busyboxmyinitrd12M.img -append "root=/dev/ram init=/bin/ash" -s -S

修改start_gdb.sh文件权限

root@ubuntu:~/linux_OS/linux-5.0# chmod 777 start_gdb.sh

执行脚本文件:

root@ubuntu:~/linux_OS/linux-5.0# ./start_gdb.sh

此时qemu界面会卡住:
flg.7
再开一个终端,启动gdb,然后连接到target remote 1234.
flg.8
2.在sys_truncate处设断点
flg.9
如图所示:
当调用系统函数时,触发断点,此时可以用list(l)来查看断点处代码
flg.10
flg.11
接下来可以用命令s单步调试,可以看到path是“test.txt”,文件重新设置的大小为12
flg.12
执行到kmem_cache_alloc函数之后,执行完毕,为文件重新分配了大小。
flg.13

五、system_call中断处理过程

ENTRY(system_call)
	RING0_INT_FRAME
	ASM_CLAC
	pushl_cfi %eax #保存系统调用号
	SAVE_ALL       #保存现场,将用到的所有cpu寄存器保存到栈中
	GET_THREAD_INFO(%ebp)#ebp用于存放当前进程thread_info结构的地址
	testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
	jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax#检查系统调用号(系统调用号应小于NR_syscalls)
	jae syscall_badsys   #不合法,跳入异常处理
syscall_call:
	call *sys_call_table(,%eax,4)#通过系统调用号在系统调用表中找到相应的系统调用内核处理函数
	movl %eax,PT_EAX(%esp)#保存返回值到栈中
syscall_exit:
	testl $_TIF_ALLWORK_MASK,%ecx#检查是否有任务需要处理
	jne syscall_exit_work#需要,进入syscall_exit_work,这里是最常见的进程调度时机
restore_all:
	TRACE_IRQS_IRET#恢复现场
irq_return:
	INTERRUPT_RETURN#iret

sys_call_table(,%eax,4):
因为分派表中的每个表项占4个字节,所以先把系统调用号乘以4,再加上sys_call_table分派表的起始地址,即可得到系统调用号对应的系统调用内核处理函数的指针。

六、总结

当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。
在Linux中是通过执行int $0x80来执行系统调用的, 这条汇编指令产生向量为128的编程异常。
整个系统调用过程如下:

1.用户应用程序填充系统调用的寄存器
2.进程从用户态切换到内核态, 并执行系统调用 entry_SYSCALL_32
3.entry_SYSCALL_32切换到内核栈, 保存现场(通用寄存器, 旧的栈. flags)
4.entry_SYSCALL_32 调用 sys_call_table 中的函数, 如果正确调用对应的函数, 如果错误退出.
5.系统调用完成, 恢复现场(通用寄存器, 旧的栈. flags).

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值