C++面试知识复习笔记

操作系统
  1. 进程和线程的概念,进程和线程的用处(为什么要用进程和线程),进程和线程的区别,进程和线程的各自同步。

    • 概念:进程是对运行时程序的封装,是系统资源调度和分配的基本单位,实现OS的并发。线程是进程的子任务,是CPU调度和分派的基本单位,是操作系统可识别的最小执行和调度单位,每个线程独占一个虚拟处理器,独自的寄存器组,指令计数器和处理器状态。每个线程完成不同任务,但是共享同一个地址空间(同样的动态内存,映射文件,目标代码),打开的文件队列和内核资源。

    • 区别:

      • 线程属于进程,一个进程可以有多个线程,线程依赖于进程;
      • 进程拥有独立的内存单元,多个线程共享进程的内存、资源。资源:共享代码段(代码+常量),数据段(全局变量+静态变量),扩展段(堆存储),但是每个线程拥有自己的栈段(运行时段,用来存放局部变量和临时变量);
      • 进程是资源分配的最小单位,线程是CPU调度的最小单位;
      • 系统开销,进程创建和撤销的开销主要是分配和回收资源(内存空间、IO设备),进程切换的时候涉及进程CPU环境保存、新北调入的CPU环境的设置。但是线程切换只涉及少量寄存器。
      • 通信:由于同一个进程中多个线程具有相同的地址空间,这样他们之间的通信和同步实现相对较为简单。进程间IPC通信,线程间可以直接读写进程数据段来通信,期间需要进程同步和互斥
      • 进程开销大,但是调试简单,线程开销小,但是调试难;
      • 进程间互不干扰,但是若线程挂掉,会导致整个进程挂掉。
      • 进程适合多核多机,线程适合多核。
    • 进程间通信方式:

      进程间通信方式有:管道、系统IPC(消息队列、信号量、信号、共享内存等),套接字socket. --> Inter-Process Communication

      • 管道:普通管道PIPE,命名管道FIFO

      • 系统IPC

        • 消息队列,由标记符(即队列ID)来标记,详细队列面向记录,并且消息具有特定格式和优先级,消息队列独立于发送和接收进程,消息队列可以实现消息的随机查询。
        • 信号量semaphore,是一个计数器,用来控制多个进程对共享资源的访问,用于实现进程间的互斥和同步。信号量是基于操作系统的PV操作,P(通过)V(释放)
        • 信号signal,信号用于通知接收进程某个事件已经发生。
        • 共享内存(shared memory),多个进程访问同一块内存空间,不同进程可以及时看到对方的进程中对共享内存数据的更新,需要同步操作(互斥锁、信号量)
        • 特点:共享内存是最快的IPC;因为多个进程同时操作,因此需要同步;信号量+共享内存;
      • 套接字socket,用于各主机间进程的通信。

    • 线程间通信方式

      • 临界区,多线程串行化访问公共资源
      • 互斥量 synchronized/lock
      • 信号量semphare, 为控制具有有限数量的用户资源而设计的,允许同一时刻多个线程访问同一个资源,但是会限制同一时刻访问此资源的最大线程数量。
      • 事件(信号),wait/notify,通过通知操作的方式来保持多线程同步,并且也可以实现多线程的优先级。
  2. Linux虚拟地址空间

    目的:为了防止不同进程在同一时刻在物理内存中运行而对物理内存产生争夺和践踏。

    虚拟地址空间和虚拟内存

    虚拟内存技术:由于CPU(地址总线32位)寻址空间是4GB,因此每个进程在运行的过程中可以独占4GB的内存,每个进程只能将自己需要的虚拟内存空间在运行时动态的从外存加载到物理内存上。若物理内存上没找到程序对应的数据,会发生缺页异常(中断),然后通过存储器映射(虚拟内存与磁盘文件之间的映射)从磁盘上加载数据。

    请求分页系统、请求分段系统、请求段页式系统都是针对虚拟内存,通过请求实现内存和外存的信息置换。

    虚拟内存的好处:

    • 扩大地址空间。准确的说,32位的CPU地址空间是固定,4GB。这里扩大的是虚拟内存,将讲部分外存映射到虚拟内存上。
    • 内存保护,每个进程运行在各自的虚拟内存地址空间,互不干扰。
    • 公平分配内存,每个进程拥有相同大小的虚存空间。
    • 进程通信时可以通过共享虚存实现。
    • 不同进程可以在物理内存中共享相同的代码,只需通过虚存将地址都映射到这个物理内存地址上即可。
    • 虚拟内存适合在多道程序设计系统中使用。
    • 在虚拟地址空间分配连续地址,但是在物理内存中可以不连续,因此可以利用碎片。

    虚拟内存的坏处:

    • 虚存管理需要建立很多数据结构,这些结构会占用额外内存
    • 虚拟地址到物理地址的转换,增加指令的执行时间
    • 页的换入换出需要磁盘IO----------->这点加上第二点其实是在讨论发生缺页中断时,需要将未命中的页从磁盘调人内存的过程。
  3. 操作系统中程序在内存中的结构

    一个程序本质上是由bss,data,text段三个组成,一个可执行程序在存储(没调入到内存时)时,分为代码段、数据区和未初始化数据区三个部分。

    • bss段(未初始化数据区):用来存放程序中未初始化的全局变量和静态变量的一块内存区域。bss段属于静态分配,程序结束后静态变量资源由系统自动释放。
    • 数据段:存放程序中已经初始化的全局变量的一块内存区域,数据段也属于静态内存分配。
    • 代码段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且属于只读区域,当然在代码段中也可能包含一些只读的常数变量。
    • text段和data段在编译时分配空间,bss不占可执行文件大小,由链接器获取内存
    • bss段(未进行初始化的数据)的内容并不存放在磁盘的程序文件中,由内核在程序开始运行前将他们设置为0。需要存放在程序文件中的只有正文段和初始化数据段。
    • data段(已经初始化的数据)则为数据分配空间,数据保存在目标文件中。

    可执行文件在运行时会多出两个区域:栈区和堆区;

    • 栈:由编译器自动释放,存放函数的参数和局部变量。
    • 堆:用于动态分配内存。堆是从低地址向高地址位增长,采用链式存储结构。
  4. 操作系统的缺页中断

    malloc()和mmap()等内存分配函数,在分配时只是建立了进程虚拟地址空间,但是没有分配虚拟内存对应的物理内存,当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常。

    缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的内存页不在内存中,会产生一次缺页中断。操作系统通过外存地址在外存地址在外存中找到所缺的页,将其调入内存。

    缺页本身是一种中断,与一般中断一样,需要四个步骤:

    1. 保护CPU现场
    2. 分析中断原因
    3. 转入缺页中断处理程序进行处理
    4. 恢复CPU现场,继续执行

    缺页中断是由于所访问的页面不在内存中,由硬件所产生的特殊中断。

