0%

面经-腾讯面经

https://blog.csdn.net/soipray/article/details/56279623

linux和os:

netstat tcpdump ipcs ipcrm (如果这四个命令没听说过或者不能熟练使用,基本上可以回家,通过的概率较小 这四个命令的熟练掌握程度基本上能体现面试者实际开发和调试程序的经验)

netstat
-t :TCP
-u :UDP
netstat –r 路由
netstat -p
https://www.cnblogs.com/ftl1012/p/netstat.html

tcpdump
tcpdump -i eth1 监听指定网络端口包
tcpdump host 210.27.48.1监听主机
tcpdump tcp port 23 and host 210.27.48.1
https://www.cnblogs.com/williamjie/p/9983958.html

ipcs
ipcs -a
ipcs -q 队列
ipcs -s 信号量
ipcs -m 共享内存
https://blog.csdn.net/huangyimo/article/details/80236181

ipcrm
ipcrm -m 18602 删除共享内存
https://ipcmen.com/ipcrm

cpu 内存 硬盘 等等与系统性能调试相关的命令必须熟练掌握,设置修改权限 tcp网络状态查看 各进程状态 抓包相关等相关命令 必须熟练掌握
cpu
iostat 监视系统输入输出设备和CPU的使用情况
内存
vmstat 显示虚拟内存状态
free 指令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存,共享内存区段,以及系统核心使用的缓冲区等。
磁盘
df 文件系统的磁盘使用情况统计。

https://blog.csdn.net/guoxiaojie_415/article/details/80526667
awk AWK是一种处理文本文件的语言,是一个强大的文本分析工具。
sed 可依照script的指令,来处理、编辑文本文件。

共享内存的使用实现原理(必考必问,然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?)
https://blog.csdn.net/al_xin/article/details/38602093
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
内存映射段
https://blog.csdn.net/ypbsyy/article/details/79915117

c++进程内存空间分布(注意各部分的内存地址谁高谁低,注意栈从高道低分配,堆从低到高分配)


为初始化区
初始化区
数据段

ELF是什么?其大小与程序中全局变量的是否初始化有什么关系(注意.bss段)
https://blog.csdn.net/liuzk2014/article/details/54409466
可执行链接文件
是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。
1.可重定位文件(Relocateable)这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类,如.o文件。
2.可执行文件(Executable file)这类文件包含了直接执行的程序,如/bin/bash等。
3.共享目录(shared object file)链接器可以使用这种文件跟其他的可重定位文件和共享目标文件链接,产生新的目标文件;动态链接器可以将几个共享目标文件与可执行文件结合,作为进程映像的一部分来运行,如glibc***.so。
4.核心转储文件(core dump file)当进程意外终止时,系统可以将该进程的地址空间内容及终止时的一些其他信息转储到核心转储文件。

.bss段在elf仅占用一个占位符,不占用磁盘空间
可以减少重新编程重新编译的代码。

使用过哪些进程间通讯机制,并详细说明(重点)
管道(无名管道和有名管道)共享内存 消息队列 信号 信号量 套接字

makefile编写,虽然比较基础,但是会被问到
预处理、编译、汇编、文本链接四个步骤,每次得到的文件是:test.i,test.s,test.o,test
https://www.cnblogs.com/tp-16b/p/8955462.html
$^ 代表所有的依赖文件
  $@ 代表所有的目标文件
  $< 代表第一个依赖文件
Makefile的编写规则:
目标文件:执行文件
命令(注意:命令前必须以tab键开头)

gdb调试相关的经验,会被问到

如何定位内存泄露?
内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的、大小任意的(内存块的大小可以在程序运行期决定)、使用完后必须显示释放的内存。应用程序一般使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块。否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
只能使用top指令观察进程的动态内存总额
ps、kill两个命令检测内存使用情况和进行回收。

