fork到底做了什么?

       在 linux 中 fork 是一个非常重要的函数,它从一个已经存在的进程中创建出了一个新进程,这个新进程称为子进程,而原进程称为父进程。

为什么 fork 函数看起来像是返回了两次?

       对于很多初学者来说,这是最不能理解的。的确,这只能说是看起来像,因为从汇编的层面就已经决定了栈的压栈和出栈是成对出现的,一个函数永远只会返回一次。fork 函数的返回两次只是一个表面现象,那本质上是怎么回事呢?

       fork 函数调用之后,就会为子进程创建一个新的 pcb,子进程就会完全拷贝父进程的地址空间,包括堆、栈、代码段都被拷贝了过来,因此,子进程的栈是从父进程那拷贝过来的,那么它和父进程的函数栈帧就是完全相同的,在函数执行完毕后,父子进程在各自的函数栈帧中返回,造成了我们所看到的两次返回的假象。

fork 后父子进程谁先执行?

       通常情况下,我们总是用 sleep 等操作来保证另一个进程先执行,但父子进程谁先执行并不是不可预测的。从linux内核2.6.32开始,在默认情况下,父进程将成为fork之后优先调度的对象。采取这种策略的原因很简单:fork是父进程发起的调用,因此fork之后,父进程在CPU中处于活跃的状态,并且其内存管理信息也被置于硬件内存单元的转译后备缓冲器(TLB),所以先调度父进程无论从减少上下文切换、CPU让出等方面都可以提高性能。linux内核从2.6.24开始,内核采用完全公平调度(CFS),用户创建的普通进程,都采用CFS调度策略。对于CFS调度策略,内核提供的控制选项默认是0,表示父进程优先获得调度,如果该值被改为1,子进程会优先获得调度。

       但POSIX标准和linux都没有保证会优先调度父进程,因此在应用中,我们不能对父子进程的执行顺序做任何假设。如果确实需要父子进程的某一特定执行顺序,那么还是得需要进程间的同步手段。

写时拷贝

       fork 函数最常规的用法:一个父进程希望复制自己,使父子进程同时执行不同的代码,这时就需要在子进程中调用 exec 族函数来使子进程丢弃当前拷贝父进程的所有代码段,并创建新的代码段和堆栈。在这种情况下如果还让子进程在一开始就完全拷贝父进程的地址空间,显然是一种很不明智的做法,因此linux引入了写时拷贝(copy-on-write)的方法。

       写时拷贝,从字面上来理解就是写的时候才拷贝,具体来说就是指子进程 pcb 的页表指向与父进程相同的物理内存页,这样就只拷贝父进程的页表项即可,但这个时候内存中的所有内容都是只读的,如果父子进程都对内存进行只读操作,那么这个状态就可以一直保持下去,一旦有一方想要对内存进行写操作,就会引发缺页异常,此时,内核才会为子进程申请一个新的物理页,并将原物理页中的内容真正的复制到新的物理页中,让父子进程真正拥有各自的物理内存页,此时他们各自内存中的内容就是可写的了。

   

### Git 中 Fork 的概念及作用 Fork 是 Git 和 GitHub/GitLab 等代码托管平台中一个非常重要的功能,它为开源项目的协作提供了简单而高效的方式[^1]。通过 Fork,用户可以创建一个项目的新副本到自己的账户下,这个副本与原始项目保持关联关系,但又独立存在。 #### 1. Fork 的基本概念 Fork 的本质是复制一个仓库到用户的个人空间中。当用户对某个开源项目感兴趣并希望贡献时,可以通过 Fork 创建属于自己的项目副本[^3]。此时,用户可以在自己的副本上自由地进行修改、开发和测试,而不会影响原始项目的稳定性。这种机制确保了项目的维护者能够控制哪些更改可以被合并到主项目中。 #### 2. Fork 的主要用途 Fork 的主要用途包括但不限于以下几点: - **参与开源项目**:开发者可以通过 Fork 将感兴趣的开源项目复制到自己的账户中,在此之上进行改进或修复问题。完成修改后,可以向原项目提交 Pull Request (PR),请求将改动合并回主项目[^3]。 - **安全的实验环境**:对于想要尝试新功能或重大改动的开发者来说,Fork 提供了一个安全的实验环境,避免直接对原项目造成破坏性影响[^4]。 - **学习和研究**:新手开发者可以通过 Fork 深入了解其他人的代码结构和实现细节,从而提升自己的编程能力[^1]。 #### 3. Fork 的工作流程 以下是使用 Fork 进行协作开发的基本流程: - **Fork 仓库**:在目标项目的页面点击“Fork”按钮,生成一份属于自己的副本。 - **克隆本地**:将 Fork 后的仓库克隆到本地环境中进行开发操作[^2]。 - **同步上游更新**:如果原项目有新的更新,可以通过添加 upstream 远程仓库地址,并执行 `git fetch upstream` 获取最新更改。然后使用 `git rebase` 或 `git merge` 将这些更改整合进本地分支。 - **提交 Pull Request**:当开发者完成了自己的修改后,可以通过 GitHub/GitLab 界面发起 Pull Request 请求,通知项目维护者审核并决定是否合并改动[^4]。 ```bash # 添加上游仓库 git remote add upstream [email protected]:x/back-end/x.git # 获取上游仓库的最新更改 git fetch upstream # 将更改合并到本地分支 git merge upstream/main ``` #### 4. 注意事项 尽管 Fork 提供了极大的便利性,但在实际使用过程中仍需注意以下几点: - **定期同步上游仓库**:为了避免代码差异过大导致冲突,建议定期从上游仓库拉取最新的更改[^3]。 - **合理管理远程仓库**:随着参与项目的增多,可能会出现多个远程仓库的情况,因此需要明确区分不同仓库的作用[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值