linux内核—进程&线程创建

1.进程与线程创建

fork调用结束时,在返回点这个想用位置上,父进程恢复执行,子进程开始执行,fork从内核返回两次,一次回到父进程,一次回到子进程。
进程调用exit退出,退出后被设置为僵死状态,直到他的父进程调用了wait或waitpid
每个进程有自己的地址空间,如果父进程希望和子进程共享地址空间,可以在调用clone()时,设置CLONE_VM标志,我们把这种进程称为线程
是否共享地址空间几乎是线程和进程本质上唯一的区别
调用fork时,子进程调用allocate_mm()申请mm_struct结构体的空间(即每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间),然后利用copy_mm()复制父进程内存描述符;而线程则在创建时,调用clone(),并设置CLONE_VM标志,这样内核就不会在调用allocate_mm函数申请mm_struct空间,仅需要调用copy_mm()将mm域指向其父进程的内存描述符即可。
综上:子进程会申请自己的进程地址空间,并复制父进程地址空间里的内容;而子线程不会申请自己的地址空间,而是共享其父进程的进程地址空间

2. 写时拷贝技术

  1. 创建pcb
  2. 拷贝父进程中的pcb的数据(拥有相同的虚拟地址空间,相同的页表)
  3. 父子进程一开始映射同一块物理内存
  4. 等到物理内存修改时,才为子进程重新开辟内存,拷贝数据。

写时拷贝技术例
在这里插入图片描述
一开始的时候,通过PCB描述的进程1和进程2上的变量val都是100,在进程1和进程2的虚拟地址空间上都是相同的位置,所以我们在程序中看到的变量val的地址相同,其实是虚拟地址,假地址。此时通过页表映射到物理内存中的相同位置,但是当进程1改变val值的时候,就不一样了。
在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int okc=0;
int main()
{
    
    
    int ret=fork();
    if(ret<0){
    
    
        perror("fork");
        return 1;
    }
    else if(ret==0){
    
    
        okc=100;
        printf("child:%d %d %p\n",getpid(),okc,&okc);//子进程先退出,修改了okc的值
    }
    else
    {
    
    
        sleep(3);
        printf("father:%d %d %p\n",getpid(),okc,&okc);
    }
    return 0;
}

在这里插入图片描述
结果:可以看出来变量的地址相同的,但是值是不同的,说明变量在两个进程地址空间中的地址是相同的,但是他们映射在物理地址上的地址是不同的。

猜你喜欢

转载自blog.csdn.net/chengcheng1024/article/details/114408095