Linux下C++后台开发面试题

C/C++语言基础

  • extern 关键字作用 参考链接

    1. extern声明变量或者函数时,它告诉编译器去其他文件中寻找定义或者实现。
    2. extern “C”的作用:为了实现C++、C的混合编程,使C++中能够调用C写的函数。它告诉C++编译器按照C的编译、链接规范来编译。因为C++编译器为了实现函数重载的功能,对函数名的编译和C编译器不一样,所以要加上extern “C”.
  • static关键字作用 参考链接

    1. 一种是面向过程的程序设计中的static,不涉及类。修饰变量时表示的是一个静态变量,在全局数据区分配内存,只在文件内可见,而文件之外是不可见的。修饰函数中表示静态函数,不能在其他文件中使用。
    2. 一种是面向对象程序设计中的static, 涉及到类。在类的数据成员前面加上static关键字,就是类的静态成员变量,是属于类的,在全局数据区分配内存,被所有的对象共享。在类的函数前面加上static,表示静态成员函数。不和任何对象有联系,没有this指针。
  • volatile关键字的作用 参考链接
    volatile是一种类型修饰符,遇到这个关键字声明的变量, 编译器对访问该变量的代码不再进行优化。访问寄存器要比访问内存块,因此CPU总是优先访问寄存器。但是有时候可能内存中的数据发生了变化,而寄存器还保存原来的值。为了防止这种情况,使用volatile来声明变量时,系统总是从内存中读取数据,而不会从寄存器中读取。

  • const关键字的作用

    1. const关键字所修饰的表示的是一个常量。取代C中的宏定义,声明的时候必须初始化。const修饰的变量不可以被修改。
    2. const修饰指针时。const int * 和int * const
    3. const 修饰引用或指针做函数的形参
    4. const 修改引用或指针做函数的返回值
    5. const修饰成员变量时,必须在构造函数列表中初始化
    6. const修饰成员函数时,说明该函数不应该修改非静态成员
  • new与malloc的区别

    1. new 分配内存按照数据类型进行分配,malloc分配内存按照大小分配。
    2. 给对象分配内存时,new 会调用构造函数,而malloc不会
    3. new返回的是指向对象的指针,而malloc返回的是(void * )
    4. new是一个操作符可以重载,而malloc是一个库函数
    5. new分配的内存用delete销毁,malloc用free销毁。delete销毁时调用析构函数,而free不会。
    6. new如果分配失败会抛出bad_malloc异常,而malloc失败会返回NULL。
    7. malloc分配内存不够时,可以用realloc扩容,new不可以。
  • #define和const定义常量的区别

    1. define宏是在预处理阶段展开,const常量是在编译运行时候使用。
    2. define宏不做类型检查,仅仅是展开替换。const常量有具体的类型,编译的时候执行类型检查。
    3. const定义的变量需要分配内存,在常量区分配。define定义的不占有内存。
  • 指针和引用的区别

    1. 指针保存的是对象的地址,引用是对象的别名
    2. 指针需要通过解引用来间接访问,而引用是直接访问。
    3. 引用在定义的时候必须初始化,而指针则不需要。
    4. 指针在赋值后还可以改变,而引用不能改变。
    5. 有常量指针,而没有常量引用.
  • 结构体中内存对齐?

    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))
  • 模板特例化
    模板特化分为全特化和偏特化,模板特化的目的就是对于某一种变量类型具有不同的实现,因此需要特化版本。例如,在STL里迭代器为了适应原生指针就将原生指针进行特化

  • 纯虚函数的介绍?

    1. 纯虚函数是用在虚函数后加上 = 0来声明的。
    2. 纯虚函数只提供声明,没有实现。纯虚函数是起到声明接口的作用。
    3. 声明虚函数的类是抽象类,不能实例化为对象。继承抽象类的子类必须重写类的纯虚函数。否则该子类还是抽象类。
    4. 虽然抽象类不能实例化,但是抽象类可以有构造函数。
