fork剖析小结

来源:互联网 发布:阿里妈妈淘宝客安全吗 编辑:程序博客网 时间:2024/05/22 01:42
fork是完全复制
     clone则是将资源有选择的复制给子进程,没有复制的资源通过指针的复制让子进程共享,其中的flag用户可以进行设置来进行选择复制共享
     vfork 除了task_struct结构和系统空间堆栈以外的资源全部都通过数据结构复制的继承
     子进程复制了父进程当前的寄存器值,将子进程加入到进程调度的就绪队列,父子进程拥有相同的页表,然后映射到相同的物理页面
     创建内核线程的函数kernel_thread,实际上只是对clone的包装,并不能调用execve执行一个可执行映像文件,只能执行内核的某一个函数

fork,vfork,clone都是调用do_fork来实现进程线程的创建的。几种系统函数的传值不同,主要体现在flag上,regs上携带一些数据子进程需要进行拷贝

进程和线程,线程可以通过指针来共享父进程中的资源,而一个进程要有其必有的一些结构

//思考   进程和线程的编程中应有的区别??
//    1.进程和线程的pid,tid来自不同的地方                //错误假设
//    2.进程有自己的虚拟地址空间,而线程是共享进程的


源码剖析过程:
首先进入do_fork之后,首先获得一个pid
然后进入关键的函数copy_process函数
/////////copy_process /////////////
进入dup_task_struct,调用alloc_task_struct申请空间,再调用alloc_thread_info申请空间一部分用于数据结构一部分用于系统堆栈,全面的复制父进程中关于task_Struct和thread_info中的元素进入新开辟的空间中,并且将子进程的这两个结构中互指指针进行重新赋值
然后返回到copy_process,对子进程中一些元素进行重新赋值
    //该函数用来创建task_struct,并且其中有的数据结构是自己申请内存然后复制父进程,有的
    //是与父进程共享指针来实现共享,线程和进程,所拥有的资源是不一样的,所以在其中,如果
    //是创建线程,肯定要创建自己的虚拟内存映射页内存,然后复制所需要的页表内容,并且将其
    //设置成为只读模式,如果要写的话就进行写时拷贝
然后开始了几个重要的底层复制与共享设置
如果传入的参数clone_flags设置为1,则就不进行复制而进行共享
copy_semundo
copy_files复制父进程中打开的文件描述符
首先,查看clone_files是否设置,如果设置就增加当前文件的共享计数,否则进行复制这里的复制与共享,复制后lseek,子进程和父进程的互不干扰,但是仍然指向一个文件,如果是共享的,两个都进行lseek,则会是最后一次设置的为准
copy_fs
copy_sighand
copy_signal
copy_mm
    若是创建的进程必有其独立的虚拟地址空间,若是线程则是共享父进程的地址空间
    进入之后,先来判断clone_vm是否设置,若是设置就直接将这个指针指向父进程的mm来通过指针实现共享
    若是进程就会进行深层次的复制,首先allocate_mm开辟空间,然后复制父进程的元素到子进程中
    然后进入dup_mmap进行深层次的设置
    首先这里面的vm_area_struct
    首先这里面会有一个链表将使用到的虚拟地址空间按照使用权限和其他属性来从低地址到高地址连成链表
    这2.6.11.12中有看到红黑树,在老的版本中看到的是avl树
    进行复制,复制父进程中的每一个vm_area_struct线性区描述符,并把复制品插入到子进程的线性区链表和红黑树
    在这其中进行也页面的设置为只读,然后可以进行写时拷贝
     一个task_struct中有两个mm_struct
copy_key
copy_namespace
copy_thread,复制父进程中的系统堆栈,使得子进程能够沿着父进程进入的路线进行返回
然后通过其它设置及将子进程加入到调度队列,子进程就可以进行返回了
///////////////////////////////copy_process////////////////////////////

这里在vfork的时候,子线程必须要先父进程一步运行
返回到do_fork


