C++后台开发校招面试常见问题

C和C++语言基础

参考书籍:《C++ primer》,《effective C++》,《STL源码解析》,《深度搜索C++对象模型》html

  • extern关键字做用linux

    1. extern声明变量在在外部定义?
    2. extern修饰函数?
    3. extern C的做用?用法?
  • static关键字做用c++

    1. static修饰局部变量?
    2. static全局变量?(限定变量在一个编译单元内,一个编译单元就是指一个cpp和它包含的头文件,这个回答能够结合编译须要经历的几个过程来答)
    3. static修饰普通函数?
    4. static修饰成员变量?
    5. static修饰成员函数?
  • volatile是干啥的git

    1. 访问寄存器要比访问内存要块,所以CPU会优先访问该数据在寄存器中的存储结果,可是内存中的数据可能已经发生了改变,而寄存器中还保留着原来的结果。为了不这种状况的发生将该变量声明为volatile,告诉CPU每次都从内存去读取数据。
    2. 一个参数能够便是const又是volatile的吗?能够,一个例子是只读状态寄存器,是volatile是由于它可能被意想不到的被改变,是const告诉程序不该该试图去修改他。
  • 说说const的做用,越多越好程序员

    1. const修饰全局变量;
    2. const修饰局部变量;
    3. const修饰指针,const int *;
    4. const修饰指针指向的对象, int * const;
    5. const修饰引用作形参;
    6. const修饰成员变量,必须在构造函数列表中初始化;
    7. const修饰成员函数,说明该函数不该该修改非静态成员,可是这并非十分可靠的,指针所指的非成员对象值可能会被改变
  • new与malloc区别github

    1. new分配内存按照数据类型进行分配,malloc分配内存按照大小分配;
    2. new不只分配一段内存,并且会调用构造函数,可是malloc则不会。new的实现原理?可是还须要注意的是,以前看到过一个题说int* p = new int与int* p = new int()的区别,由于int属于C++内置对象,不会默认初始化,必须显示调用默认构造函数,可是对于自定义对象都会默认调用构造函数初始化。翻阅资料后,在C++11中二者没有区别了,本身测试的结构也都是为0;
    3. new返回的是指定对象的指针,而malloc返回的是void*,所以malloc的返回值通常都须要进行类型转化;
    4. new是一个操做符能够重载,malloc是一个库函数;
    5. new分配的内存要用delete销毁,malloc要用free来销毁;delete销毁的时候会调用对象的析构函数,而free则不会;
    6. malloc分配的内存不够的时候,能够用realloc扩容。扩容的原理?new没用这样操做;
    7. new若是分配失败了会抛出bad_malloc的异常,而malloc失败了会返回NULL。所以对于new,正确的姿式是采用try…catch语法,而malloc则应该判断指针的返回值。为了兼容不少c程序员的习惯,C++也能够采用new nothrow的方法禁止抛出异常而返回NULL;
    8. new和new[]的区别,new[]一次分配全部内存,屡次调用构造函数,分别搭配使用delete和delete[],同理,delete[]屡次调用析构函数,销毁数组中的每一个对象。而malloc则只能sizeof(int) * n;
    9. 若是不够能够继续谈new和malloc的实现,空闲链表,分配方法(首次适配原则,最佳适配原则,最差适配原则,快速适配原则)。delete和free的实现原理,free为何直到销毁多大的空间?
  • C++多态性与虚函数表web

    1. C++多态的实现?
      多态分为静态多态和动态多态。静态多态是经过重载和模板技术实现,在编译的时候肯定。动态多态经过虚函数和继承关系来实现,执行动态绑定,在运行的时候肯定。
      动态多态实现有几个条件:
      (1) 虚函数;
      (2) 一个基类的指针或引用指向派生类的对象;
      基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表。虚函数表的地址在每一个对象的首地址。查找该虚函数表中该函数的指针进行调用。
      每一个对象中保存的只是一个虚函数表的指针,C++内部为每个类维持一个虚函数表,该类的对象的都指向这同一个虚函数表。
      虚函数表中为何就能准确查找相应的函数指针呢?由于在类设计的时候,虚函数表直接从基类也继承过来,若是覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,所以能够根据指针准确找到该调用哪一个函数。
    2. 虚函数的做用?
    3. 虚函数用于实现多态,这点你们都能答上来
    4. 可是虚函数在设计上还具备封装和抽象的做用。好比抽象工厂模式。面试

    5. 动态绑定是如何实现的?
      第一个问题中基本回答了,主要都是结合虚函数表来答就行。算法

    6. 静态多态和动态多态。静态多态是指经过模板技术或者函数重载技术实现的多态,其在编译器肯定行为。动态多态是指经过虚函数技术实如今运行期动态绑定的技术。数据库

    7. 虚函数表

    8. 虚函数表是针对类的仍是针对对象的?同一个类的两个对象的虚函数表是怎么维护的?
    9. 编译器为每个类维护一个虚函数表,每一个对象的首地址保存着该虚函数表的指针,同一个类的不一样对象实际上指向同一张虚函数表。
  • 纯虚函数如何定义,为何对于存在虚函数的类中析构函数要定义成虚函数
    为了实现多态进行动态绑定,将派生类对象指针绑定到基类指针上,对象销毁时,若是析构函数没有定义为析构函数,则会调用基类的析构函数,显然只能销毁部分数据。若是要调用对象的析构函数,就须要将该对象的析构函数定义为虚函数,销毁时经过虚函数表找到对应的析构函数。

  
  
  
  
  • 1
  • 2