2020年3月2日23:10:08

  1. fork和vfork的区别

    fork: 调用fork创建的新进程与父进程一模一样,然后使用exec载入二进制映像,替换当前进程的映像。

    早期Unix系统,在创建进程时,内核会将所有的数据复制一份,复制进程的页表项,然后把父进程的地址空间的内容逐页复制到子进程的地址空间中。现在Unix/Linux系统采用写时复制的方法。

    vfork:

    • 问题:fork后立即执行exec造成地址空间浪费。
    • vfork会挂起父进程直至子进程终止或者运行了一个新的可执行文件的映像。通过这个方式,vfork避免地址空间的按页复制,父进程和子进程共享相同的地址空间和页表项。vfork只完成一件事:复制内部的内核数据结构,这里子进程不能修改地址空间中的任何内存。

    写时复制:

    • 目的:减少fork对父进程空间整体复制带来的开销。

    • 原理:若多个进程要读取资源时,每个进程保存一个指向这个资源的指针,当进程需要修改那份资源时,会复制那份资源,然后将复制的那份提给进程。复制的过程是透明的。

    • 好处:尽量推迟代价高昂的操作,直到必要时刻才会去执行。

    • 实现:在虚拟内存下,COW是以页为基础的,在内核里,内核页相关的数据结构被标记为只读和写时复制。当进程尝试修改页时,会产生缺页中断。内核处理这个缺页中断的方式是对该页进行一次透明复制,然后清除页面的COW属性,表示它不再被共享。

    • 现代计算机系统结构中在内存管理单元(MMU)提供了硬件级别的写时复制支持。

    • 调用fork时,大量fork之后会跟着执行exec,若复制整个父进程的内容到子进程的地址空间将完全是浪费时间,并且子进程会立刻执行新的二进制可执行文件映像,先前地址空间会被交换出去。而COW恰好对此场景做了优化。

    • fork和vfork的区别:

      • fork拷贝父进程的数据段+代码段,vfork:共享数据段;
      • fork父子进程的执行次序不确定,vfork保证子进程先运行(在调用exec或exit之前与父进程数据是共享的,在调用exec或exit之后父进程才能继续调度运行);可能会有deadlock;
  2. 修改文件最大句柄数

    linux默认最大文件句柄数1024。修改最大句柄数有两个方法:

    1. limit -n <文件数>只对当前进程有效
    2. 对所有进程有效,修改Linux系统参数;
    vim /etc/security/limits.conf
    soft nofile 65536
    hard nofile 65536
    
  3. 并发(concurrency)和并行(parallelism)

    并发:宏观上两个程序同时运行,微观上两个程序的指令交织运行。并发不能提高计算机的性能,只提高了效率。

    并行:严格物理上的同时执行,并行提高了计算机的效率和性能。

  4. 查看并修改MySQL端口号

    show global variables like 'port'查看端口,Mysql:3306, sqlserver:1433, oracle:1521, db2: 5000

    修改端口,编辑/etc/my.cnf

  5. 操作系统的页表寻址

    页式内存管理,内存被分成固定长度的一个个页片。操作系统为每个进程维护了一个从虚拟地址到物理地址的映射关系的数据结构,叫做页表。页表的内容是该进程的虚拟地址到物理地址的一个映射。页表中每一项纪录这个页的基地址,通过页表,由逻辑地址的高位部分先找到逻辑地址对应的页基地址,页基地址偏移一定长度就能得到最后的物理地址,偏移长度由逻辑地址的低位部分决定,(其实这里有点像FTL中的块映射。)

    Linux最初的两级页表机制:两级分页机制将32位的虚拟空间分为三段,低12段表示页内偏移,高20段分别表示两级页表的偏移:

    从CR3寄存器中获取PGD物理地址(这个应该是基地址),获取虚拟地址的高10位作为偏移,即可定位到这个地址描述的pgd,即页表的物理地址。然后从虚拟地址中抽取中间10位作为偏移,即定位到pte,即可获取页表的入口索引。然后在虚拟地址中得到页内偏移,这样即可完成虚拟地址到物理地址的转换。对虚拟地址的分级解析过程实际上是不断深入页表层次的过程,逐渐定位到最终地址,page table work

    Linux三级页表机制:CR3,PGD,PMD,PTE,page offset
    Linux四级页表机制:PML4,PGD,PMD,PTE,page offset

  6. 有了进程为什么要有线程

    线程产生的原因:进程可以使多个程序并发执行,以提高资源的利用率和吞吐量,但是有以下缺点:

    1. 进程在同一时间只能干一种事;
    2. 进程在执行过程中如果被阻塞,整个进程就会被阻塞,即使进程中有些工作不依赖于等待资源,但是仍然不能执行;

    操作系统引入比进程更小粒度的线程,作为并发执行的基本单位,线程的优势:

    1. 资源节俭。启动新的进程,操作系统必须给他分配独立的地址空间,建立众多的数据表维护它的代码段、堆栈段、数据段;
    2. 切换效率高。一个进程运行多个线程,多个线程使用相同的地址空间,线程间切换的时间远小于进程间切换的时间。
    3. 通信机制上讲,线程间更方便通信。不同进程拥有各自独立的地址空间,要进行数据传递,只能通过进程间通信的方式进行。而同一进程的不同线程共享地址空间,一个线程的数据可以直接被其他线程使用。

    多线程程序作为一种多任务、并发的工作方式,有如下优点:

    1. 多CPU更加高效,当线程数少于CPU数量时,操作系统会保证不同线程运行在不同CPU上。
    2. 改善程序结构。一个长的进程可以拆分成读个线程,各自形成独立或者半独立的运行部分。
  7. 单核机器上写多线程程序是否需要考虑加锁

    单核机器上写多线程程序仍然需要线程锁。因为线程锁通常用来实现线程的同步和通信。在单核机器上运行多线程程序时,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,在不使用线程锁的情况下,可能会导致共享数据修改引起的冲突。

  8. 线程需要哪些上下文,SP,PC,EAX寄存器的作用

    线程在切换过程中需要保存当前线程的ID,线程状态、堆栈、寄存器(SP,PC,EAX)状态等信息.

    • SP: 堆栈指针,指向当前栈的栈顶地址
    • PC: 程序计数器,存储下一条将要执行的指令。
    • EAX: 累加寄存器,用于加法乘法的缺省寄存器。
  9. 线程间同步的方式,具体的系统调用

    1. 信号量:一种特殊的变量,用于线程同步,只能取自然数,只支持两种操作:

      • P(SV):如果信号量SV大于0,则减1,如果SV等于0,则挂起该线程;

      • V(SV):如果有其他进程因为等待SV而挂起,则唤醒,然后SV+1,否则直接SV+1;

      • 系统调用:

        • sem_wait(sem_t *sem):以原子操作的方式将信号量减1,如果信号量为0,则sem_wait将被阻塞,直到这个信号量具有非0值
        • sem_post(sem_t *sem):以原子操作将信号量减1,当信号量大于0时,其他正在调用sem_wait等待信号量的线程将被唤醒。
    2. 互斥量:又称互斥锁,主要用于线程互斥,不能保证按序访问,可以和条件锁一起实现同步,当进入临界区时,需要获取互斥锁并且加锁,当离开临界区时,需要对互斥锁解锁,以唤醒其他等待该互斥锁的线程。系统调用如下:

      • pthread_mutex_init: 初始化互斥锁
      • pthread_mutex_destroy: 销毁互斥锁
      • pthread_mutex_lock: 以原子的方式给互斥锁加锁,如果互斥锁已经被上锁,pthread_mutex_lock调用将被阻塞,直到该互斥锁的占有者将其解锁;
      • pthread_mutex_unlock: 以原子操作的方式给互斥锁解锁。
    3. 条件变量:又称条件锁,机制:当共享数据达到某个值时,唤醒等待这个共享数据的一个或者多个线程,即当某个共享变量等于某个值时,调用singnal/broadcast。系统调用如下:

      • pthread_cond_init:初始化条件变量
      • pthread_cond_destroy:销毁条件变量
      • pthread_cond_signal:唤醒一个等待目标条件变量的线程。哪个线程被唤醒取决于调度策略和优先级。
      • pthread_cond_wait: 等待目标条件变量。该函数在进入wait状态前首先解锁,接收到信号后再加锁。
  10. 多线程和多进程的不同

    1. 进程是资源分配的最小单位,线程是CPU调度的最小单位
    2. 多线程之间共享同一个进程的地址空间,线程间通信简单
    3. 线程的创建、销毁和切换简单,速度快、占用的内存少,适合于多核分布式系统,但是线程间会互相影响,一个线程意外终止会导致同一个进程的其他线程也终止,程序可靠性差;而多进程拥有各自独立的运行地址空间,进程间不会相互影响,但是进程创建、销毁和切换复杂,适合多核多机分布。
  11. 游戏服务器应该为每个用户开辟一个线程还是一个进程?

    游戏服务器应该为每个用户开辟一个进程。因为同一个进程的的线程会互相干扰,一个线程死掉会影响其他线程,从而导致进程崩溃。

  12. OS缺页置换算法

    缺页置换:当访问一个内存中不存在的页,并且内存已经满,则需要从内存中调出一个页,将数据送至磁盘对换区,替换一个页。常用的缺页置换算法:

    • 先进先出(FIFO):置换最先调入内存的页面。
    • 最近最少使用(LRU): 置换最近一段时间来最长时间未访问的页面。
  13. 多线程和多进程的使用场景

    多进程模型的优势是CPU,适用于CPU密集型,多机分布式场景,多机扩展。多线程间切换的代价小,适合IO密集型的工作场景,IO密集型的场景中经常会由于IO阻塞导致频繁的切换线程。同时,多线程模型也适用于单机多核分布式场景。

  14. 死锁发生的条件和解决死锁的方法

    死锁:两个或者两个以上进程在执行过程中,因争夺资源而造成相互等待的现象。死锁发生的4个必要条件:

    • 互斥条件:进程对分配到的资源不允许其他进程访问,若其他进程访问,只能等待直到占用该资源的进程使用完释放该资源;
    • 请求和保持条件:进程获得资源后,又对其他资源发出请求,但该资源可能被其他进程占有,此时请求阻塞,但进程不会释放自己已经占有的资源;
    • 不可剥夺条件:进程已获得的资源,在未完成使用前,不可剥夺,只能在使用后自己释放;
    • 环路等待条件:进程发生死锁后,一定存在一个进程-资源之间的环形链。

    解决死锁的方法及破坏上述四个条件之一:

    • 资源一次性分配,从而剥夺请求和保持的条件;
    • 可剥夺资源,当进程新的资源未得到满足时,释放已经占有的资源,从而破坏不可剥夺的条件;
    • 资源有序分配法,系统给每类资源赋予一个序号,每个进程按编号递增的请求资源,释放则想发,从而破坏环路等待的条件;
  15. 操作系统的结构体对齐、字节对齐

    • 对齐原因:

      • 平台原因(移植原因):不是所有硬件平台都能访问任意地址上的任意数据,某次硬件平台智能在某些地址处取,某些特定类型的数据,否则抛出硬件异常;
      • 性能原因:数据结构(尤其是栈)应该尽可能在自然边界上对齐。因为为了访问未对齐的内存,处理器需要做两次访问内存操作,而对齐的内存访问只需要一次。
    • 规则:

      • 数据成员对齐规则:结构(struct)或者联合(union)的数据成员,第一个数据成员放在offset为0处,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中较小的那个进行;
      • 结构(联合)整体的对齐规则:在数据成员完成各自对齐之后,结构(联合)本身也要进行对齐,按照#pragma pack和最大数据成员长度中较小的对齐;
      • 结构体作为成员,结构体成员要从其内部最大元素的大小的整数倍地址开始存储。
      • 定义结构体对齐;
  16. 虚拟内存置换的方式(补充#17)

    常见的内存替换算法:FIFO,LRU,LFU,LRU-K,2Q

    1. FIFO,无法体现页面冷热信息

    2. LFU(最不经常访问淘汰算法)

      • 思想:如果数据过去被访问过多次,那么将来被访问的频率也更高。
      • 实现:每个数据块一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序,每次淘汰队尾数据块。
      • 缺点:排序开销大
    3. LRU(最近最少使用替换算法)

      • 思想:如果数据最近被访问过,那么将来被访问的几率也更高。
      • 实现:使用一个栈,新页面或者命中的页面移动到栈底,每次替换栈顶的缓存页面;
      • 优点:LRU算法对热点数据命中率很高;
      • 缺点:缓存颠簸、缓存污染(突然大量偶发性数据访问时,内存中存放大量冷数据);
    4. LRU-K(LRU-2,LRU-3)

      思想:最久未使用K次淘汰算法。

      LRU-K的K表示最近使用的次数,LRU-K的主要目的是为了解决LRU算法"缓存污染"的问题。

      相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有将数据访问次数达到K次时,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距离当前时间最大的数据。

      实现:

      1. 数据第一次被访问,加入到访问历史列表;
      2. 如果数据在访问历史列表中没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;
      3. 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列中删除,将数据移动到缓存队列中,并缓存此数据,缓存队列重新按照时间排序。
      4. 缓存数据队列中被再次访问后,重新排序;
      5. 需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即淘汰"倒数第K次访问离现在最久的"数据。
    5. 2Q

      类似LRU-2,使用FIFO队列和LRU队列

      实现:

      1. 新访问的数据插入到FIFO队列;
      2. 如果数据在FIFO队列中一直没有被再次访问,则最终按照FIFO规则淘汰;
      3. 如果数据在FIFO队列中被再次访问,则将数据移动到LRU队列头部;
      4. 如果数据在LRU队列再次被访问,则将数据移动到LRU队列头部;
      5. LRU队列淘汰末尾的数据

      针对的问题:LRU的缓存污染

  17. 互斥锁(mutex)机制,互斥锁,读写锁的区别

    • 互斥锁与读写锁的区别

      • 互斥锁:mutex,用于保证在任意时刻只能有一个线程访问对象,当获取锁失败时,线程会进入睡眠, 等待锁释放时被唤醒;

      • 读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获取读操作。但是同一个时刻只能有一个线程可以获得写锁。其他获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其他线程获取,写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。读写锁适合于读取数据的频率远远大于写数据的频率的场合.

      • 区别:

        1. 读写锁区分读和写,互斥锁不区分
        2. 互斥锁同一个时间内只能允许一个线程访问该对象,无论读写;读写锁同一时刻只允许一个写者,但是允许多个读者同时读对象;
      • Linux的4中锁机制:

        1. 互斥锁:mutex
        2. 读写锁
        3. 自旋锁:spinklock在任何时刻同样只能有一个线程访问对象,但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下回极大提高效率。但是如果加锁时间过程,则会浪费CPU
        4. RCU: read-copy-update,在修改写数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后再将老数据update成新的数据。在使用RCU时,读者几乎不需要同步开销,因为不需要获取锁,也不使用原子指令,因此不会导致锁竞争,并且也不会有死锁的问题,但是写者的同步开销非常大,因为需要复制被修改的数据,还必须使用锁机制同步其他写者的修改操作,RCU适合少量写大量读的场景下.
  18. 进程状态转换图,动态就绪,静态就绪,动态阻塞,静态阻塞

    进程的5种状态:

    • 创建状态:进程正在被创建;
    • 就绪状态:进程被加入到就绪队列中等待CPU调度运行;
    • 执行状态:进程正在被运行
    • 等待阻塞状态:进程因为某种原因,i.e.,等待IO,等待设备,暂时不能运行
    • 终止状态:进程运行完毕

    交换技术(进程的挂起状态):

    • 现象:当多个进程竞争内存资源时,会造成内存资源紧张,如果此时没有就绪进程,处理机会空闲,IO速度比处理机速度慢得多,可能出现全部进程阻塞等待IO
    • 解决方法:交换技术(换出一部分进程到外存,腾出内存空间),虚拟存储技术(每个进程只能装入一部分程序和数据)
    • 在交换技术中,将内存暂时不能运行的进程或者暂时不用的数据和程序换出到外存,来腾出足够的内存空间,把已经具备运行条件的进程,或者进程所需的数据和程序换入内存,这里会出现进程的挂起状态.

    活动阻塞,静止阻塞,活动就绪,静止就绪

    • 活动阻塞:进程在内存由于某种原因被阻塞了
    • 静止阻塞:进程在外存,同时由于某个原因被阻塞了
    • 活动就绪:进程在内存,处于就绪状态,只要给CPU和调度就能直接运行;
    • 静止就绪:进程在外存,处于就绪状态,只要调到内存,给CPU和调度就能直接运行;

    状态:

    • 活动就绪—>静止就绪(内存不够,调到外存);
    • 活动阻塞—>静止阻塞(内存不够,调到外存);
    • 执行—>静止就绪(时间片用完)
  19. A* a = new A; a->i=10内存分配

  20. 给一个类,里面有static,virtual,说说这个类的内存分布

    • static修饰符:

      • static修饰成员变量,对于非静态数据成员,每个类对象都有自己的拷贝,静态数据成员被当做类的成员,无论类被定义了多少个,静态数据成员都只有一份拷贝,为该类型的所有对象所享用(包括派生类),所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。静态数据成员在全局数据区分配内存,属于本类所有对象共享,所以它不属于特定的类对象,在没有产生类对象前就可以使用
      • static修饰成员函数,与普通的成员函数相比,静态成员函数由于不是与任何对象相联系,因此它不具备this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,只能调用其他的静态成员函数。static修饰的成员函数在代码区分配内存
    • c++继承和虚函数

      c++多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现的,在编译时候确定,动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行时确定;

      动态多态实现的条件:

      • 虚函数;
      • 一个基类指针或者引用指向派生类的对象;
    • virtual修饰符

      如果一个类是局部变量,则该类数据存储在栈区,如果一个类是通过new/malloc动态申请的,则该类数据存储在堆区。

      如果这个类是virtual继承而来的子类,则该类的虚函数表指针和该类其他成员一起存储。虚函数表指针指向只读数据段中的类虚函数表,虚函数表中存放着一个个函数指针,函数指针指向代码段中的具体函数。

  21. 软链接和硬链接的区别

    为了解决文件共享的问题,linux引入软链接和硬链接、软/硬链接还可以隐藏文件路径、增加权限安全及节省存储等好处。若一个inode号对应多个文件名,则为硬链接,即硬链接就是同一个文件使用了不同的别名,使用ln创建。若文件用户数据块中存放的内容是另一个文件的路径名指向,则文件是软链接。软链接是一个普通文件,有自己独立的inode,数据块内容比较特殊。

  22. 大端和小端

    大端是指低字节存储在高地址,小端是低字节存储在低地址。可以用联合体来判断系统是大端还是小端,因为联合体变量是从低地址存储。

  23. 静态变量初始化

    静态变量存储在虚拟地址空间的数据段和bss段,c语言在代码执行前初始化,属于编译期初始化。而c++由于引入对象,对象生成必须调用构造函数,因此c++规定全局或者局部静态对象当且仅当对象首次用到时进行构造。

  24. 内核态和用户态

    用户态和内核态是操作系统的两种运行级别。两者最大的区别是特权级别不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常,中断

  25. 设计server,使得能接收多个客户端的请求

    多线程,线程池,IO复用

    线程池#last

  26. 死循环+来连接时新建线程的方法效率有点低,如何改进

    提前创建线程池,用生产者消费者模型,创建任务队列,队列作为临界资源,有新的连接,就挂到任务链表上,队列为空所有线程睡眠。改进死循环的方法:使用select epoll技术

  27. 如何唤醒被阻塞的socket线程

    给阻塞时缺少的资源。

  28. 确定当前线程是繁忙还是阻塞

    使用ps命令查看

  29. 空闲进程和阻塞进程状态会不会在唤醒时候误判?

    暂时没有标准答案

  30. 就绪状态的进程在等待什么

    被调度使用的CPU运行权

  31. 多线程同步,锁机制

    同步的时候用一个互斥量,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。在互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待他重新变为可用。在这种方式下,每次只有一个线程可以向前执行.

  32. 两个进程访问临界区资源,

    单核cpu,并且开了抢占可以造成这种情况。

  33. 假设临界区资源释放,如何保证只让一个线程获取临界区资源而不是都获取.

    暂时没有标准答案

  34. Windows消息机制

    当用户有操作(鼠标,键盘等)时,系统会将这些事件转化为消息,每个打开的进程系统都会为其维护一个消息队列,系统会将这些消息放到进程的消息队列中,而应用程序会循环从消息队列中取出消息,完成对应的操作.

  35. c++锁

    c++锁包括互斥锁,条件变量,自旋锁和读写锁

  36. 内存溢出和泄漏

    1. 内存溢出

      • 概念:程序申请内存时,没有足够的内存供申请者使用,内存溢出:申请的内存空间超过了系统实际分配的空间,系统无法满足,会报内存溢出错误.
      • 原因:内存中加载的数据量过于庞大,e.g.,一次从数据库中取出过多的数据,集合类中有对对象的引用,使用完后未清空,使得不能收回,代码死循环产生过多重复的对象实体;第三方软件中的BUG
    2. 内存泄漏

      • 概念:程序未能释放掉不再使用的内存

      • 分类

        • 堆内存泄漏(heap leak):malloc/realloc/new从堆中分配的内存,没有用free/delete删掉.
        • 系统资源泄漏(resource leak):程序使用系统分配的资源,比如bitmap,handle,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费。
  37. 【补充】 常用的线程模型

    1. future模型

      该模型需要结合callable接口配合使用,future把结果放在将来获取,当前主线程并不急于获取处理结果。允许子线程先进行处理一段时间,处理结束后把结果保存下来,当主线程需要使用的时候再向子线程索取。

      callable类似runnable接口,call方法类似run方法,不同的是run方法不能抛出受检异常和没有返回值,而call方法可以抛出受检异常并可设置返回值,两者的方法体都是线程执行体.

    2. fork&join模型

      该模型包含递归思想和回溯思想,递归用来拆分任务,回溯用来合并结果。可以用来处理一些可以进行拆分的大任务。主要是把一个大任务逐级拆分为多个子任务,然后分别在子线程中执行,当每个子线程执行结束之后逐级回溯,返回结果进行汇总合并,最终得到想要的结果。

    3. actor模型

      actor模型属于一种基于消息传递机制并行任务处理思想,它以消息的形式来进行线程间数据传输,避免全局变量的使用(免掉数据同步的隐患).actor在接收到消息后自己进行处理,也可以继续传递(分发)给其他actor进行处理

    4. 生产者消费者模型

      使用一个缓存保存任务,开启一个/多个线程来生产任务,然后再开启一个/多个来从缓存中取出任务进行处理。好处:任务的生成和处理分隔开,生产者不需要处理任务,只负责生产任务并保存到缓存,消费者只需要从缓存中取出任务进行处理。使用的时候可以根据任务的生成情况和处理情况开启不同的线程来处理.

    5. master-worker模型

      任务分发策略,开启一个master线程接收任务,然后在master中根据任务的具体情况分发给其他worker子线程,然后由子线程处理任务.

  38. 协程

    • 概念:微线程,协程看上去也是子程序,但在执行过程中,子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行.
    • 协程和线程的区别:协程最大的优势是协程执行效率极高(子程序切换不是线程切换,而是由程序自身控制,因此没有线程切换的开销,和多线程相比,线程数据越多,协程的性能优势越明显)。协程不需要锁机制,因为协程只有一个线程.
    • 多进程+协程可以充分利用多核,又能发挥协程的高效率,可获得极高的性能.
  39. 系统调用

    概念:运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供了用户程序与操作系统之间的接口

    操作系统的状态分为管态(核心态)和目态(用户态)。大多数系统交互式操作需要在内核态执行,e.g.,设备IO,进程间通信。特权指令:一类只能在核心态下运行而不能再用户态下运行的特殊指令。

  40. fork调用实例

    暂时略

  41. 【补充】用户态到内核态的转化原理
    种类:

    1. 系统调用:系统调用机制的核心依然是使用了操作系统为用户特别开放的中断来实现的,Linux的ine 80h中断
    2. 异常:当CPU在执行运行在用户态的程序时,发现某些事件不可知的异常,会触发当前运行进程切换到此处理,异常会转换到内核相关程序中处理,也即内核态,比如缺页中断.
    3. 外围设备的中断:外围设备完成用户请求的操作之后,会向CPU发出相应的中断信号,CPU会暂停执行下一条将要执行的指令,转而执行中断信号的处理程序.

    方式:三种涉及的关键步骤完全一样.

  42. 源码到可执行文件的过程

    1. 预编译:主要处理源代码文件中的以#开头的预编译指令,处理规则如下:

      • 删除所有的#define,展开所有宏定义
      • 处理所有条件预编译指令,#if,#endif,#ifdef,#elif,#else
      • 处理#include预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件
      • 删除所有的注释,// /**/
      • 保留#pragma编译器指令,编译器需要用到他们,如:#pragma once是为了防止有文件被重复引用
      • 添加行号和文件标识,便于编译器在编译时产生调试用的行号信息
    2. 编译

      把预编译之后生成的xxx.i或者xxx.ii文件进行词法分析、语法分析、语义分析、优化生成相应的汇编代码文件;

      • 词法分析:利用类似于"有限状态机"的算法,将源代码程序输入到扫描机中,将字符序列分割成一系列记号;
      • 语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树,由语法分析器输出的语法树是一种以表达式为节点的树;
      • 语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义(编译期间确定的语义),相对应的动态语义是运行期才能确定的语义.
      • 优化:源代码级别的优化过程
      • 目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列–>汇编语言表示
      • 目标代码优化:目标代码优化器对上述机器指令代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余指令.
    3. 汇编

      将汇编代码转变成机器可执行的指令(机器码文件),汇编器汇编过程:根据汇编指令和机器指令的对照表一一翻译,由汇编器as完成。经过汇编之后产生的目标文件(与可执行文件格式基本一样)

    4. 链接:将不同的源文件产生的目标文件进行链接,从而形成可执行文件,链接分为动态链接和静态链接:

      • 静态链接

        • 概念:函数和数据被编译进一个二进制文件。在使用静态库的情况下,编译链接可执行文件时,链接器从库中复制这些函数和数据,并把他们和应用程序的其他模块组合起来创建最终的可执行文件
        • 空间浪费:因为每个可执行程序对所有需要的目标文件都要有一个副本,如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本
        • 更新困难:每当库函数代码修改,这个时候就需要重新进行编译链接形成可执行文件
        • 运行速度快:静态链接时,可执行程序中已经具备了所有执行程序所需要的任何东西,执行的时候速度快
      • 动态链接

        • 概念:把程序按照模块拆分成各个相对独立的部分,在程序运行时才将他们链接在一起形成完整的程序
        • 共享库:即使每个库都依赖同一个库,这些程序在执行时也是共享同一份副本的
        • 更新方便:更新时只需要更换原来的目标文件,程序运行时,新版本的目标文件自动加载到内存并链接起来
        • 性能损耗:链接推迟到运行时
  43. 微内核、宏内核

    • 宏内核:除最基本的进程、线程管理、内存管理外,将文件系统,驱动,网络协议也都集成到了内核里面

      • 优点:效率高
      • 缺点:稳定性差,开发过程中出现bug经常会导致系统挂掉
    • 微内核: 内核只有最基本的调度、内存管理。驱动、文件系统都是用户态的守护进程实现

  44. 僵尸进程

    1. 正常进程:正常情况下,子进程是通过父进程创建的,子进程再创建新的进程,子进程的结束和父进程的运行是一个异步的过程,父进程永远无法预测子进程何时结束。当一个进程完成它的工作终止后,它的父进程需要调用wait或者waitpid系统调用取得子进程的终止状态(进程ID,退出状态,运行时间)

    2. 孤儿进程:一个父进程退出,它的一个或者多个子进程还在运行,那么这些子进程将成为孤儿进程,孤儿进程被init进程(进程id为1)所收养,并由Init进程完成状态收集

    3. 僵尸进程:一个进程使用fork创建的子进程,如果子进程退出,而父进程并没有调用wait或者waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保持在系统中。

      • 僵尸进程是每个进程必然经过的过程(发生在进程结束时)
      • 子进程在exit之后,父进程没来及处理,ps命令看到的子进程状态是Z
      • 危害:如果进程不调用wait或者waitpid,那么保留的那段信息不会释放,其进程号就会一直被占用着,系统所能使用的进程号是有限的
    4. 外部消灭:通过kill发送SIGTERM或者SIGKILL信号消灭僵尸进程的进程,他产生的僵尸进程就变成孤儿进程,孤儿进程由init接管

    5. 内部解决:

      • 子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号,信号处理函数中调用wait处理僵尸进程
      • fork两次,将子进程变成孤儿进程,其父进程转为init进程
  45. gdb调试,条件断点

  46. 5种IO模型

    1. 阻塞IO:调用者调用某个函数,等待这个函数返回,期间什么都不做,不停检测函数是否返回,必须等待函数返回才能进行下一步
    2. 非阻塞IO:非阻塞等待,每隔一段时间检测IO事件是否就绪,没就绪就可以做其他事
    3. 信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号,然后处理IO事件
    4. IO复用/多路转接IO:linux用select/poll函数实现IO复用,这两个函数会使进程阻塞,可以阻塞多个IO操作,对多个读操作和写操作的IO函数进行检测,知道有数据可读或者可写时才真正调用IO操作函数
    5. 异步IO:linux中,调用aio_read函数告诉内核描述字缓存…
  47. 异步编程的事件循环
    事件循环:不停等待事件发生,然后将这个事件的所有处理器,以及他们订阅的这个事件的时间顺依次执行完毕之后,事件循环就会开始继续等待下一个事件的触发,不断往复。

  48. 操作系统为什么分内核态和用户态

    安全性

  49. 为什么要page cache,以及操作系统如何设计page cache

    加快从磁盘读取文件的速率。page cache中有一部分磁盘文件的缓存,因为从磁盘中读取文件比较慢,所以读取文件先去page cache中查找,如果命令,则不需要去磁盘上读取,大大加快读取速度,在linux内核中,文件的每个数据块最多只能对应一个page cache项,通过两个数据结构来管理这些cache项,一个是radix tree,另一个是双向链表,radix tree是一种搜索树,linux内核利用这个数据结构来通过文件内偏移快速定位cache项.

  50. server端监听端口,但还没客户端连接进来,此时进程处于什么状态

    普通模型,处于阻塞状态,使用epoll,select等IO复用的模型,处于运行状态

  51. 实现线程池

    1. 设置一个生产者消费者队列,作为临界资源
    2. 初始化n个线程,并让其运行起来,加锁去队列取任务运行
    3. 当任务队列为空时,所有线程阻塞
    4. 当生产者队列来了一个任务后,先对队列加锁,把任务挂在队列上,然后使用条件变量去通知阻塞中的一个线程
  52. linux下得到一个文件的100行到200行

    sed -n '100,200p' <inputfile>
    awk 'NR>=100&&NR<=200{print}' <inputfile>
    head -200 <inputfile>|tail -100
    
  53. awk用法

    1. 作用:样式扫描和处理语言,它允许创建简短的程序,读取输入文件,为数据排序、处理数据、对输入执行计算以及生成报表
    2. awk [-F field-sep] 'command' <inputfile...>
    3. 内置变量…
  54. linux内核Timer定时器

    • 低精度时钟

      linux2.6.16之前只支持低精度时钟,内核定时器工作方式:

      1. 系统启动后,会读取时钟源设备(rtc,hpet,pit…)初始化当前系统的时间
      2. 内核会根据HZ(系统定时器频率,节拍率)参数值,设置时钟事件设备,启动tick(节拍)中断,HZ表示1秒钟产生多少个时钟硬件中断,tick表示连续两个中断的间隔时间
      3. 设置时钟事件设备后,时钟事件设备会定时产生一个tick中断,触发时钟中断处理函数,更新系统时钟,并检查timer wheel,进行超时时间处理

      在linux2.6.16之前,内核软件定时器采用timer wheel多级时间轮的实现机制,维护操作系统的所有定时事件,timer wheel触发是基于系统tick周期性中断

      旧内核机制:依赖系统定时器硬件定期的tick,基于tick,内核会扫描timer wheel处理超时事件,更新jiffies,wall time(墙上时间,现实时间),process的使用时间

    • 高精度时钟

      linux2.6.16内核支持高精度时钟,内核采取新的定时器hrtimer,其实现逻辑与之前的定时器逻辑区别是:

      • hrtimer采用红黑树进行高精度定时器的管理,而不是时间轮,高精度时钟定时器不再依赖系统tick中断,而是基于事件触发;
      • 新内核不再支持周期性tick,新内核定时器框架采用基于事件触发(而不是周期性触发)

      hrtime工作原理:通过将高精度时钟硬件触发时间设置为红黑树中最早到期的Timer时间,时钟到期后从红黑树中得到下一个Timer的到期时间,并设置硬件,如此往复;

      其他:在高精度时钟模式下,操作系统内核仍然需要周期性tick中断,以便刷新内核的一些任务。hrtimer是基于事件的,不会周期性触发tick中断,为了实现周期性tick中断(dynamic tick):系统创建一个模拟tick时钟的特殊hrtimer,将其超时时间设置为一个tick时长,在超时到达后,完成对应的工作,然后再次设置下一个tick的超时时间,以此达到周期性tick中断的需求.

      新内核对相关时间硬件设备进行了统一的封装,主要有两个结构:

      • 时钟源设备(clock source device):抽象那些能够提供计时功能的系统硬件,e.g.,RTC(Real Time Clock),TSC(Time Stamp Counter),HPET,ACPI PM-Timer,PIT.不同时钟源提供的精度不一样。
      • 时钟事件设备(clock event device):系统中可以触发one-shot(单次)或者周期性中断的设备

      注:当前内核同时存在新旧timer wheel和hrtimer两套timer的实现,内核启动后会进行从低精度模式到高精度时钟模式的切换,hrtimer模拟的tick中断将驱动传统的低精度定时器系统(基于时间轮)和内核进程调度.

操作系统附加知识
  1. 线程池、内存池
计算机网络
  1. 保证节点传输存储的可靠性

  2. TCP怎么保证可靠性,简述TCP建立连接和断开连接的过程

    • TCP保证可靠性:

      1. 序列号、确认应答、超时重传:数据达到接收方需要发出一个确认应答,表示已经收到该数据段,并且确认序号会说明它下一次需要接收的序列号。如果发送方迟迟未收到确认应答,那么可能是发送数据丢失,也可能是确认应答丢失,这时发送方在等待一定时间后会进行重传,这个时间一般是2*RTT(报文段往返时间)+一个偏差值

      2. 窗口控制与高速重发控制/快速重传(重复确认应答):TCP会利用窗口控制来提高传输速度,即在一个窗口大小内,不用一定等到应答才能发送下一段数据,窗口大小就是无需等待确认而可以继续发送数据的最大值。如果不使用窗口控制,每一个没收到确认应答的数据都要重发。

      3. 拥塞控制

        • 问题:窗口定的很大,发送端连续发送大量数据,可能会造成网络的拥塞。

        • 控制:

          • 慢启动:定义拥塞窗口,一开始将该窗口大小设为1,之后每次收到确认应答(经过一个RTT),将拥塞控制窗口大小*2
          • 拥塞避免:设置慢启动阈值,一般开始都设为65536,拥塞避免是指当拥塞窗口大小达到这个阈值,拥塞窗口的值不再指数上升,而是加法增加,每次确认应答,以此来避免拥塞.
          • 将报文段的超时重传看做拥塞,一旦发生超时重传,我们需要先将阈值设为当前窗口大小的一半,并且将窗口大小设为初值1,然后重新进入慢启动过程。
          • 快速重传:在遇到3次重复确认应答(高速重发控制)时,代表收到了3个报文段,但是这之前的1个段丢失了,便立即重传.
    • TCP建立连接和断开连接的过程:

      • 三次握手:

        1. client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给server,client进入SYN_SENT状态,等待server确认;
        2. server收到数据包后由标志位SYN=1知道client请求建立连接,server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将数据包发送给client以确认连接请求,server进入SYN_RCVD状态。
        3. client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给server,server检查ack是否等于K+1,ACK是否为1,如果正确则连接建立成功,client和server进入ESTABLISHED状态,完成三次握手,随后client与server可以开始传输数据
      • 四次挥手:由于TCP连接是全双工的,因此每个方向都必须单独进行关闭,原则:当一方完成数据发送任务后,发送一个FIN终止这一方向的连接,收到一个FIN只是意味着这个方向没有数据流动了,即不会再收到数据,但是在这个TCP连接上仍然能够发送数据,知道这个方向也发送了FIN。首先进行关闭的一方将执行主动关闭,另一方则执行被动关闭.

        1. 数据传输结束后,客户端的应用进程发出连接释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接收服务器发送的数据;
        2. 服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号+1,服务器进入CLOSE_WAIT状态,客户端收到后进入FIN_WAIT_2状态;
        3. 当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端确认;
        4. 客户端收到服务器的FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序列号+1.此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文段最大生存时间),然后关闭连接.
  3. TCP模型,状态转移

    TCP/IP四层模型,状态转移

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtgYLU02-1583505427319)(./image/tcp.png)]

  4. HTTP和HTTPS区别,HTTPS缺点

    • HTTP协议和HTTPS协议区别:

      1. HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输数据则是通过TLS加密后的,HTTPS具有更高的安全性.
      2. HTTPS在TCP三次握手后,还需要SSL的handshake,协商加密使用的对称加密秘钥
      3. HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书
      4. HTTP协议端口是80,HTTPS协议端口是443
    • HTTPS优点:

      1. HTTPS传输数据过程中使用密钥进行加密,所以安全性高
      2. HTTPS协议可以认证用户和服务器,确保数据发送到正确的用户和服务器
    • HTTPS缺点:

      1. 延迟高
      2. 部署成本高
  5. HTTP返回码
    HTTP协议的响应报文由状态行、响应头部和响应包体组成。响应码总体描述:

    • 1xx:指示信息->请求已接收,继续处理
    • 2xx: 成功->请求已被成功接收、理解、接受
    • 3xx:重定向,要完成请求必须进行更进一步的操作
    • 4xx:客户端错误,请求有语法错误,或者请求无法实现
    • 5xx:服务端错误,服务器未实现合法请求

    常见状态码:

    • 200 OK:客户端请求成功
    • 206 partiail content服务器已经正确处理部分GET请求实现断点续传和分片下载
    • 300 multiple choices(可选重定向):被请求的资源有一系列可供选择的反馈信息
    • 302 moved permanently(永久重定向):请求资源临时从不同URI中获取
    • 304
    • 403 服务器收到请求,但拒绝提供服务.
  6. IP地址作用,MAC地址作用

    MAC地址是硬件地址,用来定义网络设备的位置,主要由数据链路层负责,IP地址是IP协议提供的一种统一的地址格式,为互联网上每一个网络和主机分配一个逻辑地址,以此来屏蔽物理地址的差异.

  7. 操作系统的中断

  8. OSI七层协议,TCP/IP四层模型

    OSI七层协议:

    • 物理层:通过媒介传输比特,确定机械和电气规范。协议有:IEE802.3, CLOCK RJ45
    • 数据链路层:将比特组装成帧和点到点的传递,传输单位为帧,协议有:MAC, VLAN,PPP
    • 网络层:负责数据包从源到宿的传递和网际互连,传输单位为包,协议:IP,ARP,ICMP
    • 传输层:提供端到端的可靠报文传递和错误恢复,传输单位为报文,协议:TCP,UDP
    • 会话层:建立、管理和终止会话,传输单位为SPDU,协议:RPC,NFS
    • 表示层:对数据进行翻译、加密和压缩,传输单位为PPDU,协议:JPEG,ASII
    • 应用层:允许访问OSI环境的手段,传输单位APDU,协议:FTP,HTTP,DNS

    TCP/IP 4层模型包括:

    • 网络接口层:MAC,VLAN
    • 网络层:IP,ARP,ICMP
    • 传输层:TCP,UDP
    • 应用层:HTTP,DNS,SMTP
  9. 三次握手四次挥手【补充】

    TCP三次握手:(防止已经失效的连接请求报文突然又传输到服务器端导致服务器资源浪费)