5.写时拷贝<父进程通过fork产生了子进程,子进程继承了父进程中的资源,包括打开的文件,锁,并且和父进程共用代码段,数据段,堆栈段,这些都是通过复制父进程的地址映射表项实现的,并且将其权限设置为只读模式,所以两者能共用相同的物理内存。当其中有某一个进程要进行写操作的时候,就会触发缺页异常,操作系统为其申请新页面,由于OS将内存划分为页单位,每页的大小一定,所以父子进程之间对于读写的地址的偏移是一样的,进而开始进行写操作>

6.进程和线程的区别
<
     1.线程是调度和分派的基本单元,线程是能独立运行的基本单位。
     2.拥有资源,进程是资源分派的基本单元,线程可以共享进程的资源。
     3.线程支持多处理机系统,如果是多线程的话可以分派到不同的cpu上,但是单进程只能绑定到单核上。
     4.进程之间的独立性大于线程之间的独立性,每个进程拥有一个独立的地址空间和其它资源,而线程是共享进程内部的资源的。一个线程的堆栈也可以被其它线程读写。
     5.线程的并发性。
     6.系统开销,线程进程创建销毁的开销进程线程切换的效率大小不同,进程的效率是线程的5倍之多
>
线程的开创重要的原因之一就是其并发性好

多进程和多线程的区别:
<
1.线程共享进程的数据,所以线程之间的通信十分的方便,但是线程之间共享导致竞态资源,所以需要同步
2.fork创建子进程相对于线程的创建的消耗较大,进程之间的切换消耗大于线程之间的切换
3.多线程之间由于共享数据,其独立性差,而进程是拥有独立的地址空间,独立的资源,所以在资源管理上有很大优势
>

内核线程和用户线程的区别<操作系统是否能感知到线程的存在>
1.内核线程
    <1>接受操作系统的调度
    <2>切换需要占用较多的系统资源,需要用户态内核态的切换
    <3>在用户线程中一个线程的阻塞会引起整个进程的阻塞,而内核线程中一个线程的阻塞不会引起其它线程的阻塞
     <4>内核线程与用户线程存在一对一,一对多,多对多的关系
线程和进程的关系:
     在fork之初,先通过共享指针的方式共享了父进程的所有资源,然后再在copy_process中进行标志的判断,看是否需要深层次的拷贝,如果深拷贝那么就是独立的进程,否则通过设置为只读模式来实现写时拷贝技术。
     task_struct中有两个mm_struct结构,一个mm,一个是active_mm,而mm指向的是该进程的虚拟地址空间,而mm_active指向的上一个进程的用户空间,如果该线程是一个内核线程指向的内核中的代码,就不需要专门指向用户空间的虚存空间,所以就共享上一个进程的虚存空间,因为其只使用的内核的代码,每个进程都有内核区的地址映射表

fork是进行完全的复制,创建出来一个新的进程,其中有与父进程共享的,有不同于父进程的,访问相同的数据和代码,但是有自己的堆栈。
vfork创建之初使用的是和父进程共享内容,共享父进程的代码,数据和堆栈,所以在vfork调用之前只能是父子进程中的一个进程运行,否则的话两个进程在相同的堆栈上写数据,则会导致进程错乱。
vfork是为了克服fork的的全面复制,如果一个程序在fork之后调用了exec,则其所要复制的内容都会被丢弃,所以为了减少fork的复制,创建vfork,则就会直接调用exec然后成立一个新的进程,与父进程的一切无关了。

     vfork保证子进程先运行,在其调用exec或者exit之后父进程才可能被调度运行,如果在调用这两个函数之前子进程依赖父进程的进一步动作,则会导致死锁。
     子进程在vfork返回后直接运行在父进程的栈空间,并且使用父进程的内存和数据,这意味着子进程可能破坏父进程的数据结构或者栈,造成失败。

     为了避免这些问题,需要确保一旦调用 vfork() ,子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用 exit 函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续

进程和线程:
     Linux下的线程是用进程模拟的,所以Linux下的线程和进程的创建方式相同,且Linux下用户线程和内核线程是一对一的,在进程的调度机制中,线程在相同的调度队列中
原创粉丝点击