linux signal 符号表6,linux及安全第六周总结——20135227黄晓妍(示例代码)

总结部分:

操作系统内核三大功能:

进程管理,内存管理,文件系统

最核心的是进程管理

为了管理,首先要对每一个进程进行描述。进程描述符提供了所有内核需要了解的信息。

进程控制模块:task_struct(抽象task_struct的简化图)

9cd097c577894df8a56dccf7245c701b.jpg

next_task,prev_task进程链表的管理

tty_struct控制台

fs_struct文件系统描述

file_struct打开的文件描述符

mm_struct内存管理的描述

signal_struct信号的描述

Linux-3.18.6/include/linux/sched.h里的代码

79077eec85144701be3384fe34eb68a1.jpg

1235代码state进程运行状态

stack指定进程的内核堆栈

flags每个进程的标识符

c3d8d2958b3c4c08a8fbd271f694d9eb.jpg

1245CONFIG SMP条件编译,多处理器时使用到的。

2940079212fc432185ca1a69b219a39d.jpg

1251on_rq运行队列

58cb5d0c90f24ba3a5181ea2f060fa57.jpg

1295list_head tasks进程链表(双向链表)

linux进程的状态和操作系统原理的描述进程状态有所不同,比如就绪状态和运行状态都是TASK_RUNNING。(这个表示它是可运行的,但是实际上有没有在运行取决于它是否占有CPU)

e64cf97b7f6e496abe0434966c84128a.jpg

1330进程标识符pid

0daea74d90b8454bbdebb98992cef30a.jpg

1349进程的父子关系

118ba5da3f534d51ad528fc9e8f1de69.jpg

1360pid_link pids[PIDTYPE_MAX]进程的哈希表

Linux-3.18.6/arch/x86/include/asm/processor.h

thread_struct(很重要)

676283bcc84d489d91e3f2c37afb04f5.jpg

0f9481fec7804add85b39efa6c6a37e5.jpg

进程的创建概括以及fork()一个进程

Cpu_idle启动两个线程:(0号进程是所有线程的祖先)

Kernel_init用户态的进程启动,所有用户态进程的祖先(1号进程是所有进程的祖先)

Kthreadd所有线程的祖先

在shell命令行创建进程的本质一样:先复制一份进程描述符,0号进程是手工写进代码的,1号进程复制0号的pcb,然后根据1号进程的需要把它的pid等等信息修改    掉,再加载一个init可执行程序。

进程是如何创建:

先看怎么在用户态创建一个子进程

9ea135fb85824bfcb0987cb543fd0cda.jpg

Pid==0是下面两个模块都会被执行,fork()系统调用在父进程子进程各返回一次,父进程中返回0,子进程中返回子进程的pid

理解进程创建过程复杂代码的方法:

系统调用:用户态int0x80(由于是陷入进入内核的,所以机器自动保存与转换堆栈;压入用户ss,压入用户esp,压入EFLAGS,压入cs,压入eip)

中断指令跳转到内和空间sysstem_call(压入eax,把传递参数的寄存器全部压栈)执行结束后RESTORE_ALL(弹栈传递参数的寄存器,弹栈eax,iret弹栈      int0x80压栈的东西)

Fork()的也是一个系统调用,它的过程图

86ad53c554e844939f8e626f742c03d1.jpg

子进程复制了父进程的所有信息,然后做适当修改,它也会调度执行。当它被CPU调度的时候从哪里开始执行呢?子进程在内核里执行,在内核处理程序从哪里开始    执行的?与mykernel类似。

fork,vfork,clone三个系统调用都是通过调用do_fork来创建创建一个新的进程。

先我们设想,它应该如何创建一个进程,我们画一个框架,然后再通过代码求证,再对我们的框架进行修正。

我们的框架:

1.创建新进程都是通过复制父进程的信息。

2.创建新进程的过程中需要做哪些事情:

复制pcb

还需要修改复制的父进程的pcb

还需要分配新的内核堆栈

子进程需要从fork返回到用户态,那么它内核堆栈也需要从父进程中拷贝一些过来,不然不能返回

还有thread.sp(调度到子进程时的内核栈顶)和thread.ip(调度到子进程的第一条指令地址)

浏览创建进程的相关关键代码

ec8dc25c07d3435885e678c88f942e48.jpg

Linux-3.18.6/kernel/fork.c

