30

聊聊这道【快手】面试题

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI1MzYzMTI2Ng%3D%3D&%3Bmid=2247484456&%3Bidx=1&%3Bsn=f406c50bfc3635e2965e7fe7fb18b2fa
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0x00 前言

前几天大白参加了快手的视频面试, 第一次使用牛客网的在线面试系统,10点开始12点才结束,一面竟然持续了两个小时,期间最开始用手机面试,20分钟简单聊完项目之后开始编码,于是切换到PC上。

奈何牛客网的在线面试系统在 Chrome 上不好用,一度以为我电脑的摄像头坏掉了,查了一下说可能是浏览器问题,果然快速换到火狐之后就 OK 了,期间我调试环境花了大约10分钟,之后便开始了重头戏-写代码。

快手这个岗位的题目均不是 Leetcode 的题目,都是比较偏实际的问题,期间要进行 debug 所以时间会长一些。

其中有一道题看着挺简单,但是后面仔细想了想,还算是个知识的模糊点,所以决定写出来和大家分享一下。

0x01 题目描述

题目是口述并简单写了一下,基本上达意了,一起看下:

简述在 Linux 环境中使用 C++ 编写的代码输出结果 以及原因。给定两个函数 func1 和 func2,包含的代码如下:

int *glob_ptr = NULL;


void func1(){

int a = 10;

int *b = &a;

glob_ptr = b;

while(true){

//do sth

}

}


void func2(){

int *c = NULL;

c = glob_ptr;

printf("%d\n",c)

}


thread1调用func1

thread2调用func2

可确保thread1调用的func1先跑

程序的执行结果将是什么?


是否会coredump?

如果没有coredump将输出什么?

并对你的答案做出解释。

简单说这是道多线程的问题,两个线程函数运行之后,查看输出结果是什么并给出解释。

0x02 题目解析

题目中给出了全局指针变量 glob_ptr ,该变量是在进程的堆上存储的。

在 func1 中定义了局部变量a和局部指针变量b,随后将全局变量glob_ptr 的值赋给局部变量b,之后是一个 while 的死循环,来保证 func1 一直活着,从而局部变量a和b都是有效的,进而全局变量glob_ptr 也是指向一个有效的地址。

在 func2 中同样定义了局部指针变量c,并将全局指针变量glob_ptr 赋值给了局部指针变量c,之后将其结果进行输出。

2.1  一些必备知识

进程是线程的载体,也可以认为是容器,进程与进程之间是有地址隔离的,Linux 是虚地址模式,每个进程都认为自己持有了全部的内存空间,没错是 OS 骗了它。

本质上各个进程的内存访问都涉及到虚拟地址和物理地址转换,在系统层面避免了多进程之间的直接访问,所以进程之间的通信需要借助于共享内存、消息队列等机制,从而也更加安全。

虚地址模式要比实地址模式更加安全,这个是由OS来保证的,对于一些单任务的操作系统比如 vxwork 很多都是实地址模式,题目中的Linux 便是典型的虚地址模式。

进程内的多个线程共享进程的资源并且也拥有自己的独立资源,这样线程间的通信就方便很多了。

进程与进程就像独立的家庭,每个家庭是有自己的领地的,如果要进行交流需要通过其他手段。

线程与线程之间就像一个家庭中的成员,每个人都生活在相同的家庭空间,但是每个人也都有自己的私有空间,所以每个线程之间的通信要方便的多,只要喊一嗓子就可以听到了。

2.2  大白的答案

经过前简单分析,题目确保了局部变量地址的有效性,不同的地方在于一般的多线程同步都是使用锁、条件变量等手段借助于全局变量来进行多线程的数据通信和同步。

题目中借助于局部变量的实现确实是没有见过,这也是大白要写这道题的初衷。

很多时候我们对于常见的东西的正确性没有疑问,比如借助于锁的线程同步,但是有时候没见过的形式也不一定就是错误的,只能说可能是可行但不完善,其实本题就是后者情况。

经过几分钟的思考,大白给出了答案:

可能结果1 :coredump掉,因为在我看来Linux是比较安全的,进程内的多个线程的局部变量存储在私有空间栈上,从安全角度来说应该是不可以相互访问私有栈空间的,所以coredump。

可能结果2 :没有coredump,因为可能Linux并没有那么做,为进程内的线程间也做了屏障,多个线程的私有栈空间是可以相互访问的。

所以两个答案的区别根源就在于 Linux本身是否允许多线程内私有空间的访问 ,来看一张进程内多线程的内存空间分布图吧:

iInqeeE.jpg!web

经过一番思考(其实算是直觉吧),大白觉得是结果2,因为Linux本身并没有必要来实现线程内私有栈的隔离,而且Linux的地址空间的隔离应该是进程粒度的,线程还是可以相互访问的,所以我选择了不会coredump并且输出10。

但是我并不确定,因为两种结果都说得通,不确定的根源在于自己知识的盲点: 多线程私有空间的访问权限问题

0x03  代码实践

面试结束之后,晚上大白实践了一下,发现确实是结果2。简单写的糙代码如下:

uEJB3ei.png!web

为了避免程序的偶然性结果,大白在func2中间隔1s打印了10次,简单编译执行结果确实是没有coredump,间隔1s输出10。

这个问题算是被验证了,不过总觉得不能这样结束,于是在网上搜索了一些东西。

0x04  继续思考

在知乎上看到一个很相似的问题:

https://www.zhihu.com/question/265224833

rINRnmv.png!web

有意思了!和我刚才验证的问题是矛盾的,不过知乎上总是会出现一些并不成立的问题,所以还是先说是什么再说为什么吧!

简单看了几个高票答案,也证明了这个提问者问题本身的不成立,在一个腾讯员工的回答中看到一个华丽丽的代码:

VBRR3mn.png!web

大神这个代码是C++14的一些高级特性,并且没有借助于全局指针变量,直接使用局部变量来相互访问的,看下编译执行结果:

6NnIRnj.png!web

其中一个回答的图也比较好,贴一下:

2M3MVju.jpg!web

0x05.笔者小结

快手的这道题目并不复杂,但是对一些不常见的形式探究的比较深入,对此大白觉得用几句话概况吧:

正确的并不一定是好用的,不常见的也并非是错误的,正确且常见的形式往往是取舍验证之后的优解,但是并不是唯一解,掌握根源我们才能灵活应对各种奇怪的形式,并作出分析。

就这么多吧!本期完,祝各位周末愉快,疫情之下 注意防护。

春风在不久之后将吹开紧闭的家门,我们纷纷摘下口罩,洋溢着久违的笑容,自由呼吸、快乐奔跑!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK