1

进程/线程虚拟地址空间

进程/线程先申请虚拟地址空间4G,其中1G的内核空间是所有普通进程/线程共享的。每个在创建的时候在内核栈底部申请一个thread_info,进程通过fork,线程clone(区别是传入一些共享的东西)。thread_info 结构体中有一个 struct task_struct task,task 指向的就是这个进程或线程相关的 task_struct 对象。thread_info 结构体中有一个 struct task_struct task,task 指向的就是这个进程或线程相关的 task_struct 对象。

clipboard.png

task_struct描述进程/线程虚拟空间位置信息等。

volatile long state;//进程状态
void *stack; //内核栈
struct thread_info *thread_info。thread_info 指向该进程/线程的基本信息。
struct mm_struct *mm。mm_struct 对象用来管理该进程/线程的页表以及虚拟内存区。
struct mm_struct *active_mm。主要用于内核线程访问主内核页全局目录。
struct fs_struct *fs。fs_struct 是关于文件系统的对象。
struct files_struct *files。files_struct 是关于打开的文件的对象。
struct signal_struct *signal。signal_struct 是关于信号的对象。
pid_t pid;  
pid_t tgid;  
……

整体虚拟内存图示:
clipboard.png

局部变量、函数参数、返回地址等
动态分配的内存(有碎片,brk或mmap申请,内存池/垃圾回收)
BSS段 未初始化或初值为0的全局变量和静态局部变量
数据段 已初始化且初值非0的全局变量和静态局部变量
代码段 可执行代码、字符串字面值、只读变量
mmap 高效的文件I/O方式, 因而被用于装载动态共享库

mm_struct:

clipboard.png

线程私有:栈,寄存器,线程局部存储TLS(线程级别全局变量,寄存器不会用来存储需要的数据空间很宝贵)。公用:代码,数据,进程空间,打开文件。
线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID,一组寄存器值,栈,调度优先级和策略, 信号屏蔽字,errno变量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本,程序的全局内存和堆内存,栈以及文件描述符,所以线程的mm_struct *mm指针变量和所属进程的mm指针变量相同。
在创建线程的时候,可以通过pthread_attr_t来初始化线程的属性,包括线程的栈布局信息,如栈起始地址stackaddr, 栈大小stacksize。线程栈的空间开辟在所属进程的堆区,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。

fork/exec系统调用

系统调用:用户态切换到内核态。用中断实现
中断号,中断处理程序 一一对应,维护在中断向量表。中断号有限,一般用一个号(linux 是0x80)表示所有系统调用,再到系统调用表中根据系统调用号获取

clipboard.png

fork

fork过程:
使用fork函数创建的子进程从父进程的继承了全部进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
1.fork创建子进程,首先调用int80中断,然后将系统调用号保存在eax寄存器中,进入内核态后调用do_fork(),
2.申请系统堆栈,将子进程pcb插入到队列中(task_struct到内核栈?)
3.创建一份父进程的拷贝,他们的内存空间里包含了完全相同的内容,包括当前打开的资源,数据,当然也包含了程序运行到的位置,也就是说fork后子进程也是从fork函数的位置开始往下执行的,而不是从头开始。
4.对子进程资源初始化
5.为了判别当前正在运行的是哪个进程,fork函数返回了一个pid,在父进程里标识了子进程的id,在子进程里其值为0,在我们的程序里就根据这个值来分开父进程的代码和子进程的代码。
写时复制:虽然父子是两份,但只有改时才会变两份

exec

exec。当前进程替换为新的elf(可执行文件)
若想并存:

#include <stdio.h>
#include <unistd.h>
int main(){
  if(!fork())
    execve("./test",NULL,NULL);
  else
    printf("origin process!\n");
  return 0;
}

elf为程序编译链接后的,加载到进程虚拟内存的栈task_struct、各种segment中。进程有自己的虚拟内存


梦想家
107 声望76 粉丝

引用和评论

0 条评论