内存泄漏的原因
* 在类的构造函数和析构函数中没有匹配的调用new和delete函数
* 程序循环new创建出来的对象没有及时的delete掉,导致了内存的泄露
* delete掉一个void*类型的指针,导致没有调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄露;
* new创建了一组对象数组,内存回收的时候却只调用了delete而非delete []来处理,导致只有对象数组的第一个对象的析构函数得到执行并回收了内存占用,数组的其他对象所占内存得不到回收,导致内存泄露;
* 指向对象的指针数组不等同于对象数组
* 缺少拷贝构造函数 如果一个类里面有指针成员变量,要么必须显示的写拷贝构造函数和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符
* 缺少重载赋值运算符
* 没有将基类的析构函数定义为虚函数

造成野指针的原因:
* 指针变量没有被初始化(如果值不定,可以初始化为NULL)
* 指针被free或者delete后,没有置为NULL, free和delete只是把指针所指向的内存给释放掉,并没有把指针本身干掉,此时指针指向的是“垃圾”内存。释放后的指针应该被置 为NULL.
* 指针操作超越了变量的作用范围,比如返回指向栈内存的指针就是野指针。

https://blog.csdn.net/u012662731/article/details/78652651

valgrind
https://blog.csdn.net/ydyang1126/article/details/72667411
1、使用未初始化的内存
2、内存读写越界
3、内存覆盖
4、动态内存管理错误
5、内存泄漏

工具
1.Memcheck:这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够给发现开发中绝大多数的内存错误使用的情况,比如:使用未初始化
2.callgrind:它主要用来检查程序中函数中调用过程中出现的问题
3.cachegrind:它主要用来检查程序中缓存使用出现的问题
4.Helgrind:它主要用来检查多线程中出现的竞争问题
5.Massif:它主要用来检查程序中堆栈使用中出现的问题
6.Extension:可以使用core提供的 功能,自己编写特定的内存调试工具

动态链接和静态链接的区别
动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。 而静态链接就是把所有用到的函数全部链接到exe文件中。

动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入;而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了。

32位系统一个进程最多多少堆内存

多线程和多进程的区别(重点 面试官最最关心的一个问题,必须从cpu调度,上下文切换,数据共享,多核cup利用率,资源占用,等等各方面回答,然后有一个问题必须会被问到:哪些东西是一个线程私有的?答案中必须包含寄存器,否则悲催)
https://www.php.cn/faq/416853.html

写一个c程序辨别系统是64位 or 32位

#include “stdio.h”

int main(int argc,char * argv[])
{
void* number = 0;
printf(“%d\n”,sizeof(&number));
}

不用sizeof判断16位还是32位的例子

写一个c程序辨别系统是大端or小端字节序
typedef union {
int i;
char c;
} myUnion;
// 1: Little Endian; 0: Big Endian.
int isLittleEndian02(void)
{
myUnion u;
u.i = 1;
return (u.i == u.c);
}

信号:列出常见的信号,信号怎么处理?
发送给进程的唯一信息通常是一个数
信号的两个主要目的:让进程知道已经发生了一个特定的事件;强迫进程执行它自己代码中的信号处理程序。
https://blog.csdn.net/u010318270/article/details/81058037

i++是否原子操作?并解释为什么?
内存到寄存器
寄存器自增
写回内存
这三个阶段中间都可以被中断分离开.

说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁(每个技术面试官必问)
https://www.cnblogs.com/liuwei0773/articles/9506748.html
原子操作:
自旋锁
自旋锁的特点就是当一个线程获取了锁之后,其他试图获取这个锁的线程一直在循环等待获取这个锁,
读写自旋锁
读锁之间是共享的
互斥的
信号量
线程获取不到信号量的时候,不会像自旋锁一样循环区试图获取锁,而是进入睡眠,直至有信号量释放出来时,才会唤醒睡眠的线程,进入临界区执行。
读写信号量
互斥量
mutex
mutex 计数值只能为 1,也就是说最多允许一个线程访问临界区。

顺序锁
它的特点是,读锁被获取的情况下,写锁仍然可以被获取。
使用顺序锁的读操作在读之前和读之后都会检查顺序锁的序列值。如果前后值不服,这说明在读的过程中有写的操作发生。那么该操作会重新执行一次,直至读前后的序列值是一样的。

