设为首页 - 加入收藏 焦点技术网
热搜:java
当前位置:首页 >

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

2012-07-06 23:33:00.0 编程 存储  
导读:    同事最近调程序,遇到一个非常诡异的现象。一个全局变量,在没任何其他赋值的情况下,自己就莫名其妙地发生了改变。开始怀疑是消息队列出了问题。因为该程序与另一个庞大的程序通过消息队列进行通信。消息队列调试了N天,无果。    又开始怀疑是不是内存或堆栈出错。于是仔细找了所有的数组、变量及malloc函数,还是没发现有不对的地方。况且,每次该变量值变化的一模一样,并不像内存泄露导致的程序异常。  ...。。。

    同事最近调程序,遇到一个非常诡异的现象。一个全局变量,在没任何其他赋值的情况下,自己就莫名其妙地发生了改变。开始怀疑是消息队列出了问题。因为该程序与另一个庞大的程序通过消息队列进行通信。消息队列调试了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. 丰富的编程经验也是必不可少。



(编辑: flylonginsky)

网友评论
相关文章