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函数调用中的值传递,这是 此后两者的修改都互不影响.因为两者的数据虽相同,但却在不同的 ...
随机推荐
- symfony生成路由
控制器里生成地址 $this->generateUrl('course_manage_show_test', array('id' => 1)) twig前端文件生成地址: {{ path ...
- Docker系列(5)- 常用命令(1) | 帮助命令
帮助命令 [root@localhost ~]# docker version #显示docker的版本信息 [root@localhost ~]# docker info #显示docker的系统信 ...
- CI框架 模糊查询,链表查询
$data = $this->db->from('flash_news') ->select('xx,xx,xx,xx') ->limit(2) ->like('tags ...
- 华为云计算IE面试笔记-华为云计算解决方案业务迁移支持哪些迁移?有哪些特点?请描述基本的业务交付流程、业务迁移流程和原则。
1. 迁移场景:华为云计算解决方案按照源端环境来说,支持P2V.V2V(P2V:物理设备(操作系统及其上的应用软件和数据)迁移到华为虚拟化平台.V2V:其他厂商的虚拟化平台迁移到华为虚拟化平台.)以及 ...
- SpringMVC的数据输出
使用 @Controller public class OutputController { @RequestMapping("/handle01") public String ...
- Javascript设计模式之原型模式、发布订阅模式
原型模式 原型模式用于在创建对象时,通过共享某个对象原型的属性和方法,从而达到提高性能.降低内存占用.代码复用的效果. 示例一 function Person(name) { this.name = ...
- Windows Terminal 美化教程
Windows Terminal 美化教程 1.安装Windows Terminal 在微软商店搜索Windows Terminal下载即可 2.安装相应的插件 使用管理员权限打开Windows Te ...
- Object.create 和 Object.assign
Object.assign(target, ...source) 1.Object.assign方法只会拷贝源对象自身(不包括原型)的并且可枚举的属性到目标对象,使用源对象的get和目标对象的set, ...
- javascript-jquery的ajax
用一个例子来说明: html部分 <form action="name1"> <input class="class1" type=&quo ...
- 力扣 - 剑指 Offer 45. 把数组排成最小的数
题目 剑指 Offer 45. 把数组排成最小的数 思路1 将整数数组转化成字符串数组 然后使用Arrays工具类的sort方法帮助我们排序 代码 class Solution { public St ...