字节是我秋招提前批第一个投的,从7.26一面到今天9.7拿意向书,耗时40余天,历经五轮技术面,期间心情跌宕起伏,用此面经纪念一下这段心跳回忆
产品研发与工程化部门
一面
7.26 70min
个人介绍3min
针对项目提问20min
先问我哪项了解多点,我说计算机网络,然后面试官说那先问一下简单的操作系统吧(-_-
进程线程协程区别
A进程可以访问B进程的空间吗(不能
那怎么能访问呢(IPC
共享内存咋实现的(直接把物理地址映射到另一个进程的地址空间里面去
为啥共享内存快(省略了用户态到内核态的开销,追问还有吗,回答其他的不了解了)
TCP三次握手,服务端如果不调用accept会咋样(客户端发完第三次的ack包后就认为自己建立连接,而服务端不认为
那为什么会有这种状态不一致呢(拜占庭问题
TCP是可靠的,三次握手能解决什么问题呢,用你的观点来说好像不是可靠的?(建立连接的确可能出现这种问题,但是客户端第一次发包的时候,服务端认为连接不存在,返回一个RST包,这样客户端就知道连接不存在了
那我换个问题,TCP双方确立连接后正常发送数据,如果服务端突然崩了,那会咋样(客户端认为连接还在,服务端认为不存在,返回RST包提醒客户端
假设服务端进程core了,或者机器坏掉了,这两种有区别吗(第一种是返回RST包,第二种不会有任何响应,客户端会重试几次,然后认为连接失效
RST包是谁发起(服务端
是指这个进程还是这个机器呢(应该是这台机器吧,因为这个进程已经core掉了
那你知道这个包的协议是什么,就是它在哪一层(RST包就是TCP包吧,那不就是在传输层?
连接都断了,为什么还有这个包发送呢(我回答的是Linux TCP协议栈可以处理这种问题,我没有这个连接,莫名其妙接收到了这个连接的包,所以会回一个RST包
你意思说这还是一个TCP包是吧(对
面试官沉默ing,这让我产生了怀疑,RST包不是TCP包嘛?
共识算法了解哪些呢(paxos、raft、gossip,前面稍微连接一点
raft大概是咋样的逻辑(更好实现更好理解,leader、follower、candidate,相比于paxos,选主和日志复制分离,模块化,paxos的proposer和acceptor把选主和日志复制糅合在一起了
你知道它是怎么解决网络分区的问题(脑裂的问题,一半以上的支持才会确认自己leader,好像是通过item的index来决定谁的leader程度更高,另一个就自动退化为follower
C++虚函数说一下
如何取到子类的函数地址(虚函数表其实是个数组,对虚指针解引用,然后用中括号
MySQL索引(B树、B+树
为啥用B+树(减少磁盘IO次数,区间遍历
写性能相比于读性能(要差,因为要分裂和合并分支,来维持m阶的特性
下面的输出
class C { public: // int a; void func(void) { printf("hello %d", a); } }; int main() { C *c = NULL; c->func(); return 0; }
我首先说是会报member access null类似的错,然后面试官让我跑一下,啪啪打脸。。
再分析一下?(想了一会,推断说func是非虚的成员方法,所以可以直接访问
那怎么找到func的地址,或者说这个函数是放在哪里的(代码段
如果加上一个int a成员变量,会输出什么?(先想了一会,开玩笑说对自己的答案不是很自信,我首先说会输出0,然后再改口说会报错,原因是func调用了成员变量,成员变量需要访问类的对象,而c没有new出来,所以会报错,大概是对的
用rand 7 实现 rand 10
有一个无序整型数组:[3, 7, 2, 0, -1, 9, 8 ...],长度1000w左右,要求设计一个算法,找到数组中位数
先说了大小堆的联机解法,然后面试官说想要小于O(nlogn)时间复杂度的排序,对数组元素没要求,也不需要额外空间
这里我们的沟通出现了问题,我以为面试官是想要完全排序,然后说了桶排序什么的,但他原意是找到中位数就行,害,那就直接进入主题:快速选择
14min左右撸出来了,没时间debug,跑出来是死循环,然后时间也差不多了,大概说了一下思路,差不多解释清楚了
#include #include using namespace std; int quickSelect(vector &vec, int l, int r) { if (l >= r) return l; int pivot = l; // 选择第一个为枢纽 ++l; while (l < r) { while (l < r && vec[l] < vec[pivot]) ++l; while (l vec[pivot]) --r; swap(vec[l], vec[r]); } swap(vec[l], vec[pivot]); // 枢纽归位 return l; // 将pivot返回 } int findMedium(vector &vec) { int n = vec.size(); int l = 0; int r = n - 1; int k; while (true) { k = quickSelect(vec, l, r); if (k == n / 2) { // 找到中位数了 return vec[k]; } else if (k < n / 2) { l = k; // 中位数在右边 } else { r = k; // 中位数在左边 } } return -1; // never reached } int main() { vector vec {3, 7, 2, 0, -1, 9, 8}; cout << findMedium(vec) << endl; }
过了几天后hr约时间,我说到最近在实习,能否周末,hr说要尽快走完,可以安排二面三面在同一天
二面
90min
项目
两段实习
elk、日志的难点,采集日志的进程挂了咋办,日志rotate后咋办(不是很会
c++内存模型(高字节到低字节整了个遍
malloc的底层实现(brk与mmap
虚拟内存
io复用
tcp拥塞控制
题目:二维数组的地图中,搜索指定字符串是否存在
同力扣79,力扣79还加了限制条件,已经走过的单元格不能再走,得用visited数组记录
#include #include using namespace std; bool dfs(vector> &board, string &word, int wordIndex, int x, int y) { if (board[x][y] != word[wordIndex]) { return false; } if (word.size() - 1 == wordIndex) { return true; } wordIndex++; if ((x > 0 && dfs(board, word, wordIndex, x - 1, y)) || (y > 0 && dfs(board, word, wordIndex, x, y - 1)) || (x < board.size() - 1 && dfs(board, word, wordIndex, x + 1, y)) || (y < board[0].size() - 1 && dfs(board, word, wordIndex, x, y + 1)) ) { return true; } return false; } bool exist(vector> &board, string word) { for (int i = 0; i < board.size(); ++i) { for (int j = 0; j < board[0].size(); ++j) { if (dfs(board, word, 0, i, j)) { return true; } } } return false; } int main() { vector> map; vector vec = {'a', 'c', 'd', 'z'}; map.push_back(vec); vec = {'x', 't', 'r', 'o'}; map.push_back(vec); vec = {'f', 'i', 'o', 'o'}; map.push_back(vec); string str = "zoo"; cout << exist(map, str) << endl; str = "zoro"; cout << exist(map, str) << endl; str = "xtifx"; cout << exist(map, str) << endl; str = "oto"; cout << exist(map, str) << endl; }
题目:给定m个字符 [a, b, c, d],字符可能重复,以及一个长度为n的字符串tbcacbdata,问能否在这个字符串中找到一个长度为m的连续子串,使得这个子串刚好由上面m个字符组成,顺序无所谓,返回任意满足条件的一个子串的起始位置,未找到返回-1。
输入 [a, b, c, d] + tbcacbdata -> 3
输入 [a, b, c, c] + tbcacbdata -> 1
输入 [a, b, c, d] + abctbcdata -> 4
同力扣438
#include #include using namespace std; int func(string s, string p) { int res; int count[256] = {0}; int l = 0; int r = 0; int len = 0; for (auto &e : p) ++count[e]; while (r < s.size()) { if (--count[s[r++]] >= 0 && ++len == p.size()) { res = l; // 匹配命中 return res; } if (r - l == p.size() && ++count[s[l++]] > 0) --len; //++l; // 每次左指针收缩一格,进入下一轮循环 } return res; } int main() { string str = "tbcacbdata"; string pattern = "abcd"; cout << func(str, pattern) << endl; pattern = "abcc"; cout << func(str, pattern) << endl; str = "abctbcdata"; pattern = "abcd"; cout << func(str, pattern) << endl; }
最后反问环节我问面试官后面是不是还有三面,面试官说他不知道,顿感凉凉,但是面完不久后就收到hr电话,晚上三面
三面
这里相当于就是主管了,看得出来,面试官是个大佬,说话云淡风轻,问的东西都很深入
70min
Raft的Leader挂了咋办
Paxos原理
科研方向,软件定义网络是什么
实习经历
异步队列用的是什么(MQ),你对MQ的原理熟悉吗(还没深入了解,实习时间不久,先把组内架构摸清楚,还没深入了解中间件
A与B轮流从 1000 个棋子里取棋子,规定每次最多取 7 个,最少取 1 个,谁最后取完剩下的棋子谁获胜,A先取,有没有必胜的策略?
刚开始算错了,1000除以8的余数是4。。。
其实是0,后来再思考了一会,给出了答案:如果B足够聪明,A必输
有一个很大很大的输入流,大到没有存储器可以将其存储下来,也不知道到底有多大,而且只输入一次,如何从这个输入流中随机取得 7 个记录。
蓄水池算法
class A { public: A(int v) { _v = v; } void Run() { std::cout << "Hello" << std::endl; } private: int _v; } int main() { A * p = NULL; p->Run(); }
扯了一下内存模型,应该是不会报错的
如果Run里面输出_v呢?(会报错
如果Run是虚函数呢?(会报错
面试完后本地跑了一下,是这样的
struct Obj { char a; uint32_t b; uint8_t c; uint64_t d[0]; }; sizeof(Obj) = ?
字节对其的知识点有点忘了,这题不是很会,猜了个1+4+8=13,面试官说ok,下一题,我问这是对的吗,他干脆利落地说:不对
(害,这问啥呢,打击自信心啊
最后反问面试官环节问了一下这题是怎么做的,面试官很耐心的给出了解释
如果没有第四个字段,应该是4+4+4=12,加上第四个字段是16,第四个字段不占空间,但会告诉编译器要按8字节对齐
这种用法在内核里经常用到,比如下面可以直接用下标访问
Obj o1; uint64_t array[1024]; // 在内存中,array紧跟o1后面 o1.d[123]; // 可以访问array的元素
实现一个函数,删除所有 map 中 val 为 100 的元素
void RemoveElement(map &elems, int target) { for (auto it = elems.begin(); it != elems.end(); ) { if (it->second == target) { it = elems.erase(it); } else { ++it; } } }
给定一个字符串 s ,找出 至多 包含 k 个不同字符的最长子串 T ,输出子串 T 长度。
示例 1:
输入: s = "eceba", k = 2
输出: 3
解释: 则 T 为 "ece",所以长度为 3。
示例 2:
输入: s = "aa", k = 1
输出: 2
解释: 则 T 为 "aa",所以长度为 2
int func(string s, int k) { unordered_map m; int maxLen = 0; int i = 0; // 快指针 int j = 0; // 慢指针 while (i < s.size()) { if (m.size() <= k) m[s[i]]++; while (m.size() > k) { // 当前区间不满足『至多包含k个不同字符』 if (--m[s[j]] == 0) { m.erase(s[j]); } ++j; // 慢指针左移 } maxLen = max(maxLen, i - j + 1); ++i; // 快指针左移 } return maxLen; }
大概用了不到20分钟撸完了,自己造测试用例也通过了,面试官还有点嫌我慢了(-_-
过了几天收到hr电话,说我前面三面表现不错,可以加面一轮,开始被虐了。。
四面
交叉面 78min
面试官上来先介绍了一下,这是交叉面,介绍了一下自己所在的部门,还主动说了自己的名字(心里想着,是不是会稍微简单一点呢,后来啪啪打脸,不过也有可能是因为我菜吧
首先问了下阿里的实习内容,详细问了我的工作内容,还问了一下我的工作难点是什么
再询问了腾讯的实习内容
系统调用read的流程(用户态切到内核态,磁盘驱动器,通过LBA找到块位置,读到内存缓冲区,再拷贝到用户进程里面)
有什么优化方法吗(DMA、还有零拷贝技术,比如mmap, sendfile,还有写时复制技术)
gdb调试,gdb怎么调试运行的进程?(gdb attach pid),gdb调试正在运行的进程,如果你还给它发RPC会怎么样(这里没太理解的他的意思,意思是多线程,就一个线程停在断点上)
北京小汽车摇号,20万人,每个人的初始权重都是一样的,但有人没被摇中,他的权重就会增加,用计算机的语言来解决这个问题(口述代码思路)
我想到每个人维护一个int值,初始为1,没有摇中就增加1,下次摇号时,把所有人的计数值在数轴上展开,比如第一个人权重为1,那他就占1,第二个权重未3,那他就占2,3,4,然后调用一次rand函数
面试官说明白我的意思,但不满意这个解法,提示说,能不能不存下来,但是每个人按宽度对应到坐标轴上去,想了很久,想不出来
int * arr N 大根堆
del k, 数组下标k
坦白:我只知道最大堆的一些特点,比如一个节点下标为i,他的左儿子节点下标是2i,他的右儿子节点下标是2i+1,他的父亲节点下标是i/2,然后增加和删除需要一些上滤和下滤操作,但是我没自己实现过
最后面试官说ok,再换一道,让我用C++实现一个线程池,我说可能有的api不太记得完整的,面试官说没关系,能写多少是多少
大概写了有15min,然后面试官说由于时间关系就到这里吧,我大概明白你的(代码)的意思了,我没啥其他问题了,你有什么想问我的吗
解释了一下今天表现不是特别好,正好问到薄弱的地方,面试官说没关系,问了下对方的base与部门,是北京的推荐工程化落地的
四面完后几天就没下文了,心境凄凉,等了一个多星期,才有hr联系我说,争取了机会,再加面一轮,又有希望了!
五面
8.23 50min
阿里转正了吗,怎么看字节和阿里
前面四轮技术面都问了很多了,就问你一个简历上的东西吧
quic为什么可以队头不阻塞(各流独立blabla,答的不算特别完整
概率题:一种流行病患病概率是1%,有一种检测试纸,检测的准确率是99%,我现在试纸检测阳性了,请问我患病的概率有多大?(数学题,条件概率,贝叶斯公式
逻辑题:
- 给你一个天平,32个重量不一样的石头,要比较多少次才能找到最重的石头(31次
- 基于问题1,你已经找到最重的石头了,如何找到第二重的石头,还需要比较吗,还需要比较多少次?(类似于欧冠32强,画一个冠军的晋级路线图,实力最强的肯定是冠军,但亚军不一定是实力第二强的,总之:冠军这一路上碰到的队伍都有可能是实力第二强的队伍,所以五支队伍中选出最强的,需要4次)
- 给你一个最多可称8块石头的设备,可以知道总重量,32块重量不一样的石头,如何找到最重的三块石头(没时间思考了,随便说了下就结束面试了
五面整体感觉就是考察我这个人聪不聪明,问一堆智力题
五面完之后就陷入了漫长的等待,期间有联系内推人,问hr说我的面试评价不一致,还需要再讨论,感觉就是进入备胎池了
全部评论
(56) 回帖