“你还有什么要说的吗?没有的话我就去做了。”kill程序终于开口问道。这一次,我没有再回答。看到老哥扔刀杀人,我短暂的生命就这样结束了……我是一个网络程序,长期运行在Windows系统上,日子过得很惬意。但是前段时间,程序员告诉我,要移植我运行在linux系统下,需要对我进行大手术,平静的生活就这样被打破了。当我来到这个叫Linux的地方运行时,一切对我来说都是很陌生的。不再是熟悉的C盘、D盘、E盘,取而代之的是各种目录。/bin/boot/etc/dev/mnt/opt/proc/home/usr/usr64/var/sys...这里有意思,一切都是文件,硬件设备是文件,管道是文件,网络套接字也是文件,这让我很不舒服。这些都还好,我还能接受,但是直到今天...奇怪的fork今天早上,我收到了一个网络请求,需要完成一个功能。这项工作很耗时。我准备创建一个子流程让兄弟来完成。这是我第一次在Linux系统上创建进程。我有点困惑。看了半天,只看到程序员在我的代码里写了一个fork函数:pid_tpid=fork();if(pid>0){···}elseif(pid==0){···}else{···}我徘徊到fork函数门口,四处张望。“Areyougoingtocreateaprocess?”,fork函数似乎看出了我的意图。“是的,这是我第一次在这里创建进程,以前在Windows上的时候,我调用CreateProcess,但是好像没有这个名字的函数……”fork函数听了笑了,说:“不用找了,我是负责创建进程的函数。”“你?fork不是fork的意思,为什么取这样的名字?”,我说着,走向了fork函数。Fork没有理会我的问题,只是说:“在这里坐一会儿,我想和内核沟通一下,让内核创建一个子进程。”现在我明白他的意思了。像创建进程这样的操作都是在操作系统内核中通过系统调用来完成的,而像fork这样我们可以直接调用的函数只是应用层的接口,这和以前在Windows上是一样的。可我突然反应过来,急切地问道:“咦,我还没告诉你要创建的进程的参数呢,你怎么知道要启动哪个程序呢?”Fork哈哈大笑,但没有回答我的问题。不熟悉地方,只好耐心等待,等的时候睡着了。“醒醒”,不知过了多久,fork函数把我叫醒:“创建完成,请拿走,这是进程号pid”,说完就给了我一个数字。我摊开一看,看到一个大大的0!“怎么了,创建失败了?”我问道。“不是,你是刚刚创建的子进程。”“什么?弄错了吗?我专程来创建子进程的,我怎么会是子进程?”fork函数又笑了,“不是我做的。错了,你其实不是原来的你,而是一个副本,只是内核复制过来的。”“复制品?什么意思?”越听越糊涂!“每个进程在内核中都是一个task_struct结构,刚才你睡着的时候,内核创建一个进程的时候,把你原来在内核中的task_struct复制过来,创建了一个全新的进程地址空间和栈。这个地方不一样,基本一样。”“原来的我呢?我去哪儿了?”》他变成了你的父进程,我是一个特殊的函数,调用一次会返回两次,在父进程和子进程中都会返回,原来进程中,我把你的进程号给他,我返回0给你,这意味着你现在是一个子进程。”原来是这样,我惊呆了,简直颠覆了我的认知,居然有这么一个奇特的函数,调用一次,就变成了两个进程。想了想,突然明白为什么这个函数要叫fork了。Copy-on-write"你刚来,可能不熟悉,慢慢习惯吧""你们效率太高了,整个进程地址空间这么大,你居然复制了一份so快点!”fork函数又笑了!我是不是又说错了?”“进程的内存地址空间没有被复制,你现在是在和父进程共享内存空间。”“什么?共享?你刚刚不是说你创造了一个新的吗?进程空间和栈?”“你看到的内存地址空间是虚拟的,你的内存页和父进程的内存页实际上映射到同一个物理内存页,所以它们实际上是共享的。”“原来是这样的对,不过是共享的,如果两个进程一起使用,岂不是很麻烦?”“别担心,内核把这些页面设置为只读。如果只读,不会有问题,但只要其中一个尝试写,就会触发异常。内核发现异常后,会分配一个新的页面给你单独使用。哦,对了,这叫copy-on-write(COW)机制”“很有意思,你挺聪明的”“没办法,尽量降低创建进程的成本,提高创建效率,因为很多内存进程中的页面只能阅读,如果不假思索地全部复制,岂不是浪费资源和时间?表示fork函数。“有道理,有道理”,我点点头,告别了fork功能,准备回去工作了。消失的线程本以为这种奇怪的进程创建方式让我大开眼界,没想到可怕的事情才刚刚开始。和fork函数说再见没多久,就卡在了一个地方,无法再执行了。原来是前面有个锁被别的线程占用了,现在需要占用。这并不奇怪。以前工作的时候,经常遇到锁被其他线程加锁的情况,而这次等了很久,也没有线程释放。“喂,醒醒”不知道过了多久,我又睡着了。睁开眼,又一个节目台出现在我面前。“你是?”“你好,我是kill”“kill?那个专门杀进程的kill程序?找我干什么?”我惊得失眠了。kill程序从后面拿出两个数字:9、1409“你看,这是我收到的参数,1409是你的进程号PID,9就是强行杀掉你”“啊?为什么?”,然后愣了一下,我完全惊慌失措。“也许你被困在这里太久了,是人类让我结束了你的行动,”杀戮程序说道。“是啊,我也不知道是哪个该死的线程占用了这个锁还没有释放,就卡在这里了”,我委屈的说道。“哪里还有别的线程?我查了,你的进程里只有一个线程!”等了这么久!“奇怪,我明明是多线程程序啊!”,我皱起了眉头。“仔细想想,刚才是不是发生什么事了?”kill程序问道。“我刚刚执行了一个fork,生成了一个子进程,哦对了,我就是那个子进程”“难怪!”,kill程序恍然大悟。“难怪什么?”fork小子创建子进程时,只会复制当前线程,不会复制其他线程!”,kill程序说完,叹了口气,仿佛熟悉了一般。“什么??怎么会?像这样?其他线程没有复制,那岂不是很乱?”kill程序不紧不慢的说道:“这是历史遗留下来的问题。早期是单线程程序。task_struct是一个进程。Fork这样做是没有问题的。后来出现了多线程技术。一个task_struct实际上是一个线程。多个task_struct共享地址空间,成为一个线程组,即一个进程。但是fork还是只复制当前线程,还有这个Question”“我去,这个作弊fork!”“你不是第一个被骗的!等着程序员给你改造。你还有什么要说的吗?如果没有,我就去做,”kill程序终于问道。这一次,我没有回答。只见kill哥举刀落空,一切都消失了……