C->SYN->S
S->SYN/ACK->C
C->ACK->S
TCP四次挥手:

C->FIN->S
S->ACK->C
S->FIN->C
C->ACK->S
  1. 搜索baidu,会用到计算机网络中什么层,每层干什么

    • 浏览器输入URL

      浏览器将URL解析为IP地址,解析域名用到DNS协议,首先主机会查询DNS缓存,如果没有就给本地DNS发送查询请求,DNS查询分为两个方式:递归查询,迭代查询,如果是迭代查询,本地的DNS服务器向根域名服务器发送查询请求,根域名服务器告知该域名的一级域名服务器,然后本地服务器给该一级域名服务器发送查询请求,然后依次查询该域名的IP。DNS服务器是基于UDP,会用到UDP协议;得到IP地址后,浏览器就要与服务器建立一个http链接。因此要用到http协议,http生成get请求报文,将该报文传给TCP层处理,所以会用到TCP协议,如果采用https还好使用https协议,先对http数据加密,TCP数据包会发送IP层,用到IP协议,IP层经过路由选择,一跳一跳发送到目的地

    • 协议和各层的工作

      1. DNS,HTTP,HTTPS协议属于应用层,应用层是体系结构最高层,应用层确定进程之间通信的性质以满足用户的需求,应用层不仅要提供应用进程所需要的信息交换和远地操作,而且还要作为互相作用的应用进程的用户代理。用来完成一些为进行语义上有意义的信息交换所必须的功能。应用层直接为用户的应用进程提供服务。
      2. TCP,UDP属于传输层,传输层负责主机中两个进程之间的通信。因特网的传输层可使用两种不同的协议,即面向连接的传输控制协议TCP,和无连接的用户数据报协议UDP。TCP:面向连接的服务,提供可靠的交付;UDP:无连接服务,不保证提供可靠的交付,尽可能努力交付
      3. IP协议,ARP协议属于网络层,网络层负责为分组交换网上的不同主机提供通信,在发送数据时,网络层将运输层产生的报文段或用户数据报封装成分组或包进行传送。在TCP/IP体系中,分组也要IP数据报,或简称数据报。网络层的另一个任务是选择合适的路由,是源主机运输层传下来的分组能够交付到目的主机.
      4. 数据链路层,当发送数据时,数据链路层的任务是将网络层的任务交下来的IP数据报组装成帧,在两个相邻节点的链路上传送以帧为单位的数据,每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制,流量控制信息)。控制信息使接收端能够知道一个帧从哪个比特开始和哪个比特结束。控制信息还能使接收端能够检测到所收到的帧中是否有差错.
      5. 物理层,透明地传送比特流。在物理层上所传数据的单位比特。传递信息所利用的一些物理媒体,双绞线、同轴电缆,光缆,并不在物理层之内而是在物理层的下面,因此物理媒体可以当做第0层
  2. TCP拥塞控制,达到什么情况的时候开始减慢增长的速度

    • 概念:拥塞控制是防止过多的数据注入网络,使得网络中的路由器或者链路过载。流量控制是点对点的通信量控制,而拥塞控制是全局的网络流量整体性控制。发送双方都有一个拥塞窗口–cwnd

    • 慢开始:最开始发送方的拥塞窗口为1,由小到大逐渐增大发送窗口和拥塞窗口。每经过一个传输轮次,拥塞窗口cwnd加倍。当cwnd超过慢开始门限,则使用拥塞避免算法,避免cwnd增长过大.

    • 拥塞避免:每经历一个往返时间RTT,cwnd+1,在慢开始和拥塞避免的过程中,一旦发现网络拥塞,就把慢开始门限设为当前值的一半,并且重新设置cwnd=1,重新慢启动(乘法减小,加法增大)

    • 快重传:接收方每次收到一个失序的报文段后就立即发出重复确认,发送方只要连续收到三个重复确认就立即重传(尽早重传未被确认的报文段)

    • 快恢复:当发送方连续收到三个重复确认,就乘法减半(慢开始门限减半),将当前的cwnd设置为慢开始门限,并且采用拥塞避免算法(连续收到三个重复请求,说明当前网络可能没有拥塞),采用快恢复算法时,慢开始只在建立连接和网络超时时才使用;

    • 减慢增长速度的时间:

      • 采用慢开始和拥塞避免算法的时候

        1. 一旦cwnd>慢开始门限,就采用拥塞避免算法,减慢增长速度;
        2. 一旦出现丢包的情况,就重新进行慢开始,减慢增长速度
      • 采用快恢复和快重传算法的时候

        1. 一旦cwnd>慢开始门限,采用拥塞避免算法,减慢增长速度
        2. 一旦发送方连续收到三个重复确认,就采用拥塞避免算法,减慢增长速度
  3. TCP保证可靠性的措施

    • 序列号、确认应答、超时重传
    • 窗口控制与高速重发控制/快速重传(重复确认应答)
    • 拥塞控制
  4. TCP/IP数据链路层的交互过程

    数据链路层用mac地址作为通信目标,数据包到达网络等准备往数据链路层发送时,网络层首先会去自己的ARP缓存表(存着IP-MAC对应关系)去查找目标IP的MAC地址,如果查到了,就将目标IP的MAC地址封装到链路层数包的包头。如果缓存中没找到,则会发起一个广播:who is IP XXX tell IP XXX,所有收到的广播的机器看到这个IP是不是自己的,如果是自己的,则以单播方式将自己的MAC地址回复给请求的机器

  5. 传递到IP层怎么知道报文是给那个应用程序的,如何区分UDP和TCP

    1. 根据端口区分
    2. 看IP头中的协议标识字段,17是UDP,6是TCP
  6. socket开发(网络编程)的基本步骤,i.e.,网络层的具体操作

    • 服务端:socket->bind->listen->accept
    • 客户端:socket->connect
  7. TCP三次握手

  8. TCP为什么两次或者四次握手不可以,必须三次握手?

    • 两次握手不可以:tcp是全双工通信,两次握手不能确定单向数据链路是可以通信的,不能保证反向通信的正常;

    • 不用四次:本来握手应该和挥手一样,都需要确认两个方向都能联通,本来模型是:

      1. 客户端发送sync0给服务器
      2. 服务器收到sync0,回复ack(syn0+1)
      3. 服务器发送sync1
      4. 客户端收到sync1,回复ack(syn1+1),因为tcp是全双工的,上边的四步确认了数据在两个方向上都是正确到达的,但是2,3步没有上下的联系,可以合并,加快握手的效率,因此变为3步握手
  9. TCP拥塞控制

    • 发送方维持一个叫做拥塞控制的窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地变化。发送方让自己的发送窗口等于拥塞窗口另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。慢开始算法的思路:不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大,逐渐增加拥塞窗口的大小,过程cwnd的大小呈指数增长,直到超过慢启动门限,然后进入拥塞避免阶段,cwnd的大小线性增长,当出现网络拥塞(三个重复的ack或者超时)时,将慢启动门限设置为出现拥塞时候大小的一半,cwnd的大小重新从0开始进入慢启动阶段.
    • 快重传和快恢复:快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方尽早知道有报文没达到对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期.
  10. TCP和UDP的区别和各自适用的场景

    • TCP和UDP的区别

      • 连接:TCP是面向连接的传输层协议,即传输数据之前必须先建立好连接;UDP无连接
      • 服务对象:TCP是点对点的两点间服务,即一条TCP连接只能有两个端点;UDP支持一/多对一/多的交互通信;
      • 可靠性:TCP是可靠交付(无差错,不丢失,不重复,按序达到);UDP是尽最大努力交付,不保证可靠交付;
      • 拥塞控制,流量控制:TCP有拥塞控制和流量控制保证数据传输的安全性;UDP没有拥塞控制,网络拥塞不会影响源主机的发送效率.
      • 报文长度:TCP是动态报文长度,即TCP报文长度是根据接收方的窗口大小和当期网络拥塞情况决定的;UDP面向报文,不合并,不拆分,保留上面传下来报文的边界
      • 首部开销:TCP首部开销大,首部20个字节,UDP首部开销小,8字节(源端口,目的端口,数据长度,校验和)
    • TCP和UDP适用场景

      TCP是可靠的但传输速度慢,UDP是不可靠的但传输速度快,若通信数据完整性让位于通信实时性,则选择TCP协议(如文件传输,重要状态的更新);反之则使用UDP协议(如视频传输、实时通信)

  11. 三次握手四次挥手

  12. http协议

    • HTTP协议:HTTP协议(hyper text transfer protocol超文本传输协议),用于从万维网服务器传输超文本到本地浏览器的传送协议。是一个应用层的面向对象的协议,工作于客户端-服务端架构.

    • 特点:快速,灵活,无连接,无状态,支持B/S C/S,默认端口80,基于TCP协议

    • HTTP协议过程概述:HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法,URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应内容包括协议版本,错误代码,服务器信息,响应头部,响应数据

    • HTTP请求/响应步骤:

      • 客户端连接Web服务器
      • 发送HTTP请求
      • 服务器接受请求并返回HTTP响应
      • 释放连接TCP连接
      • 客户端浏览器解析HTML
  13. GET和POST区别

    • 概念:

      • GET方式请求,浏览器会把http header和data一并发送出去,服务器响应200
      • POST方式请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200
    • 区别:

      1. get参数通过url传递,post放在request body中;
      2. get请求在url传递的参数是有长度限制,而post没有;
      3. get比post更不安全
      4. get请求只能url编码,post支持多种编码方式
      5. get请求参数会被完整保留/缓存在浏览器的历史记录中,而post不会
      6. get和post本质是TCP连接,get产生一个TCP数据包,post产生两个TCP数据包
  14. socket编程中服务器和客户端主要用到哪些函数

    1. 基于TCP的socket

      • 服务器端程序:

        1. 创建socket,用函数socket()
        2. 绑定IP地址,端口等信息到socket上,用函数bind()
        3. 设置允许的最大连接数,用函数listen()
        4. 接收客户端上来的连接,用函数accept()
        5. 收发数据,用函数send()和recv(),或者read()和write()
        6. 关闭网络连接
      • 客户端程序:

        1. 创建一个socket,用函数socket()
        2. 设置要连接的对方的IP地址和端口等属性
        3. 连接服务器,用函数connect()
        4. 收发数据,用函数send()和recv(),或者read()和write()
        5. 关闭网络连接

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4x3Zk4n-1583505427323)(./image/socket.png)]

    2. 基于UDP的socket:

      • 服务器端:

        1. 建立套接字文件描述符,使用socket()
        2. 设置服务器地址和侦听端口,初始化要绑定的网络地址
        3. 绑定侦听端口,使用bind函数,将套接字文件描述符和一个地址类型变量进行绑定
        4. 接收客户端的数据,使用recvfrom函数接收客户端的网络数据
        5. 向客户端发送数据,使用sendto()函数向服务器主机发送数据
        6. 关闭套接字,使用close函数释放资源
      • 客户端:

        1. 建立套接字文件描述符,socket()
        2. 设置服务器地址和端口,struct sockaddr
        3. 向服务器发送数据,sendto()
        4. 接收服务器的数据,recvfrom()
        5. 关闭套接字,close()
          [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69rQlWnq-1583505427325)(./image/socket-udp.png)]
  15. 数字证书

  16. 介绍一下UDP的connect函数

    可能不考,先待定

  17. TCP三次握手四次挥手

  18. 阻塞、非阻塞,同步,异步

    • 阻塞和非阻塞:调用者在事件没发生的时候,一直在等待事件发生,不能去处理别的任务,这是阻塞,可以去处理别的任务,这是非阻塞
    • 同步和异步:调用者必须循环去查看事件是否发生,这是同步,调用者不用自己查看事件是否发生,而是等待着注册在事件上的回调函数通知自己,这是异步;
  19. socket编程的send(),recv(),accept(),socket()函数

    send函数用来向TCP连接的另一端发送数据。客户端一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户端发送应答,send的作用是将要发送的数据拷贝到缓冲区,协议负责传输.recv函数用来从TCP连接的另一端接收数据,当应用程序调用recv函数时,recv先等待发送缓冲区的数据被协议传送完毕,然后从缓冲区读取接收到的内容给应用层,accept函数用来接收一个连接,内核维护了一个半连接队列和一个已完成连接队列,当队列为空时,accept函数阻塞,不为空时,accept函数从队列中取出一个已完成连接,返回文件描述符.

  20. http协议会话结束标志怎么截出来

    看tcp连接是否断开四步挥手阶段

  21. 三次握手

  22. 四次挥手

  23. TCP/IP数据链路层的交互过程

