一个fork引起的“穿越”事件。

本文记述了一位程序员解决一个全局变量在没有任何赋值操作下自行改变的问题的过程。通过逐步排查,最终发现是因为子进程未退出,导致了全局变量状态在父进程中发生不可预期的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    同事最近调程序,遇到一个非常诡异的现象。一个全局变量,在没任何其他赋值的情况下,自己就莫名其妙地发生了改变。开始怀疑是消息队列出了问题。因为该程序与另一个庞大的程序通过消息队列进行通信。消息队列调试了N天,无果。

    又开始怀疑是不是内存或堆栈出错。于是仔细找了所有的数组、变量及malloc函数,还是没发现有不对的地方。况且,每次该变量值变化的一模一样,并不像内存泄露导致的程序异常。

    百思不得其解。

    后来,经过漫长的跟踪,屏蔽了其他很多语句后,终于发现了蛛丝马迹。在某一个语句后,一个print语句执行了两遍。该语句前有个fork函数,它启动了一个子进程,该子进程仅仅进行了一个写文件的操作。仔细一看,fork的子进程没有退出语句。

pid = fork();  
        // son process create here, exact copy from parent process  
        // son process will execute the main funciton after parent process  
        if(pid<0) {  
                printf("return");  
                return 0;  
        }  
        if(pid==0) {  
              。。。。
        }else {  
                。。。
        }  
      现在很多实现并不执行一个父进程数据段、栈和堆的完全复制。作为替代,使用写时复制(Copy-On-Write,COW)。这些区域由父子进程共享,而且内核将它们的访问权限改变为只读。如果父子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统的一页。没有退出,则子进程与父进程一起继续运行,一前一后。所以,导致父进程对全局变量赋值为1,而下一时刻子进程运行到这里,检查子进程的内存区域该变量为0,于是该变量前一时刻为0,后一时刻又变成了1,最终导致程序结果哭笑不得。

      这就像《黑衣人3》里J穿越到过去,在最后阿波罗升空的时刻,在野兽杀他的时候又进行了数次的穿越,使得野兽始终在那里杀他又杀不死他,J的状态就是一会儿生,一会儿死。又像周星驰在《大话西游》里月光宝盒进行了几次穿越,看到白晶晶一会儿生一会儿死。当然外国的时空穿越器比中国的要可靠,月光宝盒在进行了两次穿越后出了故障到了五百年前。

      好了,耗时两周多的怪异现象终于有了定论。我们可以总结出几点经验:

    1. 调试程序要学会抽丝剥茧一样,排除不想干的元素,使得问题简单化;

    2. 学会定位。定位到某行代码,更能帮助找到原因所在。

    3. 丰富的编程经验也是必不可少。



这是一个小作业,我是初学者,应该如何下手?附件的web是一个网页,(我对这一窍不通。目录下有css data images img js about.html contack.html index..html work.html)分别使用多线程,多进程,IO多路复用实现一个简单的web server(端口80)承载附件的web。对比这三种实现方式各自的优缺点。 Ps:Web server和普通socket通信的区别主要在于:多了字段的解析和处理。 多线程:优点:线程间数据共享方便、线程切换开销小、可以充分利用多核处理器优势;缺点:线程间同步和互斥需要额外开销容易引起死锁、线程数量过多时系统资源开销大、可能存在线程间的资源竞争 多进程:优点:进程间相互独立不会竞争资源、可以更好利用多核处理器、稳定性高;缺点:切换开销大、系统资源消耗大 IO多路复用:优点:单线程可同时处理多个IO请求,提高了并发性、节省系统资源、实现高效的事件驱动编程;缺点,计算密集型任务中IO多路复用效率可能较低、编程复杂性高 课题目的: 1. 熟悉公司的开发设计流程 2. 熟悉HTTP协议 3. 熟悉并掌握Linux环境应用开发 4. 熟悉socket网络编程 5. 加强多线程/多进程编程能力 6. 熟悉经典IO复用设计模型 课题要求: 1、实现以多线程方式实现web server功能(必修) 2、实现以多进程方式实现web server功能(必修) 3、实现以IO多路复用方式实现web server功能(必修) 4、提供完善Debug机制(必修) 5、Web server需完成get post等基础请求处理(必修) 6、如何使其具备高并发性并实现(选修) 7、使用makefile构建(必修) 前端见“实验-httpd实现”的web部分 浏览器兼容要求:Firefox,chrome、ie8+
08-08
### 如何同步和管理 GitHub 上的 fork 项目 #### 同步 fork 项目的流程 为了保持 fork 的项目与原始仓库的一致性,可以按照以下方法来实现同步: 1. **添加上游仓库 (Upstream Repository)** 首先,在本地克隆自己的 fork 仓库后,需要将原始仓库设置为 `upstream` 远程地址。这一步是为了方便后续拉取原始仓库中的最新更改[^2]。 ```bash git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git ``` 2. **获取并合并上游更新** 使用以下命令从上游仓库抓取最新的更改,并将其合并到本地分支中[^3]。 ```bash git fetch upstream git checkout main git merge upstream/main ``` 3. **推送更新至个人 fork 仓库** 完成上述操作后,可以通过以下命令将自己的修改推送到 fork 的远程仓库。 ```bash git push origin main ``` #### 提交 Pull Request (PR) 当希望向原始仓库贡献代码时,可通过 PR 实现这一目标。以下是具体的操作步骤: 1. 创建一个新的分支用于开发功能或修复 Bug。 ```bash git checkout -b feature-branch-name ``` 2. 在该分支上完成必要的改动并提交。 ```bash git commit -m "Add new functionality" ``` 3. 将此分支推送到 fork 的远程仓库。 ```bash git push origin feature-branch-name ``` 4. 访问 GitHub 页面,通过比较工具发起一个针对原始仓库的 PR 请求[^4]。 #### 常见注意事项 - 如果遇到冲突情况,则需手动解决这些冲突后再继续执行合并操作。 - 确保每次提交前都已成功同步了最新的上游版本,这样能减少不必要的麻烦[^1]。 ```python # 示例 Python 脚本展示如何自动化部分同步过程 import os def sync_fork(): commands = [ "git fetch upstream", "git checkout main", "git merge upstream/main", "git push origin main" ] for cmd in commands: result = os.system(cmd) if result != 0: print(f"Error occurred while running {cmd}") break sync_fork() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值