笔试:分成两卷 一卷有各种数学问题,智力题,计算机基础知识题。涉及到计算机组成原理,操作系统,编译原理,数据结构与算法,TCP/IP,C和C++的一些语法细节。 二卷有四道coding题,一道编译原理的,一道bloom filter。总共三个小时,我估计是没有人能做完的。 面试:一面是三个工程师交叉面试,应该是为了公平起见。 第一位面试官,首先让我做了自我介绍。 我重点强调了暑假的实习中做的某三个靠谱一点的东西,然后提到我前几天刚看了网易的pomelo,并且重点关注了其中的aoi、rpc、sync模块的实现blabla。这时面试官似乎来了兴趣,让我解释下里面的aoi算法。pomelo中的aoi算法实现的还是很简单的,就是传统的灯塔算法,我就大概解释了下,包括一个PC从一个aoi区域移动到另一个aoi区域时,aoi对象状态的变化等等。 然后面试官重点关注了下实习中做的某个东西,我就从节点、协议、cache状态、灾备等几个方面先简单介绍了下,接着说了下跟这个业务相关的一些逻辑的流程,然后重点说了下我自己做的部分。 之后面试官就开始对着简历一个一个问了,先是看到了之前做过的一个.Net项目,直接让我说c#的gc如何实现。我这直接傻眼了,之前经常一看到讲javaGC的blog都是直接关,没想到栽这上面了。记得之前看的gc的英文wiki页面,扫了一遍完全不懂,只记得有种算法的名字叫mark&sweep,而且当时也不太确定引用计数算不算gc的一种。 不过稍微稳定了一下,我试着问面试官可不可以给一个自己在实现一个gc的时候会用到的简单算法。得到了面试官的肯定之后,我就想了下怎么构思一种gc,并给出了一个很朴素的方案,定期执行如下过程:堆中内存每一字都解释为一个指针,如果有一个字没有被任何其他字指向,就进行回收。这样的漏洞很明显,面试官直接就指出循环引用的情况,我只好回答避免循环引用泄露的方法我忘记了。 接着又问了C#和c++泛型的区别,这个不了解,只是猜测了下C#会对泛型具现化的类型的维护更多的运行时信息。后来回来查了下,差别确实不小,又问了有没有做过一个server,epoll循环的问题,epoll的内核实现的问题,我就从文件系统的poll接口到epoll的回调模型讲了下,又问触发模式,就讲了Edge Level blabla。接着又看到了某个win32的项目,让我介绍下这个项目中的亮点。 我先是讲了下里面参考.Net的实体框架做出来的一个db-logic之间的cache层,记录状态,方便进行操作的撤销恢复等等。感觉面试官好像没太大兴趣的样子,面试官问了下这个GUI用的是什么,我说MFC,同时抓住这个机会开始介绍这个项目的UI层的抽象的设计。我介绍了这个交互逻辑层是利用状态模式进行的抽象,交互部分的逻辑完全与UI框架分离,点一个按钮只是改变了系统交互模块状态机的状态。面试官一针见血的问道,那这个交互过程中会有数据的改变造成UI的状态发生变化的情况吗,我思考了下,直言这部分是不能做到UI框架无关的,只能每种UI框架进行单独适配,面试官听了也表示了认可。 然后面试官注意到了一个rpc的项目,我也介绍了下背景并表示这个目前还只是一个初步的想法。又问了我了解不了解一些图形引擎,我说我之前基于Ogre写过一个小东西,面试官让我说下Ogre引擎的启动流程,我尝试性地回忆今年初搞Ogre时的印象,也只想出个大致,给他描述了下,加载渲染接口,加载插件,加载配置,初试化根对象,然后根据场景树依次初始化等等。他又问了插件都有哪些,我说了只记得一个地形渲染器blabla。然后又问如何在win的主循环中嵌入Ogre的主循环,这点还是印象比较深刻的,于是就对win32的事件主循环展开说了blabla。又问了下我平时的编码环境,我回答win下写好代码,同步到跳板机上,make+gdb+vim调整。面试官又问了下gdb相关的问题,我说了下我常用的指令,并且举了个编译hello_world并调试的例子,然后又问我core文件,我就直说进程地址空间的快照,又问了常见的core的原因,我答了遇到某些无法处理的信号的时候会core,典型如段错误。 最后给了道代码题,让把一个递归转成循环,先跟面试官确认了下不用考虑溢出的问题,我就直接给了一个O(n)的实现,写完代码给面试官看的时候提出了一个对数实现,面试官似乎有点惊讶,问我怎么想到的,我说是受那种非常trick的斐波那契对数求解的启发。然后是问问题环节,我重点问了下面试官项目里的aoi和rpc的实现,没想到他们还真是用的十字链表,并给我大致介绍了下他们的团队是怎么评估十字链表的方案和灯塔的方案的,感觉收获不小。 第二位面试官,似乎对我简历中看过上百本计算机经典名著这个槽点比较感兴趣 先让我讲了印象最深的一本书,我讲了CSAPP,并表示这是我linux的启蒙书。但是比较惊讶的是面试官说他不怎么看这本书,还问我看的是不是英文,说看中文会被鄙视的,又问了问有没有看过其他书。我说了Stevens的UNP和APUE,他笑着说这些都是老掉牙的技术了,现在谁还用这种同步模型。我又无奈,说我还看过Stevens的TCP/IP详解第一卷,他对这个来了兴趣,说这个还不错,肯定又看的中文吧。。(已崩溃..)然后又问我看了什么语言相关的书,我说我C语言这块看的是TCPL,C++primer则是我的码农生涯启蒙书,他除了吐槽我看的中文版外对看书的选择倒是比较认可。 他给我推荐了代码大全,我说我买了但是还没来得及看。同时我还说我大四打算看下SICP,他直接来了句我境界肯定没到,让我最好先看了龙书再说。。。接着开始问项目,又讲了一遍实习的东西。又说了一遍rpc的东西,讲了我的将打包-发包-收包-解包的流程利用协程的语言级特性转为同步写法的愿景,结果他直说了这东西前途不大,并说每个游戏程序员都在做这样的尝试,但是目前进展不大。然后我们又讨论了一番,他的focus似乎还是在MMO的数据并发写问题上,我因为没接触过MMO也只得作罢,只阐述了下我的观点。 然后他问了一些c++的高级机制相关的问题。上来先是一道这样的题: Class A{...}; A *pa = new A(); A *pas = new A[NUM](); 1.delete []pas; //详细流程2.delete []pa; //会发生什么3.delete pas; //哪些指针会变成野指针 第一个问题,回答的还可以,首先调用NUM次析构,然后直接一次底层的free,同时提到了delete过程中发生异常的情况。 第二个问题,我印象应该是有超出范围的内存会被回收,具体的还没查阅资料 第三个问题,答错了,面试官给我详细讲了下,目测应该是他刚踩到了一个类似的坑-_-#!!然后面试官问为什么不建议经常手动new和delete而以内存池取代。这个问题我上来直接说内存碎片和内存管理算法的复杂性,面试官不太满意,后来直到我提到malloc调用本身的耗费,他才比较认可。 让我详细讲了下malloc函数本身涉及的几种系统调用,我就从brk、mmap各种blabla,然后面试官说这时候可以开始讲算法的复杂性了。。我就又讲了两种malloc可能的分配算法,一次适配二次适配等等,以及根据申请的块大小的不同,分别是在堆的reserve中取还是直接mmap一个匿名内存映射,他还是不太满意,我只得说我还了解一种内存分配的算法,伙伴算法,他似乎比较感兴趣,让我继续讲,我blabla,感觉面试官还是比较满意的,直言面试到现在基本没人能说出来伙伴算法这东西。接着问了四种typecast操作符,当我说到static_cast类似于旧式的C强转时候,他直接鄙视的说一句,教科书看多了吧。 然后我说我看Ogre对static_cast操作符的重写就是直接用的C语法强转,然后他提示要从编译器理解,看我一直没回答到路上,就直接开始给我讲解了,static_cast可以静态决议出类型的转换可能性,即使是在继承体系中,即使包括了多重继承和虚继承,只要可以进行静态决议就可以转换成功。然后我又开始解释dynamic_cast,安全的downcast和crosscast,不过好像忘记提对指针和引用的区别了,面试官也没提,继续说了const_cast,reinterpret_cast(提到这个的时候面试官说我是目前唯一一个发对音的。。)。然后面试官就说虚表、对齐这些基础的就不问了。然后就问我对图形引擎了解不了解,我说了解不多,还是想从server做起,他说那server的竞争比较激烈啊,目前面试的基本都是做server的,只有几个有client基础,并且很鄙视的说了句server是个人都会啊,我直接无语了。。然后他说除非像xxx是内核级的(应该指的是旁边一个面试官),我觉得还是得充个胖子就说我觉得我也是内核级的。 然后他就说内核这些东西,不宜多看,里面很多东西都是体系结构相关的,很多甚至是历史遗留的,帮助不大blabla。接着也顺便问了下内核的问题,问线程与进程,面试官直接说你也不用说区别了,直接说linux里分别怎么实现的吧。我这点组织的比较乱,从POSIX规范规定的各种线程进程的概念,一直到内核里对线程的特殊实现,clone系统调用指定的创建线程的参数等等,共享的、独有的东西等等,讲的还算比较全面。问了linux的两级页表,怎么样实现各进程的进程地址空间独立的全部机制,相关的数据结构都聊了,page,vma,zone等等。然后又问我linux中进程/线程的结构体结构,存放位置等等,我就大概讲了下task_struct,thread_info等等的关系以及在进程地址空间中的位置。面试官又注意到我在某个.Net项目里面提到了fp,就很鄙视的问.Net还能fp,我就说用的lamda,然后他让我解释尾递归怎么消除,我说我看过推导直接忘。。然后他用一种很通俗的方式给我讲解了下,让我去找第三个面试官。
终面
先自我介绍,讲实习讲项目 HR对项目提了一些问题,不知为什么他特别纠结于回包是怎么回的,我直到面试快结束的时候才知道他是想了解这个协议的包结构怎么确定,当时告诉他是通过一种DSL的方式在client和server两边各生成结构体进行解析。然后注意到了我的某个win32项目用了postgreSQL,就让我当场写一个表达JOIN的SQL查询语句,N久没写过SQL语句,实习以来接触的完全是cache,都是NoSQL的,这直接无力了。 面试官直接开始后面的提问。让我写一个包结构体,包的head和body都要包括进去,我就写了一个带union的struct,然后他问到怎么样根据body的类型算出后面多少字节的,我这时也算是明白过来直接他为什么纠结于回包问题,就多附加的讲了下,说client和server两边生成的meta文件维护这些关系。 后来开始问c++问题,问了下面代码的输出: class A { public:int m; void print() { cout << "A" << endl; } }; A *pa = 0;pa->print(); 这点很明显是c++的一些底层实现原理,可以理解为这样的C代码: void print(A *this) { cout << "A" << endl; } A *pa = 0; print_A(pa); 这样显然是不会发生段错误的,所以我就说了下c++的mangle以及以上提及的几个编译器转换。 然后又在这题的基础上改动了几下问了一些关于虚表的问题,比较基础。构造函数调虚函数的问题,也很基础,从语义上和语法上分别阐述就可以了。后来让我写一个头文件,内容包括对一个数组的定义,同时保证该头文件能被无限多次include,并保证程序不会出现问题。这涉及到链接过程的强弱符号问题。一个对数组的具体定义是一个强符号,而一个符号只能有一个强符号,可以有多个弱符号。 这样的话,只要在头文件中让数组的定义只出现一次,而其他情况只要extern即可,具体的就不写了,利用#ifndef来保证只有第一次#include的时候才会定义强符号,其他情况弱符号就ok。这样一面就结束了。 二面 三对一。目测应该分别是一个手游组的主程,一个端游组的主程,一个主管或总监级别人物。 首先还是自我介绍,讲实习,讲项目,讲pomelo。 问了实习的一些问题,问了对pomelo的一些看法,我直说pomelo是前几天才看,之前完全不懂nodejs,感觉对nodejs感触很大,事件模型,以及一些语法糖对程序员非常友好等等,并表示了pomelo的我重点关注的几个模块都采用的是最基础的算法做的实现blabla。然后问了对云风的blog怎么看待,包括对c的看法和c++的看法,猜测一下应该是看我简历上写经常看大牛blog有没有产生一些自己的思考有没有盲从之类的。而且很明显,云风对c++一直持消极态度,非常偏向于c和lua,但是很明显,做游戏server的完全抛弃c++并不太现实,而且从之前面试的情况看,网易游戏应该是c++为主的,所以这里我就表达了c++还是很有优势的观点,表达了自己还是比较偏向于c++的观点。 端游主程的面试官问了几个问题,关于STL的iterator都有哪些,红黑树的性质,这时中间boss又接过来问红黑树这种实现的意义何在,我就讲到是一种工程中用来简化AVL的trade-off,blabla。然后之前的面试官就连珠炮的问了几个问题,关于我简历上写的protobuf和zmq,问了各种底层实现,我也直说pb的底层代码一直没看,但是了解到底层的编码协议库是pb高效的重要原因,并且说了句pb有google的光环加身所以显得牛逼一些。。zmq只是说自己简单用过一些,说了请求响应模型和发布订阅模型并模拟了几种在mmo中可以应用的场景。 被问到zmq可能应用在哪些方面的时候,消息队列这玩意已经到嘴边了但是因为没把握就是没说出来有点后悔,消息队列可以说是分布式系统中的一个最基础的设施。又问了下对MMO的server如何理解,有哪些主要关注点,我就表达了最近的研究感悟,对比了下SNSGame和MMO的节点的区别,灾备怎么做,通信的关注点,协议方面,数据共享方面,还有单节点内的算法选择。等等。 问了下这次来面试的基本都是研究生,然后我作为本科生有什么优势。这点我思考了下回答,暑假的实习具有游戏开发的background,这里他们都点头表示了一定程度的认可,然后就是我对游戏开发很有热情,讲了自己从小到大与游戏开发的关系,包括帝国的地编,war3的地编,然后玩过的游戏等等。最后强调了下学习能力,这个还是必须得强调下的。 最后端游主程提了一个开放性问题,如果发现某块代码有bug,但是目前在运营中还没出现错误,向PM申请排日程失败,应该怎么做。说实话,完全不明觉厉,随便想了下,只好回答自己会先拉个tag,先尝试重构一下,同时强调了下模块编写的测试驱动理念,每个模型应该具有可测试性,这样的话就可以自己重构的时候保证原来的用例通过而bug解决,然后关键的时候作为patch发布。 网易雷火游戏的面试就结束了,上午的一面持续了两个半小时,中间完全没有休息的进行车轮战。 总体来说的话,网易游戏的笔试难度非常难,基本是完爆BAT的level,简历和笔试刷掉了很多同学,笔试题二卷有三道难度一般的coding题,和一个略有难度的算法题,这可能也是面试中不太重视coding的原因,因为笔试已经进行了很好的筛选。面试的话比较关注skill-set是否和游戏开发match,C++是主打语言,各种高级特性自然要烂熟于心,网络的面试中没怎么碰到,应该是因为暑假实习的原因,问网络其实意义不大了。