《Linux内核分析》 week6作业-Linux内核fork()系统调用的创建过程
一.进程控制块PCB-stack_struct
进程在操作系统中都有一个结构,用于表示这个进程。这就是进程控制块(PCB),在Linux中具体实现是task_struct数据结构,它主要记录了以下信息:
- 状态信息,例如可执行状态、就绪状态、阻塞状态等。
- 性质,由于unix有很多变种,进行有自己独特的性质。
- 资源,资源的链接比如内存,还有资源的限制和权限等。
- 组织,例如按照家族关系建立起来的树(父进程、子进程等)。
task_struct结构体内容非常庞大,暂时没有去分析源代码,以后有时间再去研究。
二.Linux fork执行的过程
在menu中添加一个fork的系统调用,然后用gdb开始调试.执行以下命令
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -s gdb file linux-3.18.6/vmlinux target remote:1234
然后在sys_fork、sys_clone处设置断点,再逐步调试,观察fork系统调用的执行过程。
具体分析fork系统调用执行过程.
1.fork、vfork和clone三个系统调用都可以创建一个新进程,而且它们都是通过调用do_fork来实现进程的创建,do_fork通过传递不同的clone_flags来实现fork、clone、vfork。
long do_fork(unsigned long clone_flags, 1624 unsigned long stack_start, 1625 unsigned long stack_size, 1626 int __user *parent_tidptr, 1627 int __user *child_tidptr) 1628{ 1629 struct task_struct *p; 1630 int trace = 0; 1631 long nr; 1632 1633 /* 1634 * Determine whether and which event to report to ptracer. When 1635 * called from kernel_thread or CLONE_UNTRACED is explicitly 1636 * requested, no event is reported; otherwise, report if the event 1637 * for the type of forking is enabled. 1638 */ 1639 if (!(clone_flags & CLONE_UNTRACED)) { 1640 if (clone_flags & CLONE_VFORK) 1641 trace = PTRACE_EVENT_VFORK; 1642 else if ((clone_flags & CSIGNAL) != SIGCHLD) 1643 trace = PTRACE_EVENT_CLONE; 1644 else 1645 trace = PTRACE_EVENT_FORK; 1646 1647 if (likely(!ptrace_event_enabled(current, trace))) 1648 trace = 0; 1649 } 1650 1651 p = copy_process(clone_flags, stack_start, stack_size, 1652 child_tidptr, NULL, trace); #进程复制,核心函数 1653 /* 1654 * Do this prior waking up the new thread - the thread pointer 1655 * might get invalid after that point, if the thread exits quickly. 1656 */ 1657 if (!IS_ERR(p)) { 1658 struct completion vfork; 1659 struct pid *pid; 1660 1661 trace_sched_process_fork(current, p); 1662 1663 pid = get_task_pid(p, PIDTYPE_PID); 1664 nr = pid_vnr(pid); 1665 1666 if (clone_flags & CLONE_PARENT_SETTID) 1667 put_user(nr, parent_tidptr); 1668 1669 if (clone_flags & CLONE_VFORK) { 1670 p->vfork_done = &vfork; 1671 init_completion(&vfork); 1672 get_task_struct(p); 1673 } 1674 1675 wake_up_new_task(p); 1676 1677 /* forking complete and child started to run, tell ptracer */ 1678 if (unlikely(trace)) 1679 ptrace_event_pid(trace, pid); 1680 1681 if (clone_flags & CLONE_VFORK) { 1682 if (!wait_for_vfork_done(p, &vfork)) 1683 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); 1684 } 1685 1686 put_pid(pid); 1687 } else { 1688 nr = PTR_ERR(p); 1689 } 1690 return nr; 1691} 1692
do_fork()函数的核心是copy_process(),该函数完成了进程创建的绝大部分。
/* 1175 * This creates a new process as a copy of the old one, 1176 * but does not actually start it yet. 1177 * 1178 * It copies the registers, and all the appropriate 1179 * parts of the process environment (as per the clone 1180 * flags). The actual kick-off is left to the caller. 1181 */ 1182static struct task_struct *copy_process(unsigned long clone_flags, 1183 unsigned long stack_start, 1184 unsigned long stack_size, 1185 int __user *child_tidptr, 1186 struct pid *pid, 1187 int trace) 1188{ 1189 int retval; 1190 struct task_struct *p; 1191 1192 if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) 1193 return ERR_PTR(-EINVAL); 1194 1195 if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS)) 1196 return ERR_PTR(-EINVAL); 1197 1198 /* 1199 * Thread groups must share signals as well, and detached threads 1200 * can only be started up within the thread group. 1201 */ 1202 if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND)) 1203 return ERR_PTR(-EINVAL); 1204 1205 /* 1206 * Shared signal handlers imply shared VM. By way of the above, 1207 * thread groups also imply shared VM. Blocking this case allows 1208 * for various simplifications in other code. 1209 */ 1210 if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM)) 1211 return ERR_PTR(-EINVAL); 1212 1213 /* 1214 * Siblings of global init remain as zombies on exit since they are 1215 * not reaped by their parent (swapper). To solve this and to avoid 1216 * multi-rooted process trees, prevent global and container-inits 1217 * from creating siblings. 1218 */ 1219 if ((clone_flags & CLONE_PARENT) && 1220 current->signal->flags & SIGNAL_UNKILLABLE) 1221 return ERR_PTR(-EINVAL); 1222 1223 /* 1224 * If the new process will be in a different pid or user namespace 1225 * do not allow it to share a thread group or signal handlers or 1226 * parent with the forking task. 1227 */ 1228 if (clone_flags & CLONE_SIGHAND) { 1229 if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) || 1230 (task_active_pid_ns(current) != 1231 current->nsproxy->pid_ns_for_children)) 1232 return ERR_PTR(-EINVAL); 1233 } 1234 1235 retval = security_task_create(clone_flags); 1236 if (retval) 1237 goto fork_out; 1238 1239 retval = -ENOMEM; 1240 p = dup_task_struct(current); #为子进程创建一个新的内核栈,复制task_struct和thread_info结构,此时子进程的进程控制块和父进程完全一致。 1241 if (!p) 1242 goto fork_out; 1243 1244 ftrace_graph_init_task(p); 1245 1246 rt_mutex_init_task(p); 1247 1248#ifdef CONFIG_PROVE_LOCKING 1249 DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); 1250 DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); 1251#endif 1252 retval = -EAGAIN; 1253 if (atomic_read(&p->real_cred->user->processes) >= 1254 task_rlimit(p, RLIMIT_NPROC)) { 1255 if (p->real_cred->user != INIT_USER && 1256 !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) 1257 goto bad_fork_free; 1258 } 1259 current->flags &= ~PF_NPROC_EXCEEDED; 1260 1261 retval = copy_creds(p, clone_flags); 1262 if (retval < 0) 1263 goto bad_fork_free; 1264 1265 /* 1266 * If multiple threads are within copy_process(), then this check 1267 * triggers too late. This doesn't hurt, the check is only there 1268 * to stop root fork bombs. 1269 */ 1270 retval = -EAGAIN; 1271 if (nr_threads >= max_threads) 1272 goto bad_fork_cleanup_count; 1273 1274 if (!try_module_get(task_thread_info(p)->exec_domain->module)) 1275 goto bad_fork_cleanup_count; 1276 1277 delayacct_tsk_init(p); /* Must remain after dup_task_struct() */ 1278 p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER); 1279 p->flags |= PF_FORKNOEXEC; 1280 INIT_LIST_HEAD(&p->children); 1281 INIT_LIST_HEAD(&p->sibling); 1282 rcu_copy_process(p); 1283 p->vfork_done = NULL; 1284 spin_lock_init(&p->alloc_lock); 1285 1286 init_sigpending(&p->pending); 1287 1288 p->utime = p->stime = p->gtime = 0; ....
通过dup_task_struct()函数,为子进程创建一个新的内核栈,复制task_struct和thread_info结构。
ti=alloc_thread_info_node(task,node); tsk->stack=ti; setup_thread_stack(tsk,orig); //这里只是复制了thread_info
重点关注下,fork()创建子进程后,父进程从系统调用中返回,而子进程从哪开始返回.
这主要是在copy_process()中copy_thread()代码.
int copy_thread(unsigned long clone_flags, unsigned long sp, 133 unsigned long arg, struct task_struct *p) 134{ 135 struct pt_regs *childregs = task_pt_regs(p); 136 struct task_struct *tsk; 137 int err; 138 139 p->thread.sp = (unsigned long) childregs; #记录进程切换时的堆栈指针 140 p->thread.sp0 = (unsigned long) (childregs+1); 141 memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); 142 143 if (unlikely(p->flags & PF_KTHREAD)) { 144 /* kernel thread */ 145 memset(childregs, 0, sizeof(struct pt_regs)); 146 p->thread.ip = (unsigned long) ret_from_kernel_thread; 147 task_user_gs(p) = __KERNEL_STACK_CANARY; 148 childregs->ds = __USER_DS; 149 childregs->es = __USER_DS; 150 childregs->fs = __KERNEL_PERCPU; 151 childregs->bx = sp; /* function */ 152 childregs->bp = arg; 153 childregs->orig_ax = -1; 154 childregs->cs = __KERNEL_CS | get_kernel_rpl(); 155 childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED; 156 p->thread.io_bitmap_ptr = NULL; 157 return 0; 158 } 159 *childregs = *current_pt_regs();#复制内核堆栈 160 childregs->ax = 0; #这也是为什么子进程的fork返回0 161 if (sp) 162 childregs->sp = sp; 163 164 p->thread.ip = (unsigned long) ret_from_fork; #子进程开始执行处 165 task_user_gs(p) = get_user_gs(current_pt_regs()); 166 167 p->thread.io_bitmap_ptr = NULL; 168 tsk = current; 169 err = -ENOMEM; 170 171 if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { 172 p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr, 173 IO_BITMAP_BYTES, GFP_KERNEL); 174 if (!p->thread.io_bitmap_ptr) { 175 p->thread.io_bitmap_max = 0; 176 return -ENOMEM; 177 } 178 set_tsk_thread_flag(p, TIF_IO_BITMAP); 179 } 180 181 err = 0; 182 183 /* 184 * Set a new TLS for the child thread? 185 */ 186 if (clone_flags & CLONE_SETTLS) 187 err = do_set_thread_area(p, -1, 188 (struct user_desc __user *)childregs->si, 0); 189 190 if (err && p->thread.io_bitmap_ptr) { 191 kfree(p->thread.io_bitmap_ptr); 192 p->thread.io_bitmap_max = 0; 193 } 194 return err; }
然后回到do_fork()函数中,唤醒子进程并开始运行。至此,一个进程创建就完成了。
三.实验总结
中间虽然的很多细节还不是很清楚,但是对linux 创建子进程的大体流程有了一个宏观的认识,更加深刻地理解了底层Linux 内核进程运行的机制。
转载于:https://www.cnblogs.com/sixue/p/4469177.html
《Linux内核分析》 week6作业-Linux内核fork()系统调用的创建过程相关推荐
- 《Linux内核分析》 第四节 扒开系统调用的三层皮(上)
<Linux内核分析> 第四节 扒开系统调用的三层皮(上) 张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com ...
- linux内核分析 网络九,“Linux内核分析”实验报告(九)
一 Linux内核分析博客简介及其索引 本次实验简单的分析了计算机如何进行工作,并通过简单的汇编实例进行解释分析 在本次实验中 通过听老师的视频分析,和自己的学习,初步了解了进程切换的原理.操作系统通 ...
- 《Linux内核分析》 第四节 扒开系统调用的三层皮(上)
黄胤凯 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.视频学习 1 ...
- linux程序分析工具下载,linux 程序分析工具
http://blog.csdn.net/denny_233/article/details/7477282 2012 linux程序分析工具介绍(一)--"/proc" 写在最前 ...
- Linux内核分析考试试题,linux内核分析第二周作业
#include #include "mypcb.h" extern tPCB task[MAX_TASK_NUM]; extern tPCB * my_current_task; ...
- linux内核分析实验三,linux内核分析第三次实验
WIN10 CMD 启动虚拟WIFI 1.以管理员身份运行命令提示符: 快捷键win+R→输入cmd→回车 2.启用并设定虚拟WiFi网卡: 运行命令:netsh wlan set hostednet ...
- LINUX进程调度分析源码,Linux 实时调度(源码分析)
为了弄清楚在多cpu系统中是如何实现实时调度的,先引入以下几个概念: cpu的状态: 我们知道,在linux系统中,任务的优先级为0~140. INVALID:(-1)该cpu不可用 IDLE(0): ...
- linux运维初学作业,Linux运维班第二次作业(示例代码)
一.列出当前系统上所有已经登录的用户的用户名(同用户登录多次只显示一次). 题目并没有说明取出的用户名是否允许排序处理,所以可写成: # who | cut -d' ' -f1 | sort -u 二 ...
- linux 农业分析,基于嵌入式Linux的农业信息采集系统设计与研究
摘要: 精准农业是现代化农业发展的方向,将嵌入式数据采集技术应用到农业生产过程中,既可以节省人力物力成本,还可以提高农作物产量和质量,减少农药污染和提高生产效率.在精准农业生产过程中,需要采集和监测农 ...
- linux数据库分析报告,写linux数据
Linux 内存管理机制简介 在 Linux 中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于 Windo ...
最新文章
- std::transform使用
- 服务器需不需要虚拟内存,服务器需不需要虚拟内存
- 【spring】让spring的注解和xml配置文件变得优雅,最常用的注解
- [转]UI设计小技巧
- 云计算竞争愈发激烈,2019年云计算运维前景怎么样?
- 【LiveVideoStack线上分享】— FreeSWITCH视频会议“标准”解决方案
- android+4.4.2+横屏,Android 横竖屏和布局问题
- “Hello, my first blog”------第一篇博客的仪式感
- Yaml spring boot 二维数组写法
- 电商后台管理系统项目介绍和项目初始化
- DDOS防火墙新一代操作思路与进阶应用方法浅析
- 【DDD】领域驱动设计实践 —— Application层实现
- 易基因|RRBS单碱基绘制580种动物的基因组规模DNA甲基化谱:Nature子刊
- win10如何通过局域网从浏览器访问ip
- 胡喜:从 BASIC 到 basic ,蚂蚁金服技术要解决两个基本的计算问题
- 字符编码的常用种类介绍
- get crash report binary image adress on ios
- 疯狂动物城 zootopia
- 火绒安全软件v5.0.44,比较好的安全防护软件
- CentOS7.6 安装配置Amber18/AmberTools18(GPU加速版)