腾讯后台开发面试总结.docx
- 文档编号:3441730
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:15
- 大小:646.08KB
腾讯后台开发面试总结.docx
《腾讯后台开发面试总结.docx》由会员分享,可在线阅读,更多相关《腾讯后台开发面试总结.docx(15页珍藏版)》请在冰豆网上搜索。
腾讯后台开发面试总结
前段时间专心面过腾讯,经过了N轮的技术面,结果还是挂了,但没挂在技术面,比较欣慰,回来之后写一点总结,以供有梦想进入腾讯做后台服务器开发的同学参考,本文章为胡成精心总结,胡成原创,copy和转载请通知。
ps:
()之内的文字由作者点评,非面试题文字。
linux和os:
netstattcpdumpipcsipcrm
(如果这四个命令没听说过或者不能熟练使用,基本上可以回家,通过的概率较小^_^,这四个命令的熟练掌握程度基本上能体现面试者实际开发和调试程序的经验)
netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。
tcpdump命令用于监视TCP/IP连接并直接读取数据链路层的数据包头。
cpu内存硬盘等等与系统性能调试相关的命令
uptime
我们可以使用uptime命令来监视Linux系统性能和状态,这是一种非常有效的简单方法。
uptime命令会显示在一定时间间隔内系统运行队列中进程的信息。
通过这些信息可以大致地分析系统的工作负载。
所以当系统性能下降时,首先应使用uptime命令来观察系统运行队列中进程的情况。
$uptime
2:
07pmup11days,4:
54,9users,loadaverage:
1.90,1.98,2.01
在上面显示内容其中有用的信息是三个负载的平均值:
1.90、1.98和2.01分别是前1分钟、5分钟和15分钟内的负载平均值。
系统管理员需要定期运行uptime命令以观察系统的平均负载值及其变化趋势。
系统的问题往往通过上述数据反映出来。
当系统负载增大时,说明多条命令被阻塞在内存和I/O系统中。
这时需要检查系统的有关信息。
一般Linux系统,负载为2和3表示轻载,5和6表示中等程度负载,10以上为过载。
不同系统的划分标准是不同的。
系统管理员应根据实际情况确定自己系统中划分轻载和过载的界限。
19.2.2free
使用free命令可以查看系统中内存空间的大小。
内存是以KB为单位来衡量的。
例如用户可以用free命令来获取系统内存空间的大小信息,命令如下:
$free
Totalusedfrddsharedbufferscached
Mem:
9577286748902454480579636164
-/+buffers/cache:
4478850984
Swap:
1365127000129512
在上面显示内容中,Mem行中的total栏显示了当前Linux正在使用的RAM总量。
used栏显示的是被占用的内存空间,但Mem行与这一栏结合并不能产生最好的分析结果,因为其中包含了Linux分配给磁盘缓冲区的一部分分区。
在大多数系统中,used栏显示的值和total栏的值相当。
然而,我们可以核对used栏、-/+buffers/cache行对应的值来查看有多少内存处于使用状态(used栏、Swap栏对应的数值)。
这一部分是作为内存来用的磁盘,而磁盘速度比内存慢,所以最好优先使用物理内存,交换内存则较少使用。
19.2.3top
在本书第三章我们已经介绍过了top命令,它可以动态实时的察看系统性能,分析CPU、内存的使用资源以及相关进程的信息等。
关于top命令的具体的使用方法和命令参数这里就不再谈及了,如果读者还不了解,请参看第三章“3.5.6top命令”小节的内容。
top会显示系统现在的负载情况,如下所示:
#top
2:
58pmup3:
40,1user,loadaverage:
0.00,0.01,0.00
37processes:
35sleeping,2running,0zombie,0stopped
CPUstates:
5.6%user,1.6%system,0.0%nice,92.8%idle
Mem:
190776Kav,183588Kused,7188Kfree,0Kshrd,80180Kbuff
Swap:
385520Kav,1476Kused,384044Kfree65096Kcached
PIDUSERPRINISIZERSSSHARESTAT%CPU%MEMTIMECOMMAND
6root100000SW0.51.70:
18find
9root9010361036860R0.30.40:
00top
1root801008452S0.00.00:
03init
2root90000SW0.00.00:
00kevent
3root90000SW0.00.00:
00kapmd
5root90000SW0.00.00:
00kswapd
7root90000SW0.00.00:
00bdflus
8root90000SW0.00.00:
00kdated
9root-1-20000SW<0.00.00:
00telnet
top显示的系统信息包括运行时间、进程的数量和状态、CPU周期的百分比和每个进程占用的存储空间、每个进程运行的时间,以及大量的其它信息。
进程是按使用资源的多少来排列的,显示每5秒钟自动更新一次。
在显示过程中,也可以按下空格键手动更新显示。
在查找并解决系统性能故障时,这些信息是相当有用的。
在上面显示的信息中,大家可以看到显示的系统情况,运行中的进程有正在使用的top程序,它使用了0.3%的CPU和0.4%的存储空间;还有find进程使用了0.5%的CPU和1.7%的存储空间。
而其它进程要么处于睡眠状态(S),要么处于睡眠并被换出状态(SW)。
19.2.4vmstat
vmstat是VirtualMeomoryStatistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监视。
它对系统的整体情况进行统计,不足之处是无法对某个进程进行深入分析。
vmstat命令我们也曾在本书第三章简单提及过,现在让我们详细了解一下它。
vmstat的语法为:
vmstat[-V][-n][delay[count]]
其中,-V表示打印出版本信息;-n表示在周期性循环输出时,输出的头部信息仅显示一次;delay是两次输出之间的延迟时间;count是指按照这个时间间隔统计的次数。
对于vmstat输出各字段的含义,可运行manvmstat查看。
当我们运行vmstat命令后,显示结果如下:
$vmstat
procsmemoryswapiosystemcpu
rbwswpdfreebuffcachesisobiboincsussyid
000146077687986465040001210103251098
设置修改权限tcp网络状态查看各进程状态抓包相关等相关命令
tcpdumpnetstatps……
awksed需掌握
共享内存的使用实现原理
(必考必问,然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?
共享内存段最大限制是多少?
)
将一块内存映射到两个或者多个进程地址空间。
通过指针访问该共享内存区。
一般通过mmap将文件映射到进程地址共享区。
存在于进程数据段,最大限制是0x2000000Byte
c++进程内存空间分布(注意各部分的内存地址谁高谁低,注意栈从高道低分配,堆从低到高分配)
ELF是什么?
其大小与程序中全局变量的是否初始化有什么关系(注意.bss段)
使用过哪些进程间通讯机制,并详细说明(重点)
makefile编写,虽然比较基础,但是会被问到
gdb调试相关的经验,会被问到
如何定位内存泄露?
也可以用top实时观察
动态链接和静态链接的区别
动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。
而静态链接就是把所有用到的函数全部链接到exe文件中。
动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入;
而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了。
32位系统一个进程最多多少堆内存
4G
Linux实现的是虚拟地址的前3G供给用户态的进程.后1G是内核的部分.也就是用户态的进程不能访问0xc0000000以上的虚拟地址
多线程和多进程的区别
(重点面试官最最关心的一个问题,必须从cpu调度,上下文切换,数据共享,多核cup利用率,资源占用,等等各方面回答,然后有一个问题必须会被问到:
哪些东西是一个线程私有的?
答案中必须包含寄存器,否则悲催)
写一个c程序辨别系统是64位or32位
写一个c程序辨别系统是大端or小端字节序
#include
intmain()
{inta=0x80000000;
printf("osis%d\t%d\n",a,sizeof(int));
unionut
{shorts;
charc[2];
}u;
if(sizeof(short)==2)
{u.s=0x0102;
if(u.c[0]==1&&u.c[1]==2)
{printf("bigenidan\n");
}
elseif(u.c[0]==2&&u.c[1]==1)
{printf("littleendian.\n");
}
return0;
}
return0;
}
信号:
列出常见的信号,信号怎么处理?
i++是否原子操作?
并解释为什么?
汇编指令中:
在32位系统中,且i没有跨页,那么i++是原子的。
其指令格式如下:
movlx,%eax
addl$1,%eax
movl%eax,x
在64位系统中,i++无论如何也不是原子操作。
其指令代码如下:
movlx,%eax
movlx+4,%edx
addl$1,%eax
adcl$0,%edx
movl%eax,x
movl%edx,x+4
但是,在高级程序语言中,如java与C++等,并不能保证多个线程i++,操作同一个i,可以得到正确的结果。
因为还有寄存器的因素,多个cpu对应多个寄存器。
每次要先把i从内存复制到寄存器,然后++,然后再把i复制到内存中,这需要至少3步。
从这个意义上讲,说i++是原子的并不对。
之前我以为只要加上volatile修饰符就可以使i++操作编程原子性,但事实不是这样的。
加上volatile只是在编译器优化时,提示每次都从内存中读取i的值。
可是当两个线程进行操作时,仍然会出现重复写会的操作。
(这句是我的猜测,因为我写的多线程程序会出现两次i++后结果一样的情况)
因此,最安全的方法是将i++放入加锁的方法块中。
这样就可以保证安全的访问量。
例(对于java):
synchronizedmethod(){i++;}
说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?
如何避免死锁(每个技术面试官必问)
所谓死锁:
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。
虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。
1)互斥条件:
指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。
如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件:
指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件:
指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件:
指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
在系统中已经出现死锁后,应该及时检测到死锁的发生,并采取适当的措施来解除死锁。
目前处理死锁的方法可归结为以下四种:
1) 预防死锁。
这是一种较简单和直观的事先预防的方法。
方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。
预防死锁是一种较易实现的方法,已被广泛使用。
但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。
2) 避免死锁。
该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
3)检测死锁。
这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。
但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。
4)解除死锁。
这是与检测死锁相配套的一种措施。
当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。
常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。
死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
列举说明linux系统的各类异步机制
exit()_exit()的区别?
如何实现守护进程?
8.2.1实现守护进程的步骤
在Linux系统中,要编程实现一个守护进程必须遵守如下的步骤。
1.让init进程成为新产生进程的父进程。
调用fork函数创建子进程后,使父进程立即退出。
这样,产生的子进程将变成孤儿进程,并被init进程接管,同时,所产生的新进程将变为在后台运行。
2.调用setsid函数
通过调用setsid函数,使得新创建的进程脱离控制终端,同时创建新的进程组,并成为该进程组的首进程。
为了使读者更好地理解这一步骤,下面介绍进程组、会话(session)的基本概念。
在Linux系统中,所有的进程都属于各自的进程组。
进程组是一个或多个进程的集合。
打个比方,可以认为某个班级是一个进程组,而其中成员就是进程。
一个班级至少有一个成员。
当一个班级的最后一个成员不存在的时候,这个班级也就不存在了,也就是进程组消亡了。
每个进程组都有类似于进程号的标识,称为进程组ID。
进程组ID是由领头进程的进程号决定的,每个进程组都存在一个领头进程。
进程组的存在与否与领头进程是否存在没有关系。
会话是一个或多个进程组的集合。
与进程组类似,每个会话都存在一个领头进程。
Linux是一个多用户的操作系统,在同一时刻系统中会存在属于不同用户的多个进程。
如果用户在某个终端上发送了某个信号,例如,按下“Ctrl+C”发送SIGINT信号,如何确保信号被正确地发送到对应的进程,同时不会影响使用其他终端的用户的进程?
会话和进程组是Linux内核用于管理多用户情况下用户进程的方法。
每个进程都属于一个进程组,而进程组又属于某个会话。
当用户从终端登录系统(不管是终端还是伪终端),系统会创建一个新的会话。
在该终端上启动的进程都会被系统划归到会话的进程组中。
会话中的进程通过该会话中的领头进程(常称其为控制进程)与一个终端相连。
该终端是会话的控制终端。
一个会话只能有一个控制终端,反之一样。
如果会话存在一个控制终端,则它必然拥有一个前台进程组。
属于该组的进程可以从控制终端获得输入。
这时,其他的进程组都为后台进程组。
图8.3所示为会话、进程组、进程与控制终端之间的关系。
图8.3会话、进程组、进程与控制终端的关系
由于守护进程没有控制终端,而使用fork函数创建的子进程继承了父进程的控制终端、会话和进程组,因此,必须创建新的会话,以脱离父进程的影响。
Linux系统提供了setsid函数用于创建新的会话。
setsid函数的信息如表8.1所示。
表8.1setsid函数
头文件
函数形式
pid_tsetsid(void);
返回值
成功
失败
是否设置errno
调用进程的会话ID
−1
是
setsid函数将创建新的会话,并使得调用setsid函数的进程成为新会话的领头进程。
调用setsid函数的进程是新创建会话中的惟一的进程组,进程组ID为调用进程的进程号。
setsid函数产生这一结果还有个条件,即调用进程不为一个进程的领头进程。
由于在第一步中调用fork的父进程退出,使得子进程不可能是进程组的领头进程。
该会话的领头进程没有控制终端与其相连。
至此,满足了守护进程没有控制终端的要求。
3.更改当前工作目录
使用fork函数产生的子进程将继承父进程的当前工作目录。
当进程没有结束时,其工作目录是不能被卸载的。
为了防止这种问题发生,守护进程一般会将其工作目录更改到根目录下(/目录)。
更改工作目录使用的函数是chdir。
4.关闭文件描述符,并重定向标准输入、输出和错误输出
新产生的进程从父进程继承了某些打开的文件描述符,如果不使用这些文件描述符,则需要关闭它们。
守护进程是运行在系统后台的,不应该在终端有任何的输出信息。
可以使用dup函数将标准输入、输出和错误输出重定向到/dev/null设备上(/dev/null是一个空设备,向其写入数据不会有任何输出)。
下面给出具体的代码:
…
intfd;
//将标准输入输出重定向到空设备
fd=open("/dev/null",O_RDWR,0);
if(fd!
=-1)
{
dup2(fd,STDIN_FILENO);
dup2(fd,STDOUT_FILENO);
dup2(fd,STDERR_FILENO);
if(fd>2)
close(fd);
}
…
5.设置守护进程的文件权限创建掩码
很多情况下,守护进程会创建一些临时文件。
出于安全性的考虑,往往不希望这些文件被别的用户查看。
这时,可以使用umask函数修改文件权限,创建掩码的取值,以满足守护进程的要求。
linux的内存管理机制是什么?
linux的任务调度机制是什么?
标准库函数和系统调用的区别?
系统调用通过软中断int0x80从用户态进入内核态。
函数库中的某些函数调用了系统调用。
函数库中的函数可以没有调用系统调用,也可以调用多个系统调用。
编程人员可以通过函数库调用系统调用。
高级编程也可以直接采用int0x80进入系统调用,而不必通过函数库作为中介。
如果是在核心编程,也可以通过int0x80进入系统调用,此时不能使用函数库。
因为函数库中的函数是内核访问不到的。
补充一个坑爹坑爹坑爹坑爹的问题:
系统如何将一个信号通知到进程?
(这一题哥没有答出来)
c语言:
宏定义和展开(必须精通)
位操作(必须精通)
指针操作和计算(必须精通)
内存分配(必须精通)
sizeof必考
各类库函数必须非常熟练的实现
哪些库函数属于高危函数,为什么?
(strcpy等等)
c++:
一个String类的完整实现必须很快速写出来(注意:
赋值构造,operator=是关键)
虚函数的作用和实现原理(必问必考,实现原理必须很熟)
sizeof一个类求大小(注意成员变量,函数,虚函数,继承等等对大小的影响)
指针和引用的区别(一般都会问到)
多重类构造和析构的顺序
stl各容器的实现原理(必考)
externc是干啥的,(必须将编译器的函数名修饰的机制解答的很透彻)
volatile
是干啥用的,(必须将cpu的寄存器缓存机制回答的很透彻)
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。
如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
staticconst等等的用法,(能说出越多越好)
数据结构或者算法:
《离散数学》范围内的一切问题皆由可能被深入问到(这个最坑爹,最重要,最体现功底,最能加分,特别是各类树结构的实现和应用)
各类排序:
大根堆的实现,快排(如何避免最糟糕的状态?
),bitmap的运用等等
hash,任何一个技术面试官必问(例如为什么一般hashtable的桶数会取一个素数?
如何有效避免hash结果值的碰撞)
网络编程:
tcp与udp的区别(必问)
udp调用connect有什么作用?
1:
UDP中可以使用connect系统调用
2:
UDP中connect操作与TCP中connect操作有着本质区别.TCP中调用connect会引起三次握手,client与server建立连结.UDP中调用connect内核仅仅把对端ip&port记录下来.
3:
UDP中可以多次调用connect,TCP只能调用一次connect.UDP多次调用connect有两种用途:
1,指定一个新的ip&port连结.2,断开和之前的ip&port的连结.指定新连结,直接设置connect第二个参数即可.断开连结,需要将connect第二个参数中的sin_family设置成 AF_UNSPEC即可.
4:
UDP中使用connect可以提高效率.原因如下:
普通的UDP发送两个报文内核做了如下:
#1:
建立连结#2:
发送报文#3:
断开连结#4:
建立连结#5:
发送报文#6:
断开连结
采用connect方式的UDP发送两个报文内核如下处理:
#1:
建立连结#2:
发送报文#3:
发送报文另外一点,每次发送报文内核都由可能要做路由查询.5:
采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom.调用sendto的时候第五个参数必须是NULL,第六个参数是0.调用recvfrom,recv,read系统调用只能获取到先前connect的ip&port发送的报文.
UDP中使用connect的好处:
1:
会提升效率.前面已经描述了.2:
高并发服务中会增加系统稳定性.原因:
假设clientA通过非connect的UDP与serverB,C通信.B,C提供相同服务.为了负载均衡,我们让A与B,C交替通信.A与B通信IPa:
PORTa<---->IPb:
PORTbA与C通信IP
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 腾讯 后台 开发 面试 总结