fork函数详解(附代码)
虽然篇幅很长,但大多是易懂的代码,不用担心看不完
这里的所有操作,都将在下面的代码中有所体现
fork会拷贝当前进程的内存,并创建一个新的进程。如上图,fork函数会将整个进程的内存镜像拷贝到新的内存地址,包括代码段、数据段、堆栈以及寄存器内容。之后,我们就有了两个拥有完全一样内存的进程。fork系统调用在两个进程中都会返回,在父进程中,fork系统调用会返回子进程的pid。而在新创建的进程中,fork系统调用会返回0。所以即使两个进程的内存是完全一样的,我们还是可以通过fork的返回值区分旧进程和新进程。
某种程度上来说这里的拷贝操作浪费了,因为所有拷贝的内存都被丢弃并被exec替换。在大型程序中这里的影响会比较明显。实际上操作系统会对其进行优化。(比如使用COW(copy on write)技术)
fork创建的新进程从fork语句后开始执行,因为新进程也继承了父进程的PC程序计数器。
在xv6中唯一不是通过fork创建进程的场景就是创建第一个进程的时候,之后的所有进程都是通过fork创建的。
以下内容以xv6(教学用Linux操作系统)源代码为例
代码解析
// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
{
int i, pid;
struct proc *np;
//获取当前进程(进程只运行在用户态)
//fork函数是系统调用,实际上运行在内核态,用户态的寄存器内容、内存状态都会被保留下来,所以不用担心fork函数的运行影响原进程
struct proc *p = myproc();
// Allocate process.
//allocproc实际上从操作系统进程队列中找到一个未在使用状态的空进程,
//为其分配trapframe(用来存储寄存器内容),page table页表,并设置了一些寄存器状态。代码见附
if((np = allocproc()) == 0){
return -1;
}
// Copy user memory from parent to child.
//将父进程的页表内容拷贝到子进程页表中
//uvmcopy user virtual memory copy
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
}
np->sz = p->sz;
// copy saved user registers.
//将父进程的寄存器内容拷贝到子进程中
*(np->trapframe) = *(p->trapframe);
// Cause fork to return 0 in the child.
//a0为返回值寄存器,fork函数结束会将a0的值返回,因此子进程fork的返回值为0
np->trapframe->a0 = 0;
// increment reference counts on open file descriptors.
//获取父进程打开的文件描述符
for(i = 0; i < NOFILE; i++)
if(p->ofile[i])
np->ofile[i] = filedup(p->ofile[i]);
np->cwd = idup(p->cwd);
safestrcpy(np->name, p->name, sizeof(p->name));
//在这里实现了父进程fork返回子进程pid
pid = np->pid;
//下面设置子进程的父进程,将子进程的状态设置为可运行
//之后,操作系统会在进程调度时,执行子进程
release(&np->lock);
acquire(&wait_lock);
np->parent = p;
release(&wait_lock);
acquire(&np->lock);
np->state = RUNNABLE;
release(&np->lock);
return pid;
}
附:
alloproc代码
static struct proc*
allocproc(void)
{
struct proc *p;
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == UNUSED) {
goto found;
} else {
release(&p->lock);
}
}
return 0;
found:
p->pid = allocpid();
p->state = USED;
// Allocate a trapframe page.
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
// An empty user page table.
p->pagetable = proc_pagetable(p);
if(p->pagetable == 0){
freeproc(p);
release(&p->lock);
return 0;
}
// Set up new context to start executing at forkret,
// which returns to user space.
memset(&p->context, 0, sizeof(p->context));
p->context.ra = (uint64)forkret;
p->context.sp = p->kstack + PGSIZE;
return p;
}
Trapframe代码:
struct trapframe {
/* 0 */ uint64 kernel_satp; // kernel page table
/* 8 */ uint64 kernel_sp; // top of process's kernel stack
/* 16 */ uint64 kernel_trap; // usertrap()
/* 24 */ uint64 epc; // saved user program counter
/* 32 */ uint64 kernel_hartid; // saved kernel tp
/* 40 */ uint64 ra;
/* 48 */ uint64 sp;
/* 56 */ uint64 gp;
/* 64 */ uint64 tp;
/* 72 */ uint64 t0;
/* 80 */ uint64 t1;
/* 88 */ uint64 t2;
/* 96 */ uint64 s0;
/* 104 */ uint64 s1;
/* 112 */ uint64 a0;
/* 120 */ uint64 a1;
/* 128 */ uint64 a2;
/* 136 */ uint64 a3;
/* 144 */ uint64 a4;
/* 152 */ uint64 a5;
/* 160 */ uint64 a6;
/* 168 */ uint64 a7;
/* 176 */ uint64 s2;
/* 184 */ uint64 s3;
/* 192 */ uint64 s4;
/* 200 */ uint64 s5;
/* 208 */ uint64 s6;
/* 216 */ uint64 s7;
/* 224 */ uint64 s8;
/* 232 */ uint64 s9;
/* 240 */ uint64 s10;
/* 248 */ uint64 s11;
/* 256 */ uint64 t3;
/* 264 */ uint64 t4;
/* 272 */ uint64 t5;
/* 280 */ uint64 t6;
};
fork函数详解(附代码)的更多相关文章
- Linux环境fork()函数详解
Linux环境fork()函数详解 引言 先来看一段代码吧, 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include ...
- 【转】linux 中fork()函数详解
在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...
- Linux中fork()函数详解(转载)
linux中fork()函数详解 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事, ...
- fork函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- Linux C 中 fork() 函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同 ...
- fork()函数详解
原文链接:http://blog.csdn.net/jason314/article/details/5640969 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函 ...
- Linux中fork()函数详解(转)
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- 【Linux 进程】fork函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- linux中fork函数详解(转)
add by zhj: 在Linux,创建进程是用fork(),它其实就是拷贝父进程的数据段和其它数据,这相当于C函数调用中的值传递,这是 此后两者的修改都互不影响.因为两者的数据虽相同,但却在不同的 ...
随机推荐
- linux设置防火墙规则-指定ip的访问权限
vim /etc/sysconfig/iptables 可以把你当前的iptables规则放到/etc/sysconfig/iptables中,系统重启iptables时自动执行 1.查看 iptab ...
- mac的vssh用sftp连不上,报unexpected SSH2_MSG_UNIMPLEMENTED packet
- javascript 享元模式 flyweight
* 适应条件 ** 一个程序中使用了大量的相似对象 造成大的内存开销 ** 对象的大多数状态都可以变为外部状态 ** 剥离出对象的外部状态之后, 可以使用相对较少的共享对象取代大量对象 * 上传文件的 ...
- Lucene基础入门
1. 数据的分类 结构化数据: 查询方法 数据库 非结构化数据: 查询方法 : (1)顺序扫描法 : 一行一行的看,从头看到尾 (2)全文检索 : 将一部分信息提取出来,重新组织将其变得 ...
- django 模版-标签-视图-csrf-token-模版继承-HTML过滤器
""" ******模版****** --定义模版-- **变量** 视图传递给模版的数据 注意1:要遵守标识符规则 语法:{{var(即变量)}} 如果使用的变量不存在 ...
- 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载 | 百篇博客分析OpenHarmony源码 | v65.01
百篇博客系列篇.本篇为: v65.xx 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- MySQL技术专题(X)该换换你的数据库版本了,让我们一同迎接8.0的到来哦!(初探篇)
前提背景 MySQL关是一种关系数据库管理系统,所使用的 SQL 语言是用于访问数据库的最常用的标准化语言,其特点为体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,在 Web应用方面 MySQ ...
- 浅尝装饰器-@staticmethod 和@classmethod
[写在前面] 本帖归属于装饰器单元的学习,可以点击关键词'装饰器'查看其他博文讲解 [正文部分] 说到装饰器一开始我觉得很陌生,看了一下别人的博客讲解,原来以前学习遇到的静态方法@staticmeth ...
- Java内存分析--栈--堆
Java内存分析--栈--堆 JVM的内存分析: 1.栈内存 1.连续的存储空间,遵循后进先出的原则. 2.每个线程包含一个栈区,栈区只保存基础数据类型的对象和自定义对象的引用. 3.每个栈中的数据都 ...
- pycharm中的terminal和Windows命令提示符有什么区别?二者用pip安装的包是不是位于相同位置?
那要看pycharm使用了什么shell,可以在设置->工具->终端里查看shell path.如果使用的是cmd.exe那就没区别.pycharm终端和Windows命令提示符用pip安 ...