
文章插图
唯一不同的是这些核心内存区域在 32 位机器和 64 位机器上的绝对位置分布会有所不同 。
那么在此基础之上 , 内核如何为进程管理这些虚拟内存区域呢?这将是本小节重点为大家介绍的内容~~
既然我们要介绍进程的虚拟内存空间管理 , 那就离不开进程在内核中的描述符 task_struct 结构 。
struct task_struct {// 进程idpid_tpid;// 用于标识线程所属的进程 pidpid_ttgid;// 进程打开的文件信息struct files_struct*files;// 内存描述符表示进程虚拟地址空间struct mm_struct*mm;.......... 省略 .......}在进程描述符 task_struct 结构中 , 有一个专门描述进程虚拟地址空间的内存描述符 mm_struct 结构 , 这个结构体中包含了前边几个小节中介绍的进程虚拟内存空间的全部信息 。每个进程都有唯一的 mm_struct 结构体 , 也就是前边提到的每个进程的虚拟地址空间都是独立 , 互不干扰的 。
当我们调用 fork() 函数创建进程的时候 , 表示进程地址空间的 mm_struct 结构会随着进程描述符 task_struct 的创建而创建 。
long _do_fork(unsigned long clone_flags,unsigned long stack_start,unsigned long stack_size,int __user *parent_tidptr,int __user *child_tidptr,unsigned long tls){......... 省略 .......... struct pid *pid; struct task_struct *p;......... 省略 ..........// 为进程创建 task_struct 结构 , 用父进程的资源填充 task_struct 信息 p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace, tls, NUMA_NO_NODE);......... 省略 ..........}随后会在 copy_process 函数中创建 task_struct 结构 , 并拷贝父进程的相关资源到新进程的 task_struct 结构里 , 其中就包括拷贝父进程的虚拟内存空间 mm_struct 结构 。这里可以看出子进程在新创建出来之后它的虚拟内存空间是和父进程的虚拟内存空间一模一样的 , 直接拷贝过来 。static __latent_entropy struct task_struct *copy_process(unsigned long clone_flags,unsigned long stack_start,unsigned long stack_size,int __user *child_tidptr,struct pid *pid,int trace,unsigned long tls,int node){struct task_struct *p;// 创建 task_struct 结构p = dup_task_struct(current, node);....... 初始化子进程 .................. 开始继承拷贝父进程资源.......// 继承父进程打开的文件描述符 retval = copy_files(clone_flags, p);// 继承父进程所属的文件系统 retval = copy_fs(clone_flags, p);// 继承父进程注册的信号以及信号处理函数 retval = copy_sighand(clone_flags, p); retval = copy_signal(clone_flags, p);// 继承父进程的虚拟内存空间 retval = copy_mm(clone_flags, p);// 继承父进程的 namespaces retval = copy_namespaces(clone_flags, p);// 继承父进程的 IO 信息 retval = copy_io(clone_flags, p);...........省略.........// 分配 CPUretval = sched_fork(clone_flags, p);// 分配 pidpid = alloc_pid(p->nsproxy->pid_ns_for_children);...........省略.........}这里我们重点关注 copy_mm 函数 , 正是在这里完成了子进程虚拟内存空间 mm_struct 结构的的创建以及初始化 。static int copy_mm(unsigned long clone_flags, struct task_struct *tsk){// 子进程虚拟内存空间 , 父进程虚拟内存空间 struct mm_struct *mm, *oldmm; int retval;...... 省略 ...... tsk->mm = NULL; tsk->active_mm = NULL;// 获取父进程虚拟内存空间 oldmm = current->mm; if (!oldmm)return 0;...... 省略 ......// 通过 vfork 或者 clone 系统调用创建出的子进程(线程)和父进程共享虚拟内存空间 if (clone_flags & CLONE_VM) {// 增加父进程虚拟地址空间的引用计数mmget(oldmm);// 直接将父进程的虚拟内存空间赋值给子进程(线程)// 线程共享其所属进程的虚拟内存空间mm = oldmm;goto good_mm; } retval = -ENOMEM;// 如果是 fork 系统调用创建出的子进程 , 则将父进程的虚拟内存空间以及相关页表拷贝到子进程中的 mm_struct 结构中 。mm = dup_mm(tsk); if (!mm)goto fail_nomem;good_mm:// 将拷贝出来的父进程虚拟内存空间 mm_struct 赋值给子进程 tsk->mm = mm; tsk->active_mm = mm; return 0;...... 省略 ......
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 神话中高要一步一步往上爬是第几集?
- 神话一步一步爬到最高是几集?
- 总以为她会永远陪我一步一步慢慢走是什么歌
- 爱情不会一步到位而是会经过颇多挫折才圆满的星座
- Dubbo 03: 直连式 + 接口工程
- 一篇文章带你了解热门版本控制系统——Git
- 一篇文章带你了解网页框架——Vue简单入门
- 一直在等待爱却不敢主动迈出一步的星座
- 我用canvas带你看一场流星雨
- 一篇文章带你掌握MyBatis简化框架——MyBatisPlus
