博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
fork源码分析
阅读量:3941 次
发布时间:2019-05-24

本文共 6216 字,大约阅读时间需要 20 分钟。

文章目录

fork的整体流程

  1. 当用户空间的代码调用fork的时候,产生0x80号中断。(此时用户空间的程序阻塞,保存用户空间的上下文信息,进入内核程序)
  2. system_call处理0x80号中断程序,获取用户空间通过寄存器带来的系统调用号
  3. 内核程序通过系统调用号,调用对应的系统调用函数sys_fork()
  4. sys_fork()底层通过do_fork()实现,调用内核代码do_fork()
  5. 首先do_fork()向系统申请一个pid,标识新产生的子进程
    (pid 的最大值32768 开始申请时在当前进程的最大的pid的基础上加一,
    当超过最大值的时候,从开始设置的起始位置找第一个可以使用的pid)
  6. 分配进程的pcb块
    slab 分配器会为内核中经常用到的一些数据结构构造专用的高速缓存区,使得提高内存的分配效率
    alloc_task_struct(); 宏可以申请一个pcb块大小的内存块
  7. 分配内核栈 thread_info
    在task_struct结构体中,有一个指针指向一块内核栈的空间,这块空间中存储着内核进程的调用信息
  8. 复制父进程的pcb块与内核栈的信息
    *ti = *orig->thread_info; //将父进程的进程的描述信息复制到新申请的thread_info结构体中
    *tsk = *orig; //父进程的pcb块复制到新申请的task_struct结构体中,该复制属于浅拷贝,所以要有前一句的复制过程
    tsk->thread_info = ti; //新申请的pcb块中的thread_info 指向新申请的内核栈
    ti->task = tsk; //新申请的内核栈中的task指针指向新申请的pcb块
  9. 保存新进程的pid
    在 task_struct 中的pid的值设置为刚刚申请的pid的值
  10. 复制父进程的各种信息
/**		 * copy_semundo,copy_files,copy_fs,copy_sighand,copy_signal		 * copy_mm,copy_keys,copy_namespace创建新的数据结构,并把父进程相应数据结构的值复制到新数据结构中。		 * 除非clone_flags参数指出它们有不同的值。		 */		if ((retval = copy_semundo(clone_flags, p)))			goto bad_fork_cleanup_audit;		//文件描述符		if ((retval = copy_files(clone_flags, p)))			goto bad_fork_cleanup_semundo;		//文件系统		if ((retval = copy_fs(clone_flags, p)))			goto bad_fork_cleanup_files;		if ((retval = copy_sighand(clone_flags, p)))			goto bad_fork_cleanup_fs;		if ((retval = copy_signal(clone_flags, p)))			goto bad_fork_cleanup_sighand;		//复制进程的地址空间		if ((retval = copy_mm(clone_flags, p)))			goto bad_fork_cleanup_signal;		if ((retval = copy_keys(clone_flags, p)))			goto bad_fork_cleanup_mm;		if ((retval = copy_namespace(clone_flags, p)))			goto bad_fork_cleanup_keys;
  1. 在复制进程的地址空间中 (mm_struct结构体保存进程的地址空间)
    如果是产生一个线程,子线程与父线程共用一个地址空间,这里只要子进程的mm_struct 指针指向父进程的mm_struct 结构体即可。
    如果产生一个进程,需要新申请一块mm_struct 大小的空间,然后拷贝父进程的mm_struct,这里还不能一成不变的全部复制,因为父进程的逻辑页面可能已经在物理页面上有映射,所以要将这部分的内容改为未映射部分
    dump_mm(mm, oldmm)函数可以修改这部分的内容;
  2. 拷贝线程的执行信息 struct pt_regs * childregs; //保存用户态进程的上下文的信息
    childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1;	//复制父进程的用户态程序的上下文信息	*childregs = *regs;	//设置子进程地eax地值为0,这个是子进程的返回值	childregs->eax = 0;	//存放父进程用户态进程栈地起始位置	//这样子进程返回的时候,可以顺着父进程的执行位置开始往下执行	childregs->esp = esp;	/*	从esp->esp0的保存用户态的进程的上下文信息	*/	//设置子进程的esp	p->thread.esp = (unsigned long) childregs;	//子进程内核栈的起始位置	p->thread.esp0 = (unsigned long) (childregs+1);	//获取父进程的用户栈的偏移地址(eip)的值	p->thread.eip = (unsigned long) ret_from_fork;

从父进程中分出一半的时间片给子进程,然后将父进程与子进程的状态置为就绪态

当时间片轮到父进程与子进程执行的时候,会从两个进程的上下文信息中找到进程的执行位置,开始执行
子进程的eax寄存器的值为0 所以fork()的语句返回0,父进程的eax中保存的是子进程的pid 所以返回值为子进程的pid

mm_struct 结构体

struct mm_struct {
//mmap指向虚拟区间链表 struct vm_area_struct * mmap; /* list of VMAs *///指向红黑树 struct rb_root mm_rb;//指向最近的虚拟空间 struct vm_area_struct * mmap_cache; /* last find_vma result */// unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); void (*unmap_area) (struct mm_struct *mm, unsigned long addr); unsigned long mmap_base; /* base of mmap area */ unsigned long task_size; /* size of task vm space */ unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */ unsigned long free_area_cache; /* first hole of size cached_hole_size or larger *///指向进程的页目录 pgd_t * pgd;//空间中有多少用户 atomic_t mm_users; /* How many users with user space? *///引用计数;描述有多少指针指向当前的mm_struct atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) *///虚拟区间的个数 int map_count; /* number of VMAs */ struct rw_semaphore mmap_sem;//保护任务页表 spinlock_t page_table_lock; /* Protects page tables and some counters *///所有mm的链表 struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung * together off init_mm.mmlist, and are protected * by mmlist_lock */ /* Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. */ mm_counter_t _file_rss; mm_counter_t _anon_rss; unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ unsigned long total_vm, locked_vm, shared_vm, exec_vm; unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;//start_code:代码段的起始地址//end_code:代码段的结束地址//start_data:数据段起始地址//end_data:数据段结束地址 unsigned long start_code, end_code, start_data, end_data;//start_brk:堆的起始地址//brk:堆的结束地址//start_stack:栈的起始地址 unsigned long start_brk, brk, start_stack;//arg_start,arg_end:参数段的起始和结束地址//env_start,env_end:环境段的起始和结束地址 unsigned long arg_start, arg_end, env_start, env_end; unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ struct linux_binfmt *binfmt; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */ mm_context_t context; /* Swap token stuff */ /* * Last value of global fault stamp as seen by this process. * In other words, this value gives an indication of how long * it has been since this task got the token. * Look at mm/thrash.c */ unsigned int faultstamp; unsigned int token_priority; unsigned int last_interval; unsigned long flags; /* Must use atomic bitops to access the bits */ struct core_state *core_state; /* coredumping support */#ifdef CONFIG_AIO spinlock_t ioctx_lock; struct hlist_head ioctx_list;#endif#ifdef CONFIG_MM_OWNER /* * "owner" points to a task that is regarded as the canonical * user/owner of this mm. All of the following must be true in * order for it to be changed: * * current == mm->owner * current->mm != mm * new_owner->mm == mm * new_owner->alloc_lock is held */ struct task_struct *owner;#endif#ifdef CONFIG_PROC_FS /* store ref to file /proc/
/exe symlink points to */ struct file *exe_file; unsigned long num_exe_file_vmas;#endif#ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_mm *mmu_notifier_mm;#endif};

在这里插入图片描述

vm_area_struct 中包含虚拟地址块的起始地址,结束地址,和next指针。

每个进程并不是全部使用了4G的虚拟地址空间,可能仅仅使用了其中的很小的一部分,这时候,进程没有必要将所有的虚拟地址空间的地址都保存,只需要保存其中一部分即可。

当虚拟区间较少时,采用单链表即mmap管理这些虚拟区间;
当虚拟区间较多时,采用红黑树管理这些虚拟区间。

转载地址:http://tanwi.baihongyu.com/

你可能感兴趣的文章
基于Lucene查询原理分析Elasticsearch的性能(转)
查看>>
HttpClient请求外部服务器NoHttpResponseException
查看>>
springCloud升级到Finchley.RELEASE,SpringBoot升级到2.0.4
查看>>
Spring boot + Arthas
查看>>
omitted for duplicate jar包冲突排查
查看>>
如何保证缓存与数据库的双写一致性?
查看>>
java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy排查
查看>>
深浅拷贝,深浅克隆clone
查看>>
Java基础零散技术(笔记)
查看>>
Mysql优化sql排查EXPLAIN EXTENDED
查看>>
线程之间数据传递ThreadLocal,InheritableThreadLocal,TransmittableThreadLocal
查看>>
spring循环依赖,解决beans in the application context form a cycle
查看>>
分布式锁的实现
查看>>
解决POJO的属性首字母为大写,但是赋值不了的问题
查看>>
服务器运维整理(笔记)
查看>>
redis分布式锁在MySQL事务代码中使用,没控制好并发原因
查看>>
centos7中的网卡一致性命名规则、网卡重命名方法
查看>>
能切换环境的python
查看>>
Tmux 使用教程
查看>>
DLINK-DSN1100的安装使用记录
查看>>