//纯虚函数定义 virtual ~myClass() = 0;
  • 析构函数能抛出异常吗
    答案确定是不能。

    C++标准指明析构函数不能、也不该该抛出异常。C++异常处理模型最大的特色和优点就是对C++中的面向对象提供了最强大的无缝支持。那么若是对象在运行期间出现了异常,C++异常处理模型有责任清除那些因为出现异常所致使的已经失效了的对象(也即对象超出了它原来的做用域),并释放对象原来所分配的资源, 这就是调用这些对象的析构函数来完成释放资源的任务,因此从这个意义上说,析构函数已经变成了异常处理的一部分。


(1) 若是析构函数抛出异常,则异常点以后的程序不会执行,若是析构函数在异常点以后执行了某些必要的动做好比释放某些资源,则这些动做不会执行,会形成诸如资源泄漏的问题。

(2) 一般异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数自己也抛出异常,则前一个异常还没有处理,又有新的异常,会形成程序崩溃的问题。

  • 构造函数和析构函数中调用虚函数吗?

  • 指针和引用的区别

    1. 指针保存的是所指对象的地址,引用是所指对象的别名,指针须要经过解引用间接访问,而引用是直接访问;
    2. 指针能够改变地址,从而改变所指的对象,而引用必须从一而终;
    3. 引用在定义的时候必须初始化,而指针则不须要;
    4. 指针有指向常量的指针和指针常量,而引用没有常量引用;
    5. 指针更灵活,用的好威力无比,用的很差到处是坑,而引用用起来则安全多了,可是比较死板。
  • 指针与数组千丝万缕的联系

    1. 一个一维int数组的数组名其实是一个int* const 类型;
    2. 一个二维int数组的数组名其实是一个int (*const p)[n];
    3. 数组名作参数会退化为指针,除了sizeof
  • 智能指针是怎么实现的?何时改变引用计数?

    1. 构造函数中计数初始化为1;
    2. 拷贝构造函数中计数值加1;
    3. 赋值运算符中,左边的对象引用计数减一,右边的对象引用计数加一;
    4. 析构函数中引用计数减一;
    5. 在赋值运算符和析构函数中,若是减一后为0,则调用delete释放对象。
    6. share_prt与weak_ptr的区别?
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
//share_ptr可能出现循环引用,从而致使内存泄露 class A { public: share_ptr<B> p; }; class B { public: share_ptr<A> p; } int main() { while(true) { share_prt<A> pa(new A()); //pa的引用计数初始化为1 share_prt<B> pb(new B()); //pb的引用计数初始化为1 pa->p = pb; //pb的引用计数变为2 pb->p = pa; //pa的引用计数变为2 } //假设pa先离开,引用计数减一变为1,不为0所以不会调用class A的析构函数,所以其成员p也不会被析构,pb的引用计数仍然为2; //同理pb离开的时候,引用计数也不能减到0 return 0; } /* ** weak_ptr是一种弱引用指针,其存在不会影响引用计数,从而解决循环引用的问题 */
  • C++四种类型转换static_cast, dynamic_cast, const_cast, reinterpret_cast

    1. const_cast用于将const变量转为非const
    2. static_cast用的最多,对于各类隐式转换,非const转const,void*转指针等, static_cast能用于多态想上转化,若是向下转能成功可是不安全,结果未知;
    3. dynamic_cast用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,若是是非法的对于指针返回NULL,对于引用抛异常。要深刻了解内部转换的原理。
    4. reinterpret_cast几乎什么均可以转,好比将int转指针,可能会出问题,尽可能少用;
    5. 为何不使用C的强制转换?C的强制转换表面上看起来功能强大什么都能转,可是转化不够明确,不能进行错误检查,容易出错。
  • 内存对齐的原则

    1. 从0位置开始存储;
    2. 变量存储的起始位置是该变量大小的整数倍;
    3. 结构体总的大小是其最大元素的整数倍,不足的后面要补齐;
    4. 结构体中包含结构体,从结构体中最大元素的整数倍开始存;
    5. 若是加入pragma pack(n) ,取n和变量自身大小较小的一个。
  • 内联函数有什么优势?内联函数与宏定义的区别?

    1. 宏定义在预编译的时候就会进行宏替换;
    2. 内联函数在编译阶段,在调用内联函数的地方进行替换,减小了函数的调用过程,可是使得编译文件变大。所以,内联函数适合简单函数,对于复杂函数,即便定义了内联编译器可能也不会按照内联的方式进行编译。
    3. 内联函数相比宏定义更安全,内联函数能够检查参数,而宏定义只是简单的文本替换。所以推荐使用内联函数,而不是宏定义。
    4. 使用宏定义函数要特别注意给全部单元都加上括号,#define MUL(a, b) a * b,这很危险,正确写法:#define MUL(a, b) ((a) * (b))
  • C++内存管理

    1. C++内存分为那几块?(堆区,栈区,常量区,静态和全局区)
    2. 每块存储哪些变量?
    3. 学会迁移,能够说到malloc,从malloc说到操做系统的内存管理,说道内核态和用户态,而后就什么高端内存,slab层,伙伴算法,VMA能够巴拉巴拉了,接着能够迁移到fork()。
  • STL里的内存池实现
    STL内存分配分为一级分配器和二级分配器,一级分配器就是采用malloc分配内存,二级分配器采用内存池。