数据库
数据库基础
  1. 数据库事务,四个特性

    • 事务:一系列对系统中数据进行访问和更新的操作所组成的一个程序执行逻辑单元。事务DBMS中最基础的单位,事务是不可分割的。

    • 基本特征:原子性(A),一致性©,隔离性(I),持久性(D)

      • 原子性:事务包含的所有操作要么全部成功,要么全部失败回滚
      • 一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态,转账过程,总和一定
      • 隔离性:当多个用户并发访问数据库时,操作同一张表,数据库为每个用户开启的事务,不能被其他事务的操作干扰,多个并发事务之前要相互隔离,隔离等级:Read Uncommitted(读取未提交),Read Committed(读取提交),Repeated Read(可重复读),Serialization(可串行化)
      • 持久化: 数据提交了就能写成功
  2. 数据库的三大范式

    • 第一范式:关系模式R的所有属性都不能再分解为更基本的数据单位,属性不可分
    • 第二范式:在满足第一范式下,R的所有非主属性都完全依赖于R的每一个候选关键属性
    • 第三范式:R满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖R的任意一个候选关键字,非主属性不传递依赖于键码
  3. ACID特性

  4. 数据库索引

    索引是对数据库表中一列或者多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。加快检索数据.

  5. 数据库事务

  6. 数据库事务隔离

  7. inner join和left join

    暂时不考,暂时略

  8. 数据库事务一致性

  9. 索引

    暂略

  10. KV存储中,key有哪些要求

    暂时没查到