6f572bca00f1449d9473366e1018cba8.jpg

1632copy_process创建一个进程的主要代码

2a10b9f5a0d6410f97b3edc2e704dc0f.jpg

1240dup_task_struct复制pcb(看具体怎么复制)

625f0d38e1e9432ebf263395dba2e41f.jpg

6b5ca2bad65a4f668a86994ebe6ac125.jpg

320arch_dup_task_struct(tsk,orig)执行复制当前进程

7e51bee6564243d3a0ed5910258b1c97.jpg

293*dst=*src数据结构的指针的值复制

316alloc_thread_info_node(tsk,node),分配内核空间堆栈的作用和thread_info合在一起的集合体

1f95781a736f4a9aa1cd1b48a6248adf.jpg

153实际上是创建了一个一定大小的页面。一部分存放alloc_thread_info,一部分存放堆栈

190bd381500b4ce0b3eed2931920d9f5.jpg

335setup_thread_stack

b30b70e09f3443548c55fdb6f51806e4.jpg

445f4516ad42489e9226d7be2f56c51b.jpg

1240p=dup_task_struct(current);复制子进程的pcb

2457601c19ed4f0e8cd7c51f8e7b045a.jpg

1396 copy_thread

Linux-3.18.6/arch/x86/kernel/process_32.c

135*childregs=task_pt_regs(p);

159*childregs=*current_pt_regs();将父进程的现在的状态信息赋值给子进程(拷贝内核堆栈诗句和指定新进程的第一条指令)

164p.thread.ip通过ret_from_fork得到。

创建的新进程是从哪里开始执行的?

Linux-3.18.6/arch/x86/include/asm/ptrace.h

Pt_regs:系统调用压栈的内容(SAVE_ALL的全部内容)

d3ee281a42f745b2b0431c2fa9790fae.jpg

Linux-3.18.6/arch/x86/kernel/entry_32.s

290entry(ret_from_fork)新进程是从这里开始执行的

cfd5f44959c846509d7510338e26a071.jpg

505syscall exit内核堆栈返回到系统调用以前的状态继续执行

157cc949c2e34a288f926d1d1fed680f.jpg

实验部分:

使用gdb跟踪调试创建新进程的过程

cd LinuxKernel

rm menu –rf

git clone https://github.com/mengning/menu.git

cd menu

mv test_fork.c test.c//覆盖test_fork.c

make rootfs

feb7754e3a3d431ea7cdfaccd8c4b4f3.jpg

看到增加了一个fork

96fcb47e2f07452389afd3c474c94c5b.jpg

24d1efb16a1f4e18a306b96706f93c82.jpg

使用gdb跟踪调试内核

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

# 关于-s和-S选项的说明:

# -S freeze CPU at startup (use ’c’ to start execution)

# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

Gdb

(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表

(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行

cf2d3413c9d34eda9c10bc18e2f73774.jpg

363d2d564f7d4b488a6253e3208e3108.jpg

b sys_clone

b do_fork

b dup_task_struct

b copy_process

b copy_thread

b ret_from_fork

c5d09960c5354aa29954467b08974e4f.jpg

copy_process

c13e05f97761490c9347aa3abd472ee7.jpg

dup_task_struct

68e6875d6e8f40eca71bd875cd3f90b0.jpg

将父进程的现在的状态信息赋值给子进程(拷贝内核堆栈诗句和指定新进程的第一条指令)

e05e23137ef447feb7b803fc810f6866.jpg

将子进程的栈顶保存

766c1c3e2e7d42e4bcb7ce35ffa2cd5c.jpg

线程的pid保存

3d4cc8ae3fef485795bebc0da30f015b.jpg

直到syscall_exit就跟踪不到了。

思考部分:

理解创建一个新进程如何创建和修改task_struct数据结构

一般通过系统调用来创建新的进程。fork(),vfork(),clone()都是通过调用do_fork来创建新进程的。要通过复制父进程的信息pcb(task_struct),然后给新    的子进程分配内核堆栈,再通过copy_process来修改子进程的task_struct.

特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。

ce43afda65f144309b26b6074327f4c0.jpg

从ret_from_thread开始执行。子进程被创建以后是在内核运行的,因为从这里开始复制父进程的task_struct,分配内核堆栈,创建进程也是一种系统调用,在内核堆栈中,执行int0x80,保存现场,来保证执行起点和内核堆栈的一致性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值