标题

[腾讯] 技术类面试题

责任编辑:admin
日期:2012-07-24

提问(不按时间顺序):
1,使用Linuxepoll模型,水平触发模式(Level-Triggered);当socket可写时,会不停的触发socket可写的事件,如何处理?
2,从socket读数据时,socket缓存里的数据,可能超过用户缓存的长度,如何处理?例如,socket缓存有8kB的数据,而你的缓存只有2kB空间。
3,向socket发送数据时,可能只发送了用户缓存里的一半,如何处理?例如,需要向socket发送8kB数据,返回值只有2kB发送成功。
4,C++的虚函数是怎么实现的?5,C++的虚函数有什么作用?6,非阻塞connect()如何实现?7,sizeof()问题
classA
{

 

 

charc;intval;shortsh;
}
classB{
charc;
int
val;
shortsh;

voidfunc1(void);virtualfunc2(void);
}
sizeof(A),sizeof(B)分别是多少?
8,实现字符串比较函数strcmp(char*src,char*sub)9,实现内存拷贝函数strcpy(void*dst,char*src,size_tlen)10,条件变量的如何使用?你使用的线程函数是什么?11,deamon进程如何实现?
12,HTTP和CGI是什么?
13,TCP的三次握手,TIME_WAIT和CLOSE_WAIT状态是什么?因为第7题之后的属于客观题,不打算在此写答案。朋友们如有好的答案也欢迎跟贴。本人在此写出自己对前6个问题的回答:
1,使用linuxepoll模型,水平触发模式(Level-Triggered);当socket可写时,会不停的触发socket可写的事件,如何处理?
第一种᳔普通的方式:
当需要向socket写数据时,将该socket加入到epoll模型(epoll_ctl);等待可写事件。接收到socket可写事件后,调用write()或send()发送数据。。。
当数据全部写完后,将socket描述符移出epoll模型。

这种方式的缺点是:即使发送很少的数据,也要将socket加入、移出epoll模型。有一定的操作代价。
第二种方式,(是本人的改进方案,叫做directly-write)

 

 

向socket写数据时,不将socket加入到epoll模型;而是直接调用send()发送;只有当或send()返回错误码EAGAIN(系统缓存满),才将socket加入到epoll模型,等
待可写事件后,再发送数据。
全部数据发送完毕,再移出epoll模型。
这种方案的优点:当用户数据比较少时,不需要epool的事件处理。在高压力的情况下,性能怎么样呢?

对一次性直接写成功、失败的次数进行统计。如果成功次数远大于失败的次数,说明性能良好。(如果失败次数远大于成功的次数,则关闭这种直接写的操作,改用第一种方案。同时在日志里记录警告)
在我自己的应用系统中,实验结果数据证明该方案的性能良好。
事实上,网络数据可分为两种到达/发送情况:

一是分散的数据包,例如每间隔40ms左右,发送/接收3-5个MTU(或更小,这样就没超过默认的8K系统缓存)。
二是连续的数据包,例如每间隔1s左右,连续发送/接收20个MTU(或更多)。
回来查了资料,发现以下两种方式:

第三种方式:使用Edge-Triggered(边沿触发),这样socket有可写事件,只会触发一次。

可以在应用层做好标记。以避免频繁的调用epoll_ctl(EPOLL_CTL_ADD,EPOLL_CTL_MOD)。这种方式是epoll的man手册里推荐的方式,性能᳔高。但如果处理不当容易出错,事件驱动停止。
第四种方式:在epoll_ctl()使用EPOLLONESHOT标志,当事件触发以后,socket会被禁止再次触发。

需要再次调用epoll_ctl(EPOLL_CTL_MOD),才会接收下一次事件。这种方式可以禁止socket可写事件,应该也会同时禁止可读事件。会带来不便,同时并没有性能优势,因为epoll_ctl()有一定的操作代价。
2,从socket读数据时,socket缓存里的数据,可能超过用户缓存的长度,如果处理?

可以调用realloc(),扩大原有的缓存块尺寸。
但是临时申请内存的有一定性能损失。这种情况要看接收缓存的方式。
第一种方式:使用100k的大接收缓存为例。

如果要等待数据,并进行解析。可能发生缓存不够的情况。此时只能扩充缓存,或先处理100k的数据,再接收新的数据。
第二种方式:使用缓存队列,分成8K大小的队列。

不存在接收缓存不够的情况。除非用户解析已出错,使用数据接收、使用脱勾。这种方式的代价是,可能需要将缓存队列再次拷贝、拼接成一块大的缓存,再进行解析。而在本人的系统中,只需要将socket接收的数据再次原样分发给客户,所以这种方案是᳔佳方案。

 

 

3,向socket发送数据时,可能只发送了用户缓存里的一半,然后失败,如何处理?

记录缓存的偏移量。下一次socket写事件时,再从偏移的位置接着发送。那个面试官居然对这个问题问了我两次,看来我解释的不够清晰。。。。。。郁闷。
4,C++的虚函数是怎么实现的?
使用虚函数表。

回来查下资料:C++对象使用虚表,如果是基类的实例,对应位置存放的是基类的函数指针;如果是继承类,对应位置存放的是继承类的函数指针(如果在继承类有实现)。所以,当使用基类指针调用对象方法时,也会根据具体的实例,调用到继承类的方法。
5,C++的虚函数有什么作用?

虚函数作用是实现多态,很多人都能理解这一点。但却不会回答下面这一点。更重要的,虚函数其实是实现封装,使得使用者不需要关心实现的细节。在很多设计模
式中都是这样用法,例如Factory、Bridge、Strategy模式。前两天在书上刚好看到这个问题,但在面试的时候却没想起来。
个人觉得这个问题可以很好的区分C++的理解水平。
6,非阻塞connect()如何实现?
将socket设置成non-blocking,操作方法同非阻塞read()、write();面试官是在听到我介绍之后,才问我这个问题。可惜还是问我两遍。

阅读:

评论