accept函数阻塞,不为空时,accept函数从队列中取出一个已完成连接,返回文件描述符.

  1. http协议会话结束标志怎么截出来

    看tcp连接是否断开四步挥手阶段

  2. 三次握手

  3. 四次挥手

  4. TCP/IP数据链路层的交互过程

数据库
数据库基础
  1. 数据库事务,四个特性

    • 事务:一系列对系统中数据进行访问和更新的操作所组成的一个程序执行逻辑单元。事务DBMS中最基础的单位,事务是不可分割的。

    • 基本特征:原子性(A),一致性©,隔离性(I),持久性(D)

      • 原子性:事务包含的所有操作要么全部成功,要么全部失败回滚
      • 一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态,转账过程,总和一定
      • 隔离性:当多个用户并发访问数据库时,操作同一张表,数据库为每个用户开启的事务,不能被其他事务的操作干扰,多个并发事务之前要相互隔离,隔离等级:Read Uncommitted(读取未提交),Read Committed(读取提交),Repeated Read(可重复读),Serialization(可串行化)
      • 持久化: 数据提交了就能写成功
  2. 数据库的三大范式

    • 第一范式:关系模式R的所有属性都不能再分解为更基本的数据单位,属性不可分
    • 第二范式:在满足第一范式下,R的所有非主属性都完全依赖于R的每一个候选关键属性
    • 第三范式:R满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖R的任意一个候选关键字,非主属性不传递依赖于键码
  3. ACID特性

  4. 数据库索引

    索引是对数据库表中一列或者多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。加快检索数据.

  5. 数据库事务

  6. 数据库事务隔离

  7. inner join和left join

    暂时不考,暂时略

  8. 数据库事务一致性

  9. 索引

    暂略

  10. KV存储中,key有哪些要求

    暂时没查到

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值