linux及安全第六周总结——20135227黄晓妍
2016-03-31 12:15
399 查看
[b]总结部分:[/b]
操作系统内核三大功能:
进程管理,内存管理,文件系统
最核心的是进程管理
为了管理,首先要对每一个进程进行描述。进程描述符提供了所有内核需要了解的信息。
进程控制模块:task_struct(抽象task_struct的简化图)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112255113-1785411403.png)
next_task,prev_task进程链表的管理
tty_struct控制台
fs_struct文件系统描述
file_struct打开的文件描述符
mm_struct内存管理的描述
signal_struct信号的描述
Linux-3.18.6/include/linux/sched.h里的代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111540473-1190700758.png)
1235代码state进程运行状态
stack指定进程的内核堆栈
flags每个进程的标识符
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111703223-1061557281.png)
1245CONFIG SMP条件编译,多处理器时使用到的。
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111754191-1110435497.png)
1251on_rq运行队列
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111837144-1032830428.png)
1295list_head tasks进程链表(双向链表)
linux进程的状态和操作系统原理的描述进程状态有所不同,比如就绪状态和运行状态都是TASK_RUNNING。(这个表示它是可运行的,但是实际上有没有在运行取决于它是否占有CPU)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111928035-1248552296.png)
1330进程标识符pid
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112110457-38671090.png)
1349进程的父子关系
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112148285-2000259440.png)
1360pid_link pids[PIDTYPE_MAX]进程的哈希表
Linux-3.18.6/arch/x86/include/asm/processor.h
thread_struct(很重要)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112659426-921010470.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112709863-504690030.png)
进程的创建概括以及fork()一个进程
Cpu_idle启动两个线程:(0号进程是所有线程的祖先)
Kernel_init用户态的进程启动,所有用户态进程的祖先(1号进程是所有进程的祖先)
Kthreadd所有线程的祖先
在shell命令行创建进程的本质一样:先复制一份进程描述符,0号进程是手工写进代码的,1号进程复制0号的pcb,然后根据1号进程的需要把它的pid等等信息修改 掉,再加载一个init可执行程序。
进程是如何创建:
先看怎么在用户态创建一个子进程
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112921207-117194959.png)
Pid==0是下面两个模块都会被执行,fork()系统调用在父进程子进程各返回一次,父进程中返回0,子进程中返回子进程的pid
理解进程创建过程复杂代码的方法:
系统调用:用户态int0x80(由于是陷入进入内核的,所以机器自动保存与转换堆栈;压入用户ss,压入用户esp,压入EFLAGS,压入cs,压入eip)
中断指令跳转到内和空间sysstem_call(压入eax,把传递参数的寄存器全部压栈)执行结束后RESTORE_ALL(弹栈传递参数的寄存器,弹栈eax,iret弹栈 int0x80压栈的东西)
Fork()的也是一个系统调用,它的过程图
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113012691-1400669471.png)
子进程复制了父进程的所有信息,然后做适当修改,它也会调度执行。当它被CPU调度的时候从哪里开始执行呢?子进程在内核里执行,在内核处理程序从哪里开始 执行的?与mykernel类似。
fork,vfork,clone三个系统调用都是通过调用do_fork来创建创建一个新的进程。
先我们设想,它应该如何创建一个进程,我们画一个框架,然后再通过代码求证,再对我们的框架进行修正。
我们的框架:
1.创建新进程都是通过复制父进程的信息。
2.创建新进程的过程中需要做哪些事情:
复制pcb
还需要修改复制的父进程的pcb
还需要分配新的内核堆栈
子进程需要从fork返回到用户态,那么它内核堆栈也需要从父进程中拷贝一些过来,不然不能返回
还有thread.sp(调度到子进程时的内核栈顶)和thread.ip(调度到子进程的第一条指令地址)
浏览创建进程的相关关键代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113048488-131831009.png)
Linux-3.18.6/kernel/fork.c
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113431894-1022261751.png)
1632copy_process创建一个进程的主要代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113523129-1033185155.png)
1240dup_task_struct复制pcb(看具体怎么复制)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113645191-828946430.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113737973-158214770.png)
320arch_dup_task_struct(tsk,orig)执行复制当前进程
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113846691-594792562.png)
293*dst=*src数据结构的指针的值复制
316alloc_thread_info_node(tsk,node),分配内核空间堆栈的作用和thread_info合在一起的集合体
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114137660-1493143050.png)
153实际上是创建了一个一定大小的页面。一部分存放alloc_thread_info,一部分存放堆栈
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114253254-2075865392.png)
335setup_thread_stack
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114429019-1649828603.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114438754-124570051.png)
1240p=dup_task_struct(current);复制子进程的pcb
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114533723-3639076.png)
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的全部内容)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115535816-99106473.png)
Linux-3.18.6/arch/x86/kernel/entry_32.s
290entry(ret_from_fork)新进程是从这里开始执行的
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115738504-2056401642.png)
505syscall exit内核堆栈返回到系统调用以前的状态继续执行
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115836426-606318296.png)
[b]实验部分:[/b]
使用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
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115846457-1623522385.png)
看到增加了一个fork
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115856098-1758190752.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115904066-1597551928.png)
使用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继续运行
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115916785-55154629.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115925676-1105782612.png)
b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120011848-588691809.png)
copy_process
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120029988-2083205568.png)
dup_task_struct
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120128379-1825556669.png)
将父进程的现在的状态信息赋值给子进程(拷贝内核堆栈诗句和指定新进程的第一条指令)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120138769-1122597623.png)
将子进程的栈顶保存
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120148035-354294936.png)
线程的pid保存
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120158848-1558831380.png)
直到syscall_exit就跟踪不到了。
[b]思考部分:[/b]
理解创建一个新进程如何创建和修改task_struct数据结构
一般通过系统调用来创建新的进程。fork(),vfork(),clone()都是通过调用do_fork来创建新进程的。要通过复制父进程的信息pcb(task_struct),然后给新 的子进程分配内核堆栈,再通过copy_process来修改子进程的task_struct.
特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331121210488-468752438.png)
从ret_from_thread开始执行。子进程被创建以后是在内核运行的,因为从这里开始复制父进程的task_struct,分配内核堆栈,创建进程也是一种系统调用,在内核堆栈中,执行int0x80,保存现场,来保证执行起点和内核堆栈的一致性。
操作系统内核三大功能:
进程管理,内存管理,文件系统
最核心的是进程管理
为了管理,首先要对每一个进程进行描述。进程描述符提供了所有内核需要了解的信息。
进程控制模块:task_struct(抽象task_struct的简化图)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112255113-1785411403.png)
next_task,prev_task进程链表的管理
tty_struct控制台
fs_struct文件系统描述
file_struct打开的文件描述符
mm_struct内存管理的描述
signal_struct信号的描述
Linux-3.18.6/include/linux/sched.h里的代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111540473-1190700758.png)
1235代码state进程运行状态
stack指定进程的内核堆栈
flags每个进程的标识符
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111703223-1061557281.png)
1245CONFIG SMP条件编译,多处理器时使用到的。
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111754191-1110435497.png)
1251on_rq运行队列
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111837144-1032830428.png)
1295list_head tasks进程链表(双向链表)
linux进程的状态和操作系统原理的描述进程状态有所不同,比如就绪状态和运行状态都是TASK_RUNNING。(这个表示它是可运行的,但是实际上有没有在运行取决于它是否占有CPU)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331111928035-1248552296.png)
1330进程标识符pid
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112110457-38671090.png)
1349进程的父子关系
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112148285-2000259440.png)
1360pid_link pids[PIDTYPE_MAX]进程的哈希表
Linux-3.18.6/arch/x86/include/asm/processor.h
thread_struct(很重要)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112659426-921010470.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112709863-504690030.png)
进程的创建概括以及fork()一个进程
Cpu_idle启动两个线程:(0号进程是所有线程的祖先)
Kernel_init用户态的进程启动,所有用户态进程的祖先(1号进程是所有进程的祖先)
Kthreadd所有线程的祖先
在shell命令行创建进程的本质一样:先复制一份进程描述符,0号进程是手工写进代码的,1号进程复制0号的pcb,然后根据1号进程的需要把它的pid等等信息修改 掉,再加载一个init可执行程序。
进程是如何创建:
先看怎么在用户态创建一个子进程
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331112921207-117194959.png)
Pid==0是下面两个模块都会被执行,fork()系统调用在父进程子进程各返回一次,父进程中返回0,子进程中返回子进程的pid
理解进程创建过程复杂代码的方法:
系统调用:用户态int0x80(由于是陷入进入内核的,所以机器自动保存与转换堆栈;压入用户ss,压入用户esp,压入EFLAGS,压入cs,压入eip)
中断指令跳转到内和空间sysstem_call(压入eax,把传递参数的寄存器全部压栈)执行结束后RESTORE_ALL(弹栈传递参数的寄存器,弹栈eax,iret弹栈 int0x80压栈的东西)
Fork()的也是一个系统调用,它的过程图
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113012691-1400669471.png)
子进程复制了父进程的所有信息,然后做适当修改,它也会调度执行。当它被CPU调度的时候从哪里开始执行呢?子进程在内核里执行,在内核处理程序从哪里开始 执行的?与mykernel类似。
fork,vfork,clone三个系统调用都是通过调用do_fork来创建创建一个新的进程。
先我们设想,它应该如何创建一个进程,我们画一个框架,然后再通过代码求证,再对我们的框架进行修正。
我们的框架:
1.创建新进程都是通过复制父进程的信息。
2.创建新进程的过程中需要做哪些事情:
复制pcb
还需要修改复制的父进程的pcb
还需要分配新的内核堆栈
子进程需要从fork返回到用户态,那么它内核堆栈也需要从父进程中拷贝一些过来,不然不能返回
还有thread.sp(调度到子进程时的内核栈顶)和thread.ip(调度到子进程的第一条指令地址)
浏览创建进程的相关关键代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113048488-131831009.png)
Linux-3.18.6/kernel/fork.c
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113431894-1022261751.png)
1632copy_process创建一个进程的主要代码
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113523129-1033185155.png)
1240dup_task_struct复制pcb(看具体怎么复制)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113645191-828946430.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113737973-158214770.png)
320arch_dup_task_struct(tsk,orig)执行复制当前进程
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331113846691-594792562.png)
293*dst=*src数据结构的指针的值复制
316alloc_thread_info_node(tsk,node),分配内核空间堆栈的作用和thread_info合在一起的集合体
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114137660-1493143050.png)
153实际上是创建了一个一定大小的页面。一部分存放alloc_thread_info,一部分存放堆栈
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114253254-2075865392.png)
335setup_thread_stack
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114429019-1649828603.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114438754-124570051.png)
1240p=dup_task_struct(current);复制子进程的pcb
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331114533723-3639076.png)
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的全部内容)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115535816-99106473.png)
Linux-3.18.6/arch/x86/kernel/entry_32.s
290entry(ret_from_fork)新进程是从这里开始执行的
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115738504-2056401642.png)
505syscall exit内核堆栈返回到系统调用以前的状态继续执行
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115836426-606318296.png)
[b]实验部分:[/b]
使用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
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115846457-1623522385.png)
看到增加了一个fork
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115856098-1758190752.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115904066-1597551928.png)
使用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继续运行
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115916785-55154629.png)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331115925676-1105782612.png)
b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120011848-588691809.png)
copy_process
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120029988-2083205568.png)
dup_task_struct
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120128379-1825556669.png)
将父进程的现在的状态信息赋值给子进程(拷贝内核堆栈诗句和指定新进程的第一条指令)
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120138769-1122597623.png)
将子进程的栈顶保存
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120148035-354294936.png)
线程的pid保存
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331120158848-1558831380.png)
直到syscall_exit就跟踪不到了。
[b]思考部分:[/b]
理解创建一个新进程如何创建和修改task_struct数据结构
一般通过系统调用来创建新的进程。fork(),vfork(),clone()都是通过调用do_fork来创建新进程的。要通过复制父进程的信息pcb(task_struct),然后给新 的子进程分配内核堆栈,再通过copy_process来修改子进程的task_struct.
特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
![](https://images2015.cnblogs.com/blog/744818/201603/744818-20160331121210488-468752438.png)
从ret_from_thread开始执行。子进程被创建以后是在内核运行的,因为从这里开始复制父进程的task_struct,分配内核堆栈,创建进程也是一种系统调用,在内核堆栈中,执行int0x80,保存现场,来保证执行起点和内核堆栈的一致性。
相关文章推荐
- centos6.6下安装Gcc4.8.1
- linux下ioctl()函数使用的详细说明
- centos 正确 安装 jdk
- CentOS 7 修改网卡名称为eth0
- CentOS 7 修改网卡名称为eth0
- Centos添加Windows/Centos双系统启动
- 《Linux内核--分析Linux内核创建一个新进程的过程 》 20135311傅冬菁
- linux,windows,mac最强Android模拟器
- 《Linux内核设计与实现》第三章读书笔记
- linux手动安装sbt过程
- 《Linux内核设计与实现》第十八章读书笔记
- linux 查看文件夹个数
- linux 安装库 编译提示-lcrypto 无法链接,缺少 crypto 库
- 全面解析Linux 内核 3.10.x - initramfs 启动流程
- 20135202闫佳歆--week6 分析Linux内核创建一个新进程的过程——实验及总结
- linux远程下载文件 的两种方法之 ftp命令和scp命令
- Linux 中查看网口流量的利器 -- sar
- linux下mysql的卸载、安装全过程
- linux nfs挂载
- Linux文档的压缩与打包