1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

解除死锁:

剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。

列举说明linux系统的各类异步机制
信号,这是一种进程间通信的异步机制;
epoll,这是一种高效处理IO的异步通信机制。

exit()和_exit()的区别?
exit()会首先将所有使用atexit注册的函数进行调用以后再推出,而_exit()则是直接结束程序。
在exit()函数里会调用_exit()函数

如何实现守护进程?
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。
https://blog.csdn.net/qq_26499321/article/details/72724173

linux的内存管理机制是什么?
https://blog.csdn.net/u013616945/article/details/77435607
单一连续区
固定分区
可变分区
页式存储管理方案
用户进程地址空间被划分成大小相等的部分,称为页或者页面,从0开始编号。
内存空间按同样的大小分成划分成大小相等的区域,称为页框。从0开始编号。
以页为单位来进行内存分配,按照进程需要的页数来分配;逻辑上相邻的页,物理上不一定相邻。典型的页面尺寸是4K或4M。
从进程的地址空间转换到实际的内存空间是通过查页表项(记录了逻辑页与页框号的对应关系,每一个进程一个页表,存放在内存)来实现的。CPU取到逻辑地址,自动划 分为页号和页内地址;用页号查页表,得到页框号,再与页内偏移拼接成物理地址。

段式存储管理方案
用户进程地址空间按照程序自身的逻辑关系划分为若干个程序段,每个程序段都有一个段名。
内存空间被动态划分成若干长度不相同的区域,称为物理段,每个物理段有起始地址和长度决定
内存分配规则:以段为单位进行分配,每段在内存占据连续空间,但各段之间可以不相邻。
逻辑地址为段号+段内地址。

段页式存储管理方案
用户进程地址空间先按段进行划分,每一段再按页面进行划分。
内存空间:同页式存储方案。
内存分配规则:同页式存储方案。
逻辑地址如下:
虚拟内存技术
基本思想:每个程序拥有自己的地址空间,这个空间被分割成很多个块,每一个块称为页面。每一页都有连续的地址范围。这些页被映射到物理内存,但并不是所有页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻执行必要的映射。当程序引用到一部分不再物理内存中的地址空间时,由操作系统(缺页异常)负责将缺失的部分装入物理内存并重新执行失败的指令。