C++多态性与虚函数表
  • C++多态的实现?
    多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。

  • 虚函数的作用
    1.虚函数用于实现多态。
    2.虚函数在设计上还具有封装和抽象的作用。比如抽象工厂模式。

  • 谈谈虚函数表?

    1. 基类指针在调用所指对象的虚函数时,就会去查找该对象的虚函数表。虚函数表的地址在每个对象的首地址。查找该虚函数表中该函数的指针进行调用。
    2. 每个对象中保存的只是一个虚函数表的指针,C++内部为每一个类维持一个虚函数表,该类的对象的vptr指针都指向这同一个虚函数表。
    3. 虚函数表中为什么就能准确查找相应的函数指针呢?因为继承的时候,派生类的虚函数表直接从基类也继承过来,如果覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,因此可以根据指针准确找到该调用哪个函数。
  • 为什么要有虚析构函数?
    当基类指针指向派生类对象时,delete指针销毁对象时,如果析构函数没有定义为虚析构函数,则会调用基类的析构函数,显然只能销毁部分数据。如果要调用所指对象的析构函数,就需要将该对象的析构函数定义为虚函数,销毁时通过虚函数表找到对应的析构函数。

  • 构造函数可以是虚函数吗?
    不可以。因为虚函数的执行依赖于虚函数表,调用虚函数需要通过对象中指向虚函数表的vptr指针。而对象的vptr指针是在构造函数中初始化的。所以,如果构造函数是虚函数,构造对象时,vptr指针还没有初始化,将没办法进行。

  • 析构函数能够抛出异常吗?

    1. 如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
    2. 通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。
  • 必须在构造函数初始化式里进行初始化的数据成员有哪些?

    1. const常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
    2. 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
    3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化
C++11新特性
  • 智能指针怎么实现的?

    1. 构造函数中计数初始化为1
    2. 拷贝构造函数中计数值加1
    3. 赋值运算符中,左边的对象引用计数减一,右边的对象引用计数加一
    4. 析构函数中引用计数减一
    5. 在赋值运算符和析构函数中,如果减一后为0,则调用delete释放对象
  • 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的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错
C++内存管理
  • C++内存分为那几块?(栈区,堆区,常量区,静态/全局数据区区,代码区)
  • 堆区和栈区的区别?

    1. 申请方式上:栈区由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。堆区一般由程序员手动分配释放。
    2. 申请大小的限制:堆的空间比较灵活,也比较大
    3. 申请效率上:栈由系统自动分配,速度较快。堆是由new/malloc分配的内存,一般速度比较慢,而且容易产生内存碎片
    4. 内存空间的增长方式:栈是从高地址往低地址增长,而堆是从低地址往高地址增长。
  • 如何定位内存泄漏?

    1. 在windows平台下通过CRT中的库函数进行检测
    2. 在可能泄漏的调用前后生成块的快照,比较前后的状态,定位泄漏的位置
    3. Linux下通过工具valgrind检测
  • 内存泄露定位、检测原理?
    自己重载new操作符,用list或者map对分配的内存进行收集,如果释放了,就删除节点,最后检测容器里面还有没有节点,有就是泄露了。可以在重载的new中记录是哪一行的代码分配的内存被泄露了,这样就可以定位到内存泄露的位置。

  • 如何判断大小端?

union un
{
  int i;
  char ch;
};
void fun()
{
  union un test;
  test.i = 1;
  if(test.ch == 1)
    cout << "小端" << endl;
  else
    cout << "大端" << endl;
}
  • ++i是否是原子操作?
    明显不是,++i主要有三个步骤,把数据从内存放在寄存器上,在寄存器上进行自增,把数据从寄存器拷贝会内存,每个步骤都可能被中断。
常用的库函数实现
  • 没有考虑重叠的strcpy
char* strcpy(char *dst, const char *src)
{
  assert(dst != NULL);
  assert(src != NULL);
  char *ret = dst;
  while((*dst++ = *src++) != '\0');
  return ret;
}

考虑重叠的strcpy

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
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函数
char strcat(char *dst, const char *src)
{
    char *ret = dst;
    while(*dst != '\0')
    ++dst;
    while((*dst++ = *src++) != '\0');
    return ret;

}
  • 手写strcmp
int strcmp(const char *str1, const char *str2)
{
  while((*str1 == *str2) && (*str1 != '\0'))
  {
      ++str1;
      ++str2;
  }
    return *str1 - *str2;
}

STL标准库

  • STL中内存池的实现

    1. STL内存分配分为一级分配器和二级分配器,一级分配器就是采用malloc分配内存,二级分配器采用内存池。
    2. 二级分配器设计的非常巧妙,分别给8k,16k,…, 128k等比较小的内存片都维持一个空闲链表,每个链表的头节点由一个数组来维护。需要分配内存时从合适大小的链表中取一块下来。假设需要分配一块10K的内存,那么就找到最小的大于等于10k的块,也就是16K,从16K的空闲链表里取出一个用于分配。释放该块内存时,将内存节点归还给链表。
    3. 如果要分配的内存大于128K则直接调用一级分配器。
    4. 为了节省维持链表的开销,采用了一个union结构体,分配器使用union里的next指针来指向下一个节点,而用户则使用union的空指针来表示该节点的地址。
  • STL里set和map是基于什么实现的。红黑树的特点?

    1. set和map都是基于红黑树实现的
    2. 红黑树的定义:
      (1) 节点是红色或者黑色;
      (2) 父节点是红色的话,子节点就不能为红色;
      (3) 从根节点到每个页子节点路径上黑色节点的数量相同;
      (4) 根是黑色的,NULL节点被认为是黑色的。
    3. 红黑树是一种平衡二叉查找树,与AVL树的区别是什么?(AVL树是完全平衡的,红黑树基本上是平衡的。)
    4. 为什么选用红黑数呢?因为红黑数是平衡二叉树,其插入和删除的效率都是N(logN),与AVL相比红黑数插入和删除最多只需要3次旋转,而AVL树为了维持其完全平衡性,在坏的情况下要旋转的次数太多。

Linux操作系统

进程与线程
  • 进程和线程的联系区别?

    1. 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
    2. 一个程序至少有一个进程,一个进程至少有一个线程,线程的划分尺度小于进程
    3. 进程在执行过程中拥有独立的内存单元,而多个线程共享所在进程的地址空间和其它资源
    4. 线程执行开销小,但不利于资源的管理和保护,而进程正相反
  • 什么时候用多进程?什么时候用多线程?
    进程和线程对比

    1. 需要频繁创建销毁的优先用线程
    2. 需要进行大量计算的优先使用线程
    3. 强相关的处理用线程,弱相关的处理用进程
      什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
      一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
      当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
    4. 可能要扩展到多机分布的用进程,多核分布的用线程
  • LINUX中进程和线程使用的几个函数?

    1. 进程:fork, exec, wait
    2. 线程:pthread_create、pthread_exit、pthread_jion、pthread_kill; pthread_mutex_lock、pthread_mutex_unlock、pthread_cond_init、pthread_cond_signal、pthread_cond_wait
  • Linux线程同步
    互斥锁、条件变量、读写锁、信号量

  • 互斥锁和自旋锁的区别?
    互斥锁得不到资源的时候阻塞,不占用cpu资源。自旋锁得不到资源的时候,不停的查询,而然占用cpu资源。

  • Linux进程间通讯

    1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 命名管道 (FIFO) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    3. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
    4. 信号量:信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据,有XSI信号量和POSIX信号量,POSIX信号量更加完善。
    5. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。(原理一定要清楚,常考)
    6. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生,常见的信号。
    7. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
  • 匿名管道与命名管道的区别?
    匿名管道只能在具有公共祖先的两个进程间使用

  • 共享内存映射mmap?
    mmap是内存文件映射,将一个文件映射到进程的地址空间,用户进程的地址空间的管理是通过vm_area_struct结构体进行管理的。mmap通过映射一个相同的文件到两个不同的进程,就能实现这两个进程的通信,采用该方法可以实现任意进程之间的通信。mmap也可以采用匿名映射,不指定映射的文件,但是只能在父子进程间通信。

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

  • 进程调度算法?
    先来先服务FIFO、最高优先级算法HPF、时间片轮转算法、多级队列反馈法

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

  • 什么是孤儿进程?
    一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作

  • 什么是僵尸进程?
    一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

  • 僵尸进程的危害?
    僵死进程的PID还占据着。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。

  • 如何避免僵尸进程?

    1. 父进程调用wait/waitpid函数等待子进程结束然后回收。
    2. 通过信号机制。子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
    3. fork两次。子进程退出,从而孙进程成为孤儿进程,他的父进程变为init进程,通过init进程可以处理僵尸进程。
  • Linux定时器的实现
    参考Linux下定时器实现
    1.Select实现:

    void seconds_sleep(unsigned seconds){
    struct timeval tv;
    tv.tv_sec=seconds;
    tv.tv_usec=0;
    int err;
    do{
       err=select(0,NULL,NULL,NULL,&tv);
    }while(err<0 && errno==EINTR);
    }

    2、settimer,信号实现
    3、最小堆、红黑树
    4、时间轮

Linux内存管理
  • 虚拟内存的作用和实现?

  • 页面替换算法?
    最佳置换算法、先进先出页面置换算法、改进型FIFO算法、最近不常使用算法、最久未使用算法(LRU)、时钟替换算法

  • Linux的slab层,VAM?

  • 什么是伙伴算法?
  • 高端内存的理解?
  • Linux是如何避免内存碎片的

    1. 伙伴算法,用于管理物理内存,避免内存碎片
    2. 高速缓存Slab层用于管理内核分配内存,避免碎片
    Linux IO模型
  • 五种IO模型 参考链接
    阻塞IO,非阻塞IO,IO复用,信号驱动式IO,异步IO
  • 什么是IO多路复用?为什么要使用IO多路复用?
  • select,poll,epoll的区别?
  • epoll两种触发模式?有什么区别?使用哪一种好?
    select/poll/epoll区别
    几种网络服务器模型的介绍与比较
    epoll为什么这么快
    epoll内核源码详解

  • 如何实现线程池?

  • 如何实现内存池?

    死锁
  • 死锁产生的必要条件?

    1. 互斥条件:一个资源每次只能被一个进程使用
    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺
    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
  • 死锁的避免?

    1. 加锁顺序:如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生
    2. 加锁时限:个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间
    3. 死锁检测:当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生
Linux常用的命令与shell编程
  • 与CPU,内存,磁盘相关的命令(top,free, df, fdisk)
  • 网络相关的命令netstat,tcpdump等
  • sed, awk, grep三个超强大的命名,分别用与格式化修改,统计,和正则查找
  • ipcs和ipcrm命令
  • 查找当前目录以及字母下以.c结尾的文件,且文件中包含”hello world”的文件的路径
  • 创建定时任务
  • 定时器的实现?
  • Linux命令 在一个文件中,倒序打印第二行前100个大写字母
    cat filename | head -n 2 | tail -n 1 | grep ‘[[:upper:]]’ -o | tr -d ‘\n’| cut -c 1-100 | rev

计算机网络TCP/IP

