首先,把上周用汇编实现的getpid加入menuOS。
可见getpid工作正常。
接着开始调试内核。
qemu -kernel arch/x86/boot/bzImage -initrd rootfs/rootfs.img -s -S
新建一个终端,用gdb链接到端口1234
gdb -q -ex 'file vmlinux' -ex 'target remote:1234'
gdb 的 -ex 可以让gdb直接执行某命令。 非常建议把这两行指令放在两个bash file里,方便调试。
查得getpid对应sys_getpid, 在sys_getpid处设置断点后,使用next命令。
Breakpoint 1, sys_getpid () at kernel/sys.c:817
817 {
(gdb) n
818 return task_tgid_vnr(current);
(gdb)
817 {
(gdb)
818 return task_tgid_vnr(current);
(gdb)
819 }
(gdb)
schedule () at kernel/sched/core.c:2866
2866 {
(gdb)
2867 struct task_struct *tsk = current;
(gdb)
2869 sched_submit_work(tsk);
(gdb)
2866 {
(gdb)
2869 sched_submit_work(tsk);
(gdb)
2870 __schedule();
(gdb)
2871 }
0x0804a13b in ?? ()
(gdb)
Cannot find bounds of current function
可以发现getpid调用了kernel/sys.c模块,得到当前pid,最后恢复现场。
接着使用单步调试, 发现在kernel/pid.c中有以下关键代码。
pid_nr_ns (ns=<optimized out>, pid=<optimized out>) at kernel/pid.c:502
502 if (pid && ns->level <= pid->level) {
(gdb)
503 upid = &pid->numbers[ns->level];
(gdb) l
498 {
499 struct upid *upid;
500 pid_t nr = 0;
501
502 if (pid && ns->level <= pid->level) {
503 upid = &pid->numbers[ns->level];
504 if (upid->ns == ns)
505 nr = upid->nr;
506 }
507 return nr;
(gdb)
系统调用流程(ubuntu上找不到合适的软件画图。。只能用文字箭头表示)
用户态 内核态
getpid() -> int 0x80 利用中断向量匹配system_call 保存现场->
内核态 用户态
sys_getpid() 利用系统调用号匹配 -> syscall_after_call 保存返回值 -> restore all 恢复现场 ->iret 返回用户态
以下摘录一些kernel/entry_32.S 关键代码。
ENTRY(system_call)
SAVE_ALL
GET_THREAD_INFO(%ebp)
syscall_call:
call *sys_call_table(,%eax,4)
syscall_after_call:
movl %eax,PT_EAX(%esp) # store the return value
restore_all:
TRACE_IRQS_IRET
irq_return:
INTERRUPT_RETURN