linux的任务调度机制是什么?
先来先服务(队列)
最短优先(优先队列)
优先权调度算法的类型
高响应比优先调度算法
时间片轮转法
多级反馈队列调度算法
(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。

标准库函数和系统调用的区别?
系统调用是通向操作系统本身的接口,是面向底层硬件的。通过系统调用,可以使得用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互,是操作系统留给应用程序的一个接口。
库函数调用则是面向应用开发的,库函数把系统调用进行封装或者组合,可以实现更多的功能
库函数是语言或应用程序的一部分,而系统调用是内核提供给应用程序的接口,属于系统的一部分
库函数在用户地址空间执行,系统调用是在内核地址空间执行,库函数运行时间属于用户时间,系统调用属于系统时间,库函数开销较小,系统调用开销较大
库函数是有缓冲的,系统调用是无缓冲的
系统调用依赖于平台,库函数并不依赖

补充一个坑爹坑爹坑爹坑爹的问题:系统如何将一个信号通知到进程?(这一题哥没有答出来)
:内核在进程所在的进程表项的信号域设置对应的信号的位,进程会维护一个未决信号的链表,处于用户态时就会处理信号。
内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。
进程检查信号的时机是:进程即将从内核态返回用户态时。如果进程睡眠了,要看睡眠能不能被中断,如果能被中断则唤醒。
进程有一个链表的数据结构,维护一个未决信号的链表。
信号在进程中注册,其实就是把该信号加入到这个未决信号链表当中。
可靠信号不管链表中是否已经有这个信号了,还是会加进去。不可靠信号,如果链表中已经有这个信号了,就会忽略。
进程处理信号的时机就是从内核态即将返回用户态的时候。
执行用户自定义的信号处理函数的方法很巧妙。把该函数的地址放在用户栈栈顶,进程从内核返回到用户态的时候,先弹出信号处理函数地址,于是就去执行信号处理函数了,然后再弹出,才是返回进入内核时的状态。
被屏蔽的信号,取消屏蔽后还会被检查。
c语言:

宏定义和展开(必须精通)

位操作(必须精通)

指针操作和计算(必须精通)

内存分配(必须精通)

sizeof必考

各类库函数必须非常熟练的实现

哪些库函数属于高危函数,为什么?(strcpy等等)
strcpy 赋值到目标区间可能会造成缓冲区溢出!

c++:
一个String类的完整实现必须很快速写出来(注意:赋值构造,operator=是关键)

虚函数的作用和实现原理(必问必考,实现原理必须很熟)

sizeof一个类求大小(注意成员变量,函数,虚函数,继承等等对大小的影响)
一个对象的大小大于等于所有非静态成员大小的总和。
1. 为类的非静态成员数据的类型大小之和.
2. 编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).
3. 为了优化存取效率,进行的边缘调整(对齐
4. 与类中的构造函数,析构函数以及其他的成员函数无关.

指针和引用的区别(一般都会问到)

多重类构造和析构的顺序

stl各容器的实现原理(必考)

extern c 是干啥的,(必须将编译器的函数名修饰的机制解答的很透彻)
主要作用就是为了能够正确实现C++代码调用其他C语言代码
由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

volatile是干啥用的,(必须将cpu的寄存器缓存机制回答的很透彻)
访问寄存器比访问内存单元要快,编译器会优化减少内存的读取,可能会读脏数据。声明变量为volatile,编译器不再对访问该变量的代码优化,仍然从内存读取,使访问稳定。
特别在两个或多个进程之间通讯时,
例如:进程A有变量volatile BOOL fOK;
进程B中某个线程B1一直处于等待状态,直到进程A的变量fOK为TRUE,结束等待.

static const等等的用法,(能说出越多越好)

数据结构或者算法:

《离散数学》范围内的一切问题皆由可能被深入问到(这个最坑爹,最重要,最体现功底,最能加分,特别是各类树结构的实现和应用)

各类排序:大根堆的实现,快排(如何避免最糟糕的状态?),bitmap的运用等等

hash, 任何一个技术面试官必问(例如为什么一般hashtable的桶数会取一个素数?如何有效避免hash结果值的碰撞)
危险出现在当假设所选非素数m=x*y,如果需要hash的key正好跟这个约数x存在关系就惨了,最坏情况假设都为x的倍数
减少碰撞

开放地址法 线性探测再散列。
再哈希法
链地址法
建立一个公共溢出区
网络编程:
tcp与udp的区别(必问)

udp调用connect有什么作用?

tcp连接中时序图,状态图,必须非常非常熟练

socket服务端的实现,select和epoll的区别(必问)
select和epoll属于I/O多路复用模型,用于持续监听多个socket,获取其IO事件。

select 调用时轮询一次所有描述字,超时时再轮询一次。如果没有描述字准备好,则返回0;中途错误返回-1;有描述字准备好,则将其对应位置为1,其他描述字置为0,返回准备好的描述字个数。
缺点
1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,
2. 效率问题, select 每次调用都会线性扫描全部的 FD 集合,这样效率就会呈现线性下降,
3. 内核 / 用户空间 内存拷贝问题

epoll(触发)
epoll采用了中断注册回调的方式,socket IO就绪时发出中断,然后将socket加入就绪队列。由三个系统调用:epoll_create,epoll_ctl,epoll_wait。
能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率:它会复用文件描述符集合来传递结果,不需要每次等待事件之前都重新准备要被侦听的文件描述符集合;获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合。
select/epoll都需要在内核和用户空间之间复制数据,epoll使用了内存映射(mmap)技术,将内核和用户空间指向同一块内存。
1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目
2. 效率提升, Epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,
3. Epoll 使用了“共享内存 ”

epoll_create 创建一个epoll句柄,size-监听套接字的数。当创建好epoll句柄后,会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗完。
epoll_ctl 事件注册函数
epoll_wait。 等待函数

select - 如果同时建立很多连接,但只有少数事件发生,这种轮询会造成效率很低;频繁从内核拷贝、复制描述字;监听描述字受限于内核的FD_SETSIZE;
epoll - 这种回调触发式操作会保证效率;不需要频繁的拷贝;监听描述字没有限止,只与系统资源有关;epoll返回时已经明确的知道哪个sokcet fd发生了事件,不用再一个个比对。

epoll哪些触发模式,有啥区别?(必须非常详尽的解释水平触发和边缘触发的区别,以及边缘触发在编程中要做哪些更多的确认)
Edge Triggered(简称ET) 仅当状态发生变化时才会通知
Level Triggered(简称LT).,采用LT模式类似于原来的select/poll操作,只要还有没有处理的事件就会一直通知.

大规模连接上来,并发模型怎么设计
TCP循环服务器 首先TCP服务器接受一个客户端的连接请求,处理连接请求,在完成这个客户端的所有请求后断开连接,然后再接受下一个客户端的请求。
TCP并发服务器 并发服务器的思想是每一个客户端的请求并不由服务器的主进程直接处理,而是服务器主进程创建一个子进程来处理。
UDP循环服务器 UDP服务器每次从套接字上读取一个客户端的数据报请求,处理接收到的UDP数据报,然后将结果返回给客户机。
多路复用I/O并发服务器 创建子进程会带来系统资源的大量消耗,为了解决这个问题,采用多路复用I/O模型的并发服务器。采用select函数创建多路复用I/O模型的并发服务器

tcp结束连接怎么握手,time_wait状态是什么,为什么会有time_wait状态?哪一方会有time_wait状态,如何避免time_wait状态占用资源(必须回答的详细)

tcp头多少字节?哪些字段?(必问)
20 源端口 目的端口 序号 确认号 数据偏移 窗口 检验和 紧急指针 选项 填充
https://www.cnblogs.com/Mr-F/p/9167917.html

8个字节

什么是滑动窗口(必问)

conntect会阻塞,怎么解决?(必考必问,提示:设置非阻塞,返回之后用select检测状态)
1.使用定时器;(最常用也最有效的一种方法)
2.采用非阻塞模式:设置非阻塞,返回之后用select检测状态。

如果select返回可读,结果只读到0字节,什么情况?
某个套接字集合中没有准备好,可能会select内存用FD_CLR清该位为0.

udp调用connect有什么作用
1.因为UDP可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom()时都必须指定目标IP和端口号。通过调用connect()建立一个端到端的连接,就可以和TCP一样使用send()/recv()传递数据,而不需要每次都指定目标IP和端口号。但是它和TCP不同的是它没有三次握手的过程。

keepalive 是什么东东?如何使用?
TCP的keepalive是侧重在保持客户端和服务端的连接,一方会不定期发送心跳包给另一方,当一方端掉的时候,没有断掉的定时发送几次心跳包,如果间隔发送几次,对方都返回的是RST,而不是ACK,那么就释放当前链接。设想一下,如果tcp层没有keepalive的机制,一旦一方断开连接却没有发送FIN给另外一方的话,那么另外一方会一直以为这个连接还是存活的,几天,几月。那么这对服务器资源的影响是很大的。

HTTP的keep-alive一般我们都会带上中间的横杠,普通的http连接是客户端连接上服务端,然后结束请求后,由客户端或者服务端进行http连接的关闭。下次再发送请求的时候,客户端再发起一个连接,传送数据,关闭连接。这么个流程反复。但是一旦客户端发送connection:keep-alive头给服务端,且服务端也接受这个keep-alive的话,两边对上暗号,这个连接就可以复用了,一个http处理完之后,另外一个http数据直接从这个连接走了。减少新建和断开TCP连接的消耗。

列举你所知道的tcp选项,并说明其作用。
1、kind=0,选项表结束(EOP)选项
一个报文段仅用一次。放在末尾用于填充,用途是说明:首部已经没有更多的消息,应用数据在下一个32位字开始处
2、kind=1,空操作(NOP)选项
没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍
3、kind=2,最大报文段长度(MSS)选项
TCP连接初始化时,通信双方使用该选项来协商最大报文段长度。TCP模块通常将MSS设置为(MTU-40)字节(减掉的这40字节包括20字节的TCP头部和20字节的IP头部)。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是一般情况),从而避免本机发生IP分片。对以太网而言,MSS值是1460(1500-40)字节。
4、kind=3,窗口扩大因子选项
TCP连接初始化时,通信双方使用该选项来协商接收窗口的扩大因子。在TCP的头部中,接收窗口大小是用16位表示的,故最大为65535字节,但实际上TCP模块允许的接收窗口大小远不止这个数(为了提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。
假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。注意,M的取值范围是0~14。我们可以通过修改 /proc/sys/net/ipv4/tcp_window_scaling 内核变量来启用或关闭窗口扩大因子选项。
和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收窗口大小就是该TCP报文段的实际接收窗口大小。当连接建立好之后,每个数据传输方向的窗口扩大因子就固定不变了。

5、kind=4,选择性确认(Selective Acknowledgment,SACK)选项

TCP通信时,如果某个TCP报文段丢失,则TCP会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK技术正是为改善这种情况而产生的,它使TCP只重新发送丢失的TCP报文段,而不用发送所有未被确认的TCP报文段。选择性确认选项用在连接初始化时,表示是否支持SACK技术。我们可以通过修改 /proc/sys/net/ipv4/tcp_sack 内核变量来启用或关闭选择性确认选项。

6、kind=5,SACK实际工作的选项
该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个4字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用8字节,所以TCP头部选项中实际上最多可以包含4个这样的不连续数据块(考虑选项类型和长度占用的2字节)。

7、kind=8,时间戳选项。
该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为TCP流量控制提供重要信息。我们可以通过修改 /proc/sys/net/ipv4/tcp_timestamps 内核变量来启用或关闭时间戳选项。

socket什么情况下可读?

db:
mysql,会考sql语言,服务器数据库大规模数据怎么设计,db各种性能指标

最后:补充一个最最重要,最最坑爹,最最有难度的一个题目:一个每秒百万级访问量的互联网服务器,每个访问都有数据计算和I/O操作,如果让你设计,你怎么设计?

1, 使用Linux epoll模型,水平触发模式(Level-Triggered);当socket可写时,会不停的触发socket可写的事件,如何处理?

2, 从socket读数据时,socket缓存里的数据,可能超过用户缓存的长度,如何处理? 例如,socket缓存有8kB的数据,而你的缓存只有2kB空间。

3, 向socket发送数据时, 可能只发送了用户缓存里的一半,如何处理?例如,需要向socket发送8kB数据,返回值只有2kB发送成功。

4, C++的虚函数是怎么实现的?

5, C++的虚函数有什么作用?

6, 非阻塞connect()如何实现?

7,sizeof()问题

class A
{
char c;
int val;
short sh;
}

class B
{
char c;
int val;
short sh;
void func1(void);
virtual func2(void);
}

sizeof(A), sizeof(B) 分别是多少?

8, 实现字符串比较函数 strcmp

9, 实现内存拷贝函数 strcpy(void*dst, char * src, size_t len)

10,条件变量的如何使用? 你使用的线程函数是什么?

11, deamon进程如何实现?

12, HTTP和CGI是什么?

13, TCP的三次握手, TIME_WAIT和CLOSE_WAIT状态是什么?

因为第7题之后的属于客观题,不打算在此写答案。 朋友们如有好的答案也欢迎跟贴。

本人在此写出自己对前6个问题的回答:

1, 使用linux epoll模型,水平触发模式(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();
面试官是在听到我介绍之后,才问我这个问题。可惜还是问我两遍。

select epoll

聚集索引和非聚集索引

网络编程