TCP/UDP
  • TCP、UDP的首部?
  • TCP、UDP的区别
  • TCP、UDP的应用场景
  • 如何实现可靠的UDP
  • TCP三次握手与四次挥手
  • 详细说明TCP状态迁移过程

  • 2MSL是什么状态?
    主动关闭的Socket端会进入TIME_WAIT状态,并且持续2MSL时间长度,MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒,因而,TIME_WAIT状态一般维持在1-4分钟。

  • TIME_WAIT作用是什么?

    1. 可靠地实现TCP全双工连接的终止
      在进行关闭连接四路握手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允 许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节。因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状态信息进入TIME_WAIT状态。
    2. 防止旧的连接中的重复分节对新连接造成干扰
      TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个 原来的迷途分节就称为lost duplicate。在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身 (incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免这个情 况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时 候,来自连接先前化身的重复分组已经在网络中消逝。
  • TCP重发机制,Nagle算法

  • TCP的拥塞控制使用的算法和具体过程
  • TCP的窗口滑动流量控制
  • TCP客户与服务器模型,用到哪些函数?
  • UDP客户与服务器模型,用到哪些函数?

  • 什么是坚持定时器?

HTTP

http/https 1.0、2.0、1.1、2.0
  • http的主要特点

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

    1. 默认持久连接节省通信量,只要客户端服务端任意一端没有明确提出断开TCP连接,就一直保持连接,可以发送多次HTTP请求
    2. 管线化,客户端可以同时发出多个HTTP请求,而不用一个个等待响应
    3. 断点续传ftghh
  • http2.0的特点

    1. HTTP/2采用二进制格式而非文本格式
    2. HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个HTTP连接就可以实现多个请求响应
    3. 使用报头压缩,HTTP/2降低了开销
    4. HTTP/2让服务器可以将响应主动“推送”到客户端缓存中
  • get/post 区别

    1. get重点在从服务器上获取资源,post重点在向服务器发送数据
    2. get传输数据是通过URL请求,以field(字段)= value的形式,置于URL后,并用”?”连接,多个请求数据间用”&”连接,这个过程用户是可见的。post传输数据通过Http的post机制,将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的。
    3. Get传输的数据量小,因为受URL长度限制,但效率较高。Post可以传输大量数据,所以上传文件时只能用Post方式
    4. get是不安全的,因为URL是可见的,可能会泄露私密信息,如密码等。post较get安全性较高
  • 返回状态码
    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的区别?如何实现加密传输?

    1. https就是在http与传输层之间加上了一个SSL
    2. 对称加密与非对称加密
  • ssl三次握手?
    参考SSL工作流程

  • cookie和session介绍和区别?

  • 浏览器中输入一个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协议。

  • 域名解析过程?

  • ARP的机制,RARP的实现?

  • Ping和TraceRoute实现原理

    1. Ping是通过发送ICMP报文回显请求实现
    2. TraceRoute通过发送UDP报文,设置目的端口为一个不可能的值,将IP首部中的TTL分别设置从1到N,每次逐个增加,如果收到端口不可达,说明到达目的主机,如果是因为TTL跳数超过,路由器会发送主机不可达的ICMP报文。
  • 套接字选项用过哪些?

  • 当我们访问国外网站时很卡是什么原因?游戏加速器的原理?

  • 带宽一定时,多线程下载为什么比单线程要快?

  • 为什么局域网网速那么快?

数据库

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

数据结构与算法

链表
  • 链表和插入和删除,单向和双向链表都要会
  • 链表的问题考虑多个指针和递归
  • 常见的算法题?
    1. 反向打印链表(递归)
    2. 打印倒数第K个节点(前后指针)
    3. 链表是否有环(快慢指针)等等
    4. 链表的翻转
    5. 双向链表的冒泡排序
    6. 跳表的介绍?
栈和队列
  • 队列和栈的区别?
    从实现,应用,自身特点多个方面来阐述,不要只说一个先入先出,先入后出,这个你会别人也会,要展现出你比别人掌握的更深
  • 栈的出栈顺序有多少种?
  • 典型的应用场景?
  • 常见的算法题?
    1. 两个栈模拟队列
    2. 两个队列模拟栈
哈希表
  • 哈希算法有哪些?
  • 解决哈希冲突?
  • STL中hash_map扩容发生什么?
    1. 创建一个新桶,该桶是原来桶两倍大最接近的质数(判断n是不是质数的方法:用n除2到 sqrt(n) 范围内的数)
    2. 将原来桶里的数通过指针的转换,插入到新桶中(注意STL这里做的很精细,没有直接将数据从旧桶遍历拷贝数据插入到新桶,而是通过指针转换)
    3. 通过swap函数将新桶和旧桶交换,销毁新桶。
排序和查找

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

海量数据问题

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

位运算
  1. 判断一个数二进制有多少个1?
布隆过滤器

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

设计模式

  • 单例模式线程安全的写法
  • STL里的迭代器使用了迭代器模式
  • MVC的理解

分布式系统

  • map_reduce原理 参考链接
  • 负载均衡
  • CDN
  • 一致性哈希

系统设计

  • 5
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
高级开发面试题一般包括更加细致和高级的技术问题,涉及到开发者在实际项目中的运用和解决问题的能力。以下是一个可能的回答: 在高级开发面试中,面试官可能会问到关于具体开发框架或者技术栈的问题。例如,他们可能会要求我解释MVC框架以及我在实际项目中的应用经验。我会回答MVC的全称是Model-View-Controller,它是一种软件开发架构模式,将应用程序分为三个主要部分:模型,视图和控制器。模型负责处理数据逻辑,视图负责显示数据和界面,控制器处理用户的请求并负责协调模型和视图之间的交互。对于MVC的应用经验,我可以分享我在过去项目中使用MVC框架的经历,以及如何通过它来组织和管理代码的优势。 除了框架和技术栈问题,高级开发面试还可能涉及到代码质量保证和性能优化方面的问题。例如,面试官可能会问我在实际项目中如何确保代码的质量和可维护性。我会谈到我在代码开发过程中遵循SOLID原则,编写可读性强的代码,并使用单元测试和集成测试来确保代码的正确性和可靠性。另外,我还会提到我在代码评审中的经验,以及如何利用代码静态分析工具和自动化测试工具来帮助检测潜在的问题。 性能优化也是一个重要的话题。如果被问到如何提高应用程序的性能,我会谈到我在过去项目中使用的一些策略,如对数据库进行索引优化,减少网络传输量,优化算法和数据结构等。 总的来说,高级开发面试题旨在测试开发者在实际项目中的运用和解决问题的能力。通过提出关于框架、技术栈、代码质量保证和性能优化等方面的问题,面试官可以更全面地了解开发者的能力和经验,并决定是否适合担任高级开发职位。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值