二级分配器设计的很是巧妙,分别给8k,16k,…, 128k等比较小的内存片都维持一个空闲链表,每一个链表的头节点由一个数组来维护。须要分配内存时从合适大小的链表中取一块下来。假设须要分配一块10K的内存,那么就找到最小的大于等于10k的块,也就是16K,从16K的空闲链表里取出一个用于分配。释放该块内存时,将内存节点归还给链表。
若是要分配的内存大于128K则直接调用一级分配器。
为了节省维持链表的开销,采用了一个union结构体,分配器使用union里的next指针来指向下一个节点,而用户则使用union的空指针来表示该节点的地址。

  • STL里set和map是基于什么实现的。红黑树的特色?

    1. set和map都是基于红黑树实现的。
    2. 红黑树是一种平衡二叉查找树,与AVL树的区别是什么?AVL树是彻底平衡的,红黑树基本上是平衡的。
    3. 为何选用红黑数呢?由于红黑数是平衡二叉树,其插入和删除的效率都是N(logN),与AVL相比红黑数插入和删除最多只须要3次旋转,而AVL树为了维持其彻底平衡性,在坏的状况下要旋转的次数太多。
      红黑树的定义:
      (1) 节点是红色或者黑色;
      (2) 父节点是红色的话,子节点就不能为红色;
      (3) 从根节点到每一个页子节点路径上黑色节点的数量相同;
      (4) 根是黑色的,NULL节点被认为是黑色的。
  • STL里的其余数据结构和算法实现也要清楚
    这个问题,把STL源码剖析好好看看,不只面试不慌,本身对STL的使用也会上升一个层次。

  • 必须在构造函数初始化式里进行初始化的数据成员有哪些
    (1) 常量成员,由于常量只能初始化不能赋值,因此必须放在初始化列表里面
    (2) 引用类型,引用必须在定义的时候初始化,而且不能从新赋值,因此也要写在初始化列表里面
    (3) 没有默认构造函数的类类型,由于使用初始化列表能够没必要调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化

  • 模板特化
    (1) 模板特化分为全特化和偏特化,模板特化的目的就是对于某一种变量类型具备不一样的实现,所以须要特化版本。例如,在STL里迭代器为了适应原生指针就将原生指针进行特化。

  • 定位内存泄露
    (1)在windows平台下经过CRT中的库函数进行检测;
    (2)在可能泄漏的调用先后生成块的快照,比较先后的状态,定位泄漏的位置
    (3)Linux下经过工具valgrind检测

  • 手写strcpy

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
char* strcpy(char* dst, const char* src) { assert(dst); assert(src); char* ret = dst; while((*dst++ = *src++) != '\0'); return ret; } //该函数是没有考虑重叠的 char* strcpy(char* dst, const char* src) { assert((dst != NULL) && (src != NULL)); char* ret = dst; int size = strlen(src) + 1; if(dst > src || dst < src + len) { dst = dst + size - 1; src = src + size - 1; while(size--) { *dst-- = *src--; } } else { while(size--) { *dst++ = *src++; } } return ret; }
  • 手写memcpy函数
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
void* memcpy(void* dst, const void* src, size_t size) { if(dst == NULL || src == NULL) { return NULL; } void* res = dst; char* pdst = (char*)dst; char* psrc = (char*)src; if(pdst > psrc && pdst < psrc + size) //重叠 { pdst = pdst + size - 1; psrc = pdst + size - 1; while(size--) { *pdst-- = *psrc--; } } else //无重叠 { while(size--) { *dst++ = *src++; } } return ret; }
  • 手写strcat函数
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
char* strcat(char* dst, const char* src) { char* ret = dst; while(*dst != '\0') ++dst; while((*dst++ = *src) != '\0'); return ret; }
  • 手写strcmp函数
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
int strcmp(const char* str1, const char* str2) { while(*str1 == *str2 && *str1 != '\0') { ++str1; ++str2; } return *str1 - *str2; }

数据结构与算法

这一块考察范围太广,主要靠多刷题吧,牛客网,剑指OFFER,LeetCode等。

Hash表

  • Hash表实现(拉链和分散地址)
  • Hash策略常见的有哪些?
  • STL中hash_map扩容发生什么?
    (1) 建立一个新桶,该桶是原来桶两倍大最接近的质数(判断n是否是质数的方法:用n除2到 s q r t ( n ) 范围内的数) ;
    (2) 将原来桶里的数经过指针的转换,插入到新桶中(注意STL这里作的很精细,没有直接将数据从旧桶遍历拷贝数据插入到新桶,而是经过指针转换)
    (3) 经过swap函数将新桶和旧桶交换,销毁新桶。

  • 二叉树结构,二叉查找树实现;
  • 二叉树的六种遍历;
  • 二叉树的按层遍历;
  • 递归是解决二叉树相关问题的神级方法;
  • 树的各类常见算法题(http://blog.csdn.net/xiajun07061225/article/details/12760493);

  • 什么是红黑树?

    • 节点为红色或者黑色;
    • 根节点为黑色;
    • 从根节点到每一个叶子节点通过的黑色节点个数的和相同;
    • 若是父节点为红色,那么其子节点就不能为红色。
  • 红黑树与AVL树的区别

    • 红黑树与AVL树都是平衡树,可是AVL是彻底平衡的(平衡就是值树中任意节点的左子树和右子树高度差不超过1);
    • 红黑树效率更高,由于AVL为了保证其彻底平衡,插入和删除的时候在最坏的状况下要旋转logN次,而红黑树插入和删除的旋转次数要比AVL少。
  • Trie树(字典树)

    • 每一个节点保存一个字符
    • 根节点不保存字符
    • 每一个节点最多有n个子节点(n是全部可能出现字符的个数)
    • 查询的复杂父为O(k),k为查询字符串长度

链表

  • 链表和插入和删除,单向和双向链表都要会
  • 链表的问题考虑多个指针和递归
    (1) 反向打印链表(递归)
    (2) 打印倒数第K个节点(先后指针)
    (3) 链表是否有环(快慢指针)等等。b ggg

栈和队列

  • 队列和栈的区别?(从实现,应用,自身特色多个方面来阐述,不要只说一个先入先出,先入后出,这个你会别人也会,要展示出你比别人掌握的更深)
  • 典型的应用场景

海量数据问题

  • 十亿整数(随机生成,可重复)中前K最大的数
    相似问题的解决方法思路:首先哈希将数据分红N个文件,而后对每一个文件创建K个元素最小/大堆(根据要求来选择)。最后将文件中剩余的数插入堆中,并维持K个元素的堆。最后将N个堆中的元素合起来分析。能够采用归并的方式来合并。在归并的时候为了提升效率还须要建一个N个元素构成的最大堆,先用N个堆中的最大值填充这个堆,而后就是弹出最大值,指针后移的操做了。固然这种问题在如今的互联网技术中,通常就用map-reduce框架来作了。
    大数据排序相同的思路:先哈希(哈希是好处是分布均匀,相同的数在同一个文件中),而后小文件装入内存快排,排序结果输出到文件。最后建堆归并。

  • 十亿整数(随机生成,可重复)中出现频率最高的一千个

排序算法

  • 排序算法固然是基础内容了,必须至少能快速写出,快排,建堆,和归并
  • 每种算法的时间空间复杂度,最好最差平均状况

位运算

布隆过滤器

几十亿个数常常要查找某一个数在不在里面,使用布隆过滤器,布隆过滤器的原理。布隆过滤器可能出现误判,怎么保证无偏差?

网络与TCP/IP

参考书籍:《图解TCP/IP》,《TCP/IP详解 卷一》,《图解HTTP》,《HTTP权威指南》

  
  
  
  
  • 1
  • 2
(1) Ping是经过发送ICMP报文回显请求实现。 (2) TraceRoute经过发送UDP报文,设置目的端口为一个不可能的值,将IP首部中的TTL分别设置从1N,每次逐个增长,若是收到端口不可达,说明到达目的主机,若是是由于TTL跳数超过,路由器会发送主机不可达的ICMP报文。

HTTP

http/https 1.0、1.一、2.0

  1. http的主要特色:
    简单快速:当客户端向服务器端发送请求时,只是简单的填写请求路径和请求方法便可,而后就能够经过浏览器或其余方式将该请求发送就好了
    灵活: HTTP 协议容许客户端和服务器端传输任意类型任意格式的数据对象
    无链接:无链接的含义是限制每次链接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开链接,采用这种方式能够节省传输时间。(当今多数服务器支持Keep-Alive功能,使用服务器支持长链接,解决无链接的问题)
    无状态:无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即客户端发送HTTP请求后,服务器根据请求,会给咱们发送数据,发送完后,不会记录信息。(使用 cookie 机制能够保持 session,解决无状态的问题)

  2. http1.1的特色
    a、默认持久链接节省通讯量,只要客户端服务端任意一端没有明确提出断开TCP链接,就一直保持链接,能够发送屡次HTTP请求
    b、管线化,客户端能够同时发出多个HTTP请求,而不用一个个等待响应
    c、断点续传

  3. http2.0的特色
    a、HTTP/2采用二进制格式而非文本格式
    b、HTTP/2是彻底多路复用的,而非有序并阻塞的——只需一个HTTP链接就能够实现多个请求响应
    c、使用报头压缩,HTTP/2下降了开销
    d、HTTP/2让服务器能够将响应主动“推送”到客户端缓存中

get/post 区别

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
区别一: get重点在从服务器上获取资源,post重点在向服务器发送数据; 区别二: get传输数据是经过URL请求,以field(字段)= value的形式,置于URL后,并用"?"链接,多个请求数据间用"&"链接,如http://127.0.0.1/Test/login.action?name=admin&password=admin,这个过程用户是可见的; post传输数据经过Http的post机制,将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的; 区别三: Get传输的数据量小,由于受URL长度限制,但效率较高; Post能够传输大量数据,因此上传文件时只能用Post方式; 区别四: get是不安全的,由于URL是可见的,可能会泄露私密信息,如密码等; postget安全性较高;

返回状态码

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
200:请求被正常处理 204:请求被受理但没有资源能够返回 206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中经过Content-Range指定范围的资源。 301:永久性重定向 302:临时重定向 303:与302状态码有类似功能,只是它但愿客户端在请求一个URI的时候,能经过GET方法重定向到另外一个URI上 304:发送附带条件的请求时,条件不知足时返回,与重定向无关 307:临时重定向,与302相似,只是强制要求使用POST方法 400:请求报文语法有误,服务器没法识别 401:请求须要认证 403:请求的对应资源禁止被访问 404:服务器没法找到对应资源 500:服务器内部错误 503:服务器正忙

http 协议头相关

http数据由请求行,首部字段,空行,报文主体四个部分组成
首部字段分为:通用首部字段,请求首部字段,响应首部字段,实体首部字段

https与http的区别?如何实现加密传输?

  • https就是在http与传输层之间加上了一个SSL
  • 对称加密与非对称加密

浏览器中输入一个URL发生什么,用到哪些协议?

浏览器中输入URL,首先浏览器要将URL解析为IP地址,解析域名就要用到DNS协议,首先主机会查询DNS的缓存,若是没有就给本地DNS发送查询请求。DNS查询分为两种方式,一种是递归查询,一种是迭代查询。若是是迭代查询,本地的DNS服务器,向根域名服务器发送查询请求,根域名服务器告知该域名的一级域名服务器,而后本地服务器给该一级域名服务器发送查询请求,而后依次类推直到查询到该域名的IP地址。DNS服务器是基于UDP的,所以会用到UDP协议。

获得IP地址后,浏览器就要与服务器创建一个http链接。所以要用到http协议,http协议报文格式上面已经提到。http生成一个get请求报文,将该报文传给TCP层处理。若是采用https还会先对http数据进行加密。TCP层若是有须要先将HTTP数据包分片,分片依据路径MTU和MSS。TCP的数据包而后会发送给IP层,用到IP协议。IP层经过路由选路,一跳一跳发送到目的地址。固然在一个网段内的寻址是经过以太网协议实现(也能够是其余物理层协议,好比PPP,SLIP),以太网协议须要直到目的IP地址的物理地址,有须要ARP协议。

安全相关

至少了解攻击的原理和基本的防护方法,常见的攻击方法有一下几种

  • SQL注入
  • XSS
  • CSRF
  • SYN洪水攻击
  • APR欺骗

数据库

主要参考书籍:《数据库系统概念》,《高性能MySQL》

  • SQL语言(内外链接,子查询,分组,汇集,嵌套,逻辑)
  • MySQL索引方法?索引的优化?
  • InnoDB与MyISAM区别?
  • 事务的ACID
  • 事务的四个隔离级别
  • 查询优化(从索引上优化,从SQL语言上优化)
  • B-与B+树区别?
  • MySQL的联合索引(又称多列索引)是什么?生效的条件?
  • 分库分表

Linux

主要参考书籍:《现代操做系统》,《APUE》,《UNP》,《LINUX内核设计与实现》,《深刻理解LINUX内核》

进程与线程

(1) 进程与线程区别?
(2) 线程比进程具备哪些优点?
(3) 何时用多进程?何时用多线程?
(4) LINUX中进程和线程使用的几个函数?
(5) 线程同步?
在Windows下线程同步的方式有:互斥量,信号量,事件,关键代码段
在Linux下线程同步的方式有:互斥锁,自旋锁,读写锁,屏障(并发完成同一项任务时,屏障的做用特别好使)
知道这些锁之间的区别,使用场景?

进程间通信方式

管道( pipe ):管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程关系。

命名管道 (FIFO) : 有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。

信号量:信号量用于实现进程间的互斥与同步,而不是用于存储进程间通讯数据,有XSI信号量和POSIX信号量,POSIX信号量更加完善。

消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存( shared memory ) :共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的 IPC 方式,它是针对其余进程间通讯方式运行效率低而专门设计的。它每每与其余通讯机制,如信号两,配合使用,来实现进程间的同步和通讯。(原理必定要清楚,常考)

信号 ( sinal ) : 信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生,常见的信号。

套接字( socket ) : 套解口也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样及其间的进程通讯。

  • 匿名管道与命名管道的区别:匿名管道只能在具备公共祖先的两个进程间使用。
  • 共享文件映射mmap
    mmap创建进程空间到文件的映射,在创建的时候并不直接将文件拷贝到物理内存,一样采用缺页终端。mmap映射一个具体的文件能够实现任意进程间共享内存,映射一个匿名文件,能够实现父子进程间共享内存。

  • 常见的信号有哪些?:SIGINT,SIGKILL(不能被捕获),SIGTERM(能够被捕获),SIGSEGV,SIGCHLD,SIGALRM

内存管理

  1. 虚拟内存的做用?
  2. 虚拟内存的实现?
  3. 操做系统层面对内存的管理?
  4. 内存池的做用?STL里内存池如何实现
  5. 进程空间和内核空间对内存的管理不一样?
  6. Linux的slab层,VAM?
  7. 伙伴算法
  8. 高端内存

进程调度

  1. Linux进程分为两种,实时进程和非实时进程;
  2. 优先级分为静态优先级和动态优先级,优先级的范围;
  3. 调度策略,FIFO,LRU,时间片轮转
  4. 交互进程经过平均睡眠时间而被奖励;

死锁

(1) 死锁产生的条件;
(2) 死锁的避免;

命令行

  • Linux命令 在一个文件中,倒序打印第二行前100个大写字母
  
  
  
  
  • 1
cat filename | head -n 2 | tail -n 1 | grep '[[:upper:]]' -o | tr -d '\n'| cut -c 1-100 | rev
  • 与CPU,内存,磁盘相关的命令(top,free, df, fdisk)

  • 网络相关的命令netstat,tcpdump等

  • sed, awk, grep三个超强大的命名,分别用与格式化修改,统计,和正则查找

  • ipcs和ipcrm命令

  • 查找当前目录以及字母下以.c结尾的文件,且文件中包含”hello world”的文件的路径

  • 建立定时任务

IO模型

  • 五种IO模型:阻塞IO,非阻塞IO,IO复用,信号驱动式IO,异步IO

  • select,poll,epoll的区别

select:是最初解决IO阻塞问题的方法。用结构体fd_set来告诉内核监听多个文件描述符,该结构体被称为描述符集。由数组来维持哪些描述符被置位了。对结构体的操做封装在三个宏定义中。经过轮寻来查找是否有描述符要被处理,若是没有返回**
存在的问题:
1. 内置数组的形式使得select的最大文件数受限与FD_SIZE;
2. 每次调用select前都要从新初始化描述符集,将fd从用户态拷贝到内核态,每次调用select后,都须要将fd从内核态拷贝到用户态;
3. 轮寻排查当文件描述符个数不少时,效率很低;

poll:经过一个可变长度的数组解决了select文件描述符受限的问题。数组中元素是结构体,该结构体保存描述符的信息,每增长一个文件描述符就向数组中加入一个结构体,结构体只须要拷贝一次到内核态。poll解决了select重复初始化的问题。轮寻排查的问题未解决。**

epoll:轮寻排查全部文件描述符的效率不高,使服务器并发能力受限。所以,epoll采用只返回状态发生变化的文件描述符,便解决了轮寻的瓶颈。
- 为何使用IO多路复用,最主要的缘由是什么?
- epoll有两种触发模式?这两种触发模式有什么区别?编程的时候有什么区别?
- 上一题中编程的时候有什么区别,是在边缘触发的时候要把套接字中的数据读干净,那么当有多个套接字时,在读的套接字一直不停的有数据到达,如何保证其余套接字不被饿死(面试网易游戏的时候问的一个问题,答不上来,印象贼深入)。

  1. select/poll/epoll区别
  2. 几种网络服务器模型的介绍与比较
  3. epoll为何这么快(搞懂这篇文章,关于IO复用的问题就信手拈来了)

线程池,内存池 本身动手实现一遍

Linux的API

  • fork与vfork区别
    fork和vfork都用于建立子进程。可是vfork建立子进程后,父进程阻塞,直到子进程调用exit()或者excle()。
    对于内核中过程fork经过调用clone函数,而后clone函数调用do_fork()。do_fork()中调用copy_process()函数先复制task_struct结构体,而后复制其余关于内存,文件,寄存器等信息。fork采用写时拷贝技术,所以子进程和父进程的页表指向相同的页框。可是vfork不须要拷贝页表,由于父进程会一直阻塞,直接使用父进程页表。

  • exit()与_exit()区别
    exit()清理后进入内核,_exit()直接陷入内核。

  • 孤儿进程与僵死进程

    1. 孤儿进程是怎么产生的?
    2. 僵死进程是怎么产生的?
    3. 僵死进程的危害?
    4. 如何避免僵死进程的产生?
  • Linux是如何避免内存碎片的

    1. 伙伴算法,用于管理物理内存,避免内存碎片;
    2. 高速缓存Slab层用于管理内核分配内存,避免碎片。
  • 共享内存的实现原理?
    共享内存实现分为两种方式一种是采用mmap,另外一种是采用XSI机制中的共享内存方法。mmap是内存文件映射,将一个文件映射到进程的地址空间,用户进程的地址空间的管理是经过vm_area_struct结构体进行管理的。mmap经过映射一个相同的文件到两个不一样的进程,就能实现这两个进程的通讯,采用该方法能够实现任意进程之间的通讯。mmap也能够采用匿名映射,不指定映射的文件,可是只能在父子进程间通讯。XSI的内存共享实际上也是经过映射文件实现,只是其映射的是一种特殊文件系统下的文件,该文件是不能经过read和write访问的。

两者区别:

一、 系统V共享内存中的数据,历来不写入到实际磁盘文件中去;而经过mmap()映射普通文件实现的共享内存通讯能够指定什么时候将数据写入磁盘文件中。注:前面讲到,系统V共享内存机制实际是经过映射特殊文件系统shm中的文件实现的,文件系统shm的安装点在交换分区上,系统从新引导后,全部的内容都丢失。

二、 系统V共享内存是随内核持续的,即便全部访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核从新引导以前,对该共享内存区域的任何改写操做都将一直保留。

三、 经过调用mmap()映射普通文件进行进程间通讯时,必定要注意考虑进程什么时候终止对通讯的影响。而经过系统V共享内存实现通讯的进程则否则。注:这里没有给出shmctl的使用范例,原理与消息队列大同小异。

  • 系统调用与库函数(open, close, create, lseek, write, read)

  • 同步方法有哪些?

    1. 互斥锁,自旋锁,信号量,读写锁,屏障
    2. 互斥锁与自旋锁的区别:互斥锁得不到资源的时候阻塞,不占用cpu资源。自旋锁得不到资源的时候,不停的查询,而然占用cpu资源。
    3. 死锁

其余

  • ++i是不是原子操做
    明显不是,++i主要有三个步骤,把数据从内存放在寄存器上,在寄存器上进行自增,把数据从寄存器拷贝会内存,每一个步骤均可能被中断。

  • 判断大小端

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
union un { int i; char ch; }; void fun() { union un test; test.i = 1; if(ch == 1) cout << "小端" << endl; else cout << "大端" << endl; }

设计模式

分布式系统

部分问题只是列出思考的概要,去书中和实践中找到这些问题的答案才能真正的消化。

PS:欢迎转载,转载请标明出处!

相关文章
相关标签/搜索