Liunx学习笔记

1、 说说include头文件的顺序以及双引号""和尖括号<>的区别

参考回答

  1. 区别:

    (1)尖括号<>的头文件是系统文件,双引号""的头文件是自定义文件。

    (2)编译器预处理阶段查找头文件的路径不一样。

  2. 查找路径:

    (1)使用尖括号<>的头文件的查找路径:编译器设置的头文件路径-->系统变量。

    (2)使用双引号""的头文件的查找路径:当前头文件目录-->编译器设置的头文件路径-->系统变量

2、 简述C++从代码到可执行二进制文件的过程

参考回答

C++和C语言类似,一个C++程序从源码到执行文件,有四个过程,预编译、编译、汇编、链接。

3、 说说数组和指针的区别

参考回答

  1. 概念:

    (1)数组:数组是用于储存多个相同类型数据的集合。 数组名是首元素的地址。

    (2)指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。 指针名指向了内存的首地址。

  2. 区别:

    (1)赋值:同类型指针变量可以相互赋值;数组不行,只能一个一个元素的赋值或拷贝

    (2)存储方式:

    数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,数组的存储空间,不是在静态区就是在栈上。

    指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。

    (3)求sizeof:

    数组所占存储空间的内存大小:sizeof(数组名)/sizeof(数据类型)

    在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。

4、 说说什么是野指针,怎么产生的,如何避免?

参考回答

  1. 概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

  2. 产生原因:释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。这些我们都要注意避免。

  3. 避免办法:

    (1)初始化置NULL

    (2)申请内存后判空

    (3)指针释放后置NULL

    (4)使用智能指针

5、 说说new和malloc的区别,各自底层实现原理。

参考回答

  1. new是操作符,而malloc是函数。

  2. new在调用的时候先分配内存,在调用构造函数,释放的时候调用析构函数;而malloc没有构造函数和析构函数。

  3. malloc需要给定申请内存的大小,返回的指针需要强转;new会调用构造函数,不用指定内存的大小,返回指针不用强转。

  4. new可以被重载;malloc不行

  5. new分配内存更直接和安全。

  6. new发生错误抛出异常,malloc返回null

6、 说说C++中函数指针和指针函数的区别。

参考回答

  1. 定义不同

    指针函数本质是一个函数,其返回值为指针。

    函数指针本质是一个指针,其指向一个函数。

7、 简述C++有几种传值方式,之间的区别是什么?

参考回答

传参方式有这三种:值传递、引用传递、指针传递

  1. 值传递:形参即使在函数体内值发生变化,也不会影响1实参的值;

  2. 引用传递:形参在函数体内值发生变化,会影响实参的值;

  3. 指针传递:在指针指向没有发生改变的前提下,形参在函数体内值发生变化,会影响实参的值

8、 简述一下堆和栈的区别

参考回答

区别:

  1. 堆栈空间分配不同。栈由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等;堆一般由程序员分配释放。

  2. 堆栈缓存方式不同。栈使用的是一级缓存, 它们通常都是被调用时处于存储空间中,调用完毕立即释放;堆则是存放在二级缓存中,速度要慢些。

  3. 堆栈数据结构不同。堆类似数组结构;栈类似栈结构,先进后出。

9、内存泄露及解决办法:

什么是内存泄露?

简单地说就是申请了一块内存空间,使用完毕后没有释放掉。(1)new和malloc申请资源使用后,没有用delete和free释放;(2)子类继承父类时,父类析构函数不是虚函数。(3)Windows句柄资源使用后没有释放。

怎么检测?

第一:良好的编码习惯,使用了内存分配的函数,一旦使用完毕,要记得使用其相应的函数释放掉。

第二:将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表。

第三:使用智能指针。

第四:一些常见的工具插件,如ccmalloc、Dmalloc、Leaky、Valgrind等等。

10、 malloc和局部变量分配在堆还是栈?

参考回答

malloc是在堆上分配内存,需要程序员自己回收内存;局部变量是在栈中分配内存,超过作用域就自动回收。

11、 内存模型,堆栈,常量区。

参考回答

·

内存模型(内存布局):

如上图,从低地址到高地址,一个程序由受保护的地址(一般储存的是NULL)、代码段、数据段、 BSS 段组成。

加:受保护的地址(0-4k):一般NULL指向这个地址。

  1. 数据段:存放程序中已初始化的全局变量和静态变量的一块内存区域。

  2. 代码段:存放程序执行代码的一块内存区域。只读,代码段的头部还会包含一些只读的常数变量。

  3. BSS 段:存放程序中未初始化的全局变量和静态变量的一块内存区域。

  4. 可执行程序在运行时又会多出两个区域:堆区和栈区。

    堆区:动态申请内存用。堆从低地址向高地址增长。

    栈区:存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。

  5. 最后还有一个文件映射区,位于堆和栈之间。

堆 heap :由new分配的内存块,其释放由程序员控制(一个new对应一个delete)

栈 stack :是那些编译器在需要时分配,在不需要时自动清除的存储区。存放局部变量、函数参数。

常量存储区 :存放常量,不允许修改。

12、 简述一下什么是面向对象

参考回答

  1. 面向对象是一种编程思想,把一切东西看成是一个个对象,比如人、耳机、鼠标、水杯等,他们各自都有属性,比如:耳机是白色的,鼠标是黑色的,水杯是圆柱形的等等,把这些对象拥有的属性变量和操作这些属性变量的函数打包成一个类来表示

  2. 面向过程和面向对象的区别

    面向过程:根据业务逻辑从上到下写代码

    面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程

13、 简述一下面向对象的三大特征

参考回答

面向对象的三大特征是封装、继承、多态。

继承方式

private继承

protected继承

public继承

基类的private成员

不可见

不可见

不可见

基类的protected成员

变为private成员

仍为protected成员

仍为protected成员

基类的public成员

变为private成员

变为protected成员

仍为public成员仍为public成员

  1. 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。封装本质上是一种管理:我们如何管理兵马俑呢?比如如果什么都不管,兵马俑就被随意破坏了。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通 道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,不想给别人看到的,我们使用protected/private把成员封装起来。开放一些共有的成员函数对成员合理的访问。所以封装本质是一种管理。

  2. 继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

    三种继承方式

  3. 多态:用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。实现多态,有二种方式,重写,重载。

14、 简述一下 C++ 的重载和重写,以及它们的区别

参考回答

  1. 重写

    是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类对象调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。

    示例如下:

#include<bits/stdc++.h>

using namespace std;

class A

{

public:

virtual void fun()

{

cout << "A";

}

};

class B :public A

{

public:

virtual void fun()

{

cout << "B";

}

};

int main(void)

{

A* a = new B();

a->fun();//输出B,A类中的fun在B类中重写

}

  1. 重载

    我们在平时写代码中会用到几个函数但是他们的实现功能相同,但是有些细节却不同。例如:交换两个数的值其中包括(int, float,char,double)这些个类型。在C语言中我们是利用不同的函数名来加以区分。这样的代码不美观而且给程序猿也带来了很多的不便。于是在C++中人们提出了用一个函数名定义多个函数,也就是所谓的函数重载。函数重载是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。

#include<bits/stdc++.h>

using namespace std;

class A

{

void fun() {};

void fun(int i) {};

void fun(int i, int j) {};

void fun1(int i,int j){};

};

15、如何理解抽象类?

参考回答

  1. 抽象类的定义如下:

    纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”,有虚函数的类就叫做抽象类。

  2. 抽象类有如下几个特点:

    1)抽象类只能用作其他类的基类,不能建立抽象类对象。

    2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。

    3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。

16、 请说说 STL 的基本组成部分

参考回答

标准模板库(Standard Template Library,简称STL)简单说,就是一些常用数据结构和算法的模板的集合。

 广义上讲,STL分为3类:Algorithm(算法)、Container(容器)和Iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接。

 详细的说,STL由6部分组成:容器(Container)、算法(Algorithm)、 迭代器(Iterator)、仿函数(Function object)、适配器(Adaptor)、空间配制器(Allocator)。

17、Linux中查看进程运行状态的指令、查看内存使用情况的指令、tar解压文件的参数。

参考回答

  1. 查看进程运行状态的指令:ps命令。“ps -aux | grep PID”,用来查看某PID进程状态

  2. 查看内存使用情况的指令:free命令。“free -m”,命令查看内存使用情况。

  3. tar解压文件的参数:

    五个命令中必选一个

    -c: 建立压缩档案

    -x:解压

    -t:查看内容

    -r:向压缩归档文件末尾追加文件

    -u:更新原压缩包中的文件

    这几个参数是可选的

    -z:有gzip属性的

    -j:有bz2属性的

    -Z:有compress属性的

    -v:显示所有过程

    -O:将文件解开到标准输出

18、 说说常用的Linux命令

参考回答

  1. cd命令:用于切换当前目录

  2. ls命令:查看当前文件与目录

  3. grep命令:该命令常用于分析一行的信息,若当中有我们所需要的信息,就将该行显示出来,该命令通常与管道命令一起使用,用于对一些命令的输出进行筛选加工。

  4. cp命令:复制命令

  5. mv命令:移动文件或文件夹命令

  6. rm命令:删除文件或文件夹命令

  7. ps命令:查看进程情况

  8. kill命令:向进程发送终止信号

  9. tar命令:对文件进行打包,调用gzip或bzip对文件进行压缩或解压

  10. cat命令:查看文件内容,与less、more功能相似

  11. top命令:可以查看操作系统的信息,如进程、CPU占用率、内存信息等

  12. pwd命令:命令用于显示工作目录。

19、 简述GDB常见的调试命令,什么是条件断点,多进程下如何调试。

参考回答

GDB调试:gdb调试的是可执行文件,在gcc编译时加入 -g ,告诉gcc在编译时加入调试信息,这样gdb才能调试这个被编译的文件 gcc -g tesst.c -o test

GDB命令格式:

  1. quit:退出gdb,结束调试

  2. list:查看程序源代码

    list 5,10:显示5到10行的代码

    list test.c:5, 10: 显示源文件5到10行的代码,在调试多个文件时使用

    list get_sum: 显示get_sum函数周围的代码

    list test,c get_sum: 显示源文件get_sum函数周围的代码,在调试多个文件时使用

  3. reverse-search:字符串用来从当前行向前查找第一个匹配的字符串

  4. run:程序开始执行

  5. help list/all:查看帮助信息

  6. break:设置断点

    break 7:在第七行设置断点

    break get_sum:以函数名设置断点

    break 行号或者函数名 if 条件:以条件表达式设置断点

  7. watch 条件表达式:条件表达式发生改变时程序就会停下来

  8. next:继续执行下一条语句 ,会把函数当作一条语句执行

  9. step:继续执行下一条语句,会跟踪进入函数,一次一条的执行函数内的代码

条件断点:break if 条件 以条件表达式设置断点

多进程下如何调试:用set follow-fork-mode child 调试子进程

或者set follow-fork-mode parent 调试父进程

20、 说说什么是大端小端,如何判断大端小端?

参考回答

小端模式:低的有效字节存储在低的存储器地址。小端一般为主机字节序;常用的X86结构是小端模式。很多的ARM,DSP都为小端模式。

大端模式:高的有效字节存储在低的存储器地址。大端为网络字节序;KEIL C51则为大端模式。

有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

如何判断:我们可以根据联合体来判断系统是大端还是小端。因为联合体变量总是从低地址存储。

21、 简述一下虚拟内存和物理内存,为什么要用虚拟内存,好处是什么?

参考回答

  1. 物理内存:物理内存有四个层次,分别是寄存器、高速缓存、主存、磁盘。

    寄存器:速度最快、量少、价格贵。

    高速缓存:次之。

    主存:再次之。

    磁盘:速度最慢、量多、价格便宜。

    操作系统会对物理内存进行管理,有一个部分称为内存管理器(memory manager),它的主要工作是有效的管理内存,记录哪些内存是正在使用的,在进程需要时分配内存以及在进程完成时回收内存。

  2. 虚拟内存:操作系统为每一个进程分配一个独立的地址空间,但是虚拟内存。虚拟内存与物理内存存在映射关系,通过页表寻址完成虚拟地址和物理地址的转换。

  3. 为什么要用虚拟内存:因为早期的内存分配方法存在以下问题:

    (1)进程地址空间不隔离。会导致数据被随意修改。

    (2)内存使用效率低。

    (3)程序运行的地址不确定。操作系统随机为进程分配内存空间,所以程序运行的地址是不确定的。

  4. 使用虚拟内存的好处:

    (1)扩大地址空间。每个进程独占一个4G空间,虽然真实物理内存没那么多。

    (2)内存保护:防止不同进程对物理内存的争夺和践踏,可以对特定内存地址提供写保护,防止恶意篡改。

    (3)可以实现内存共享,方便进程通信。

    (4)可以避免内存碎片,虽然物理内存可能不连续,但映射到虚拟内存上可以连续。

  5. 使用虚拟内存的缺点:

    (1)虚拟内存需要额外构建数据结构,占用空间。

    (2)虚拟地址到物理地址的转换,增加了执行时间。

    (3)页面换入换出耗时。

    (4)一页如果只有一部分数据,浪费内存。

22、 请你说说并发和并行

参考回答

  1. 并发:对于单个CPU,在一个时刻只有一个进程在运行,但是线程的切换时间则减少到纳秒数量级,多个任务不停来回快速切换。

  2. 并行:对于多个CPU,多个进程同时运行。

  3. 区别。通俗来讲,它们虽然都说是"多个进程同时运行",但是它们的"同时"不是一个概念。并行的"同时"是同一时刻可以多个任务在运行(处于running),并发的"同时"是经过不同线程快速切换,使得看上去多个任务同时都在运行的现象。

23、 说说进程、线程、协程是什么,区别是什么?

参考回答

  1. 进程:程序是指令、数据及其组织形式的描述,而进程则是程序的运行实例,包括程序计数器、寄存器和变量的当前值。

  2. 线程:微进程,一个进程里更小粒度的执行单元。一个进程里包含多个线程并发执行任务。

  3. 协程:协程是微线程,在子程序内部执行,可在子程序内部中断,转而执行别的子程序,在适当的时候再返回来接着执行。

区别:

  1. 线程与进程的区别:

    (1)一个线程从属于一个进程;一个进程可以包含多个线程。

    (2)一个线程挂掉,对应的进程挂掉;一个进程挂掉,不会影响其他进程。

    (3)进程是系统资源调度的最小单位;线程CPU调度的最小单位。

    (4)进程系统开销显著大于线程开销;线程需要的系统资源更少。

    (5)进程在执行时拥有独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。

    (6)进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈,线程切换时只需要切换硬件上下文和内核栈。

    (7)通信方式不一样。

    (8)进程适应于多核、多机分布;线程适用于多核

  2. 线程与协程的区别:

    (1)协程执行效率极高。协程直接操作栈基本没有内核切换的开销,所以上下文的切换非常快,切换开销比线程更小。

    (2)协程不需要多线程的锁机制,因为多个协程从属于一个线程,不存在同时写变量冲突,效率比线程高。

    (3)一个线程可以有多个协程。

24、 请你说说Linux的fork的作用

参考回答

fork函数用来创建一个子进程。对于父进程,fork()函数返回新创建的子进程的PID。对于子进程,fork()函数调用成功会返回0。如果创建出错,fork()函数返回-1。

25、 请你说说什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程

参考回答

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

  2. 僵尸进程:是指一个进程使用fork函数创建子进程,如果子进程退出,而父进程并没有调用wait()或者waitpid()系统调用取得子进程的终止状态,那么子进程的进程描述符仍然保存在系统中,占用系统资源,这种进程称为僵尸进程。

  3. 如何解决僵尸进程:

    (1)一般,为了防止产生僵尸进程,在fork子进程之后我们都要及时使用wait系统调用;同时,当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait(或waitpid),就可以清理退出的子进程以达到防止僵尸进程的目的。

    (2)使用kill命令。

26、 请你说说什么是守护进程,如何实现?

参考回答

  1. 守护进程:守护进程是运行在后台的一种生存期长的特殊进程。它独立于控制终端,处理一些系统级别任务。

  2. 如何实现:

    (1)创建子进程,终止父进程。方法是调用fork() 产生一个子进程,然后使父进程退出。

    (2)调用setsid() 创建一个新会话。

    (3)将当前目录更改为根目录。使用fork() 创建的子进程也继承了父进程的当前工作目录。

    (4)重设文件权限掩码。文件权限掩码是指屏蔽掉文件权限中的对应位。

    (5)关闭不再需要的文件描述符。子进程从父进程继承打开的文件描述符。

27、 说说进程同步的方式?

参考回答

  1. 信号量semaphore:是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步。P操作(递减操作)可以用于阻塞一个进程,V操作(增加操作)可以用于解除阻塞一个进程。

  2. 管道:一个进程通过调用管程的一个过程进入管程。在任何时候,只能有一个进程在管程中执行,调用管程的任何其他进程都被阻塞,以等待管程可用。

  3. 消息队列:消息的链接表,放在内核中。消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除;消息队列可以实现消息的随机查询,可以按照消息的类型读取。

28、 说说进程有多少种状态?

参考回答

进程有五种状态:创建、就绪、执行、阻塞、终止。一个进程创建后,被放入队列处于就绪状态,等待操作系统调度执行,执行过程中可能切换到阻塞状态(并发),任务完成后,进程销毁终止。

答案解析

创建状态

一个应用程序从系统上启动,首先就是进入创建状态,需要获取系统资源创建进程管理块(PCB:Process Control Block)完成资源分配。

就绪状态

在创建状态完成之后,进程已经准备好,处于就绪状态,但是还未获得处理器资源,无法运行。

运行状态

获取处理器资源,被系统调度,当具有时间片开始进入运行状态。如果进程的时间片用完了就进入就绪状态。

阻塞状态

在运行状态期间,如果进行了阻塞的操作,如耗时的I/O操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。等待再次获取处理器资源,被系统调度,当具有时间片就进入运行状态。

终止状态

进程结束或者被系统终止,进入终止状态

相互转换如图:

29、 说说线程间通信的方式有哪些?

参考回答

线程间的通信方式包括临界区、互斥量、信号量、条件变量、读写锁:

  1. 临界区:每个线程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。

  2. 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。

  3. 信号量:计数器,允许多个线程同时访问同一个资源。

  4. 条件变量:通过条件变量通知操作的方式来保持多线程同步。

  5. 读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。·

30、 说说线程同步方式有哪些?

参考回答

线程间的同步方式包括互斥锁、信号量、条件变量、读写锁:

  1. 互斥锁:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。

  2. 信号量:计数器,允许多个线程同时访问同一个资源。

  3. 条件变量:通过条件变量通知操作的方式来保持多线程同步。

  4. 读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。

31、 说说什么是死锁,产生的条件,如何解决?

参考回答

  1. 死锁: 是指多个进程在执行过程中,因争夺资源而造成了互相等待。此时系统产生了死锁。比如两只羊过独木桥,若两只羊互不相让,争着过桥,就产生死锁。

  2. 产生的条件:死锁发生有四个必要条件:

    (1)互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问,只能等待,直到进程使用完成后释放该资源;

    (2)请求保持条件:进程获得一定资源后,又对其他资源发出请求,但该资源被其他进程占有,此时请求阻塞,而且该进程不会释放自己已经占有的资源;

    (3)不可剥夺条件:进程已获得的资源,只能自己释放,不可剥夺;

    (4)环路等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  3. 如何解决:

    (1)资源一次性分配,从而解决请求保持的问题

    (2)可剥夺资源:当进程新的资源未得到满足时,释放已有的资源;

    (3)资源有序分配:资源按序号递增,进程请求按递增请求,释放则相反。

答案解析

举个例子,比如:如果此时有两个线程T1和T2,它们分别占有R1和R2资源

此时,T1请求R2资源的同时,T2请求R1资源。

这个时候T2说:你把R1给我,我就给你R2

T1说:不行,你要先给我R2,我才能给你R1

那么就这样,死锁产生了。如下图:

32、 有了进程,为什么还要有线程?

参考回答

  1. 原因

    进程在早期的多任务操作系统中是基本的执行单元。每次进程切换,都要先保存进程资源然后再恢复,这称为上下文切换。但是进程频繁切换将引起额外开销,从而严重影响系统的性能。为了减少进程切换的开销,人们把两个任务放到一个进程中,每个任务用一个更小粒度的执行单元来实现并发执行,这就是线程。

  2. 线程与进程对比

    (1)进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。

    但多个线程共享进程的内存,如代码段、数据段、扩展段,线程间进行信息交换十分方便。

    (2)调用 fork() 来创建进程的代价相对较高,即便利用写时复制技术,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销依然不菲。

    但创建线程比创建进程通常要快 10 倍甚至更多。线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表。

33、 说说多线程和多进程的不同?

参考回答

(1)一个线程从属于一个进程;一个进程可以包含多个线程。

(2)一个线程挂掉,对应的进程挂掉,多线程也挂掉;一个进程挂掉,不会影响其他进程,多进程稳定。

(3)进程系统开销显著大于线程开销;线程需要的系统资源更少。

(4)多个进程在执行时拥有各自独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。

(5)多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。

(6)通信方式不一样。

(7)多进程适应于多核、多机分布;多线程适用于多核

34、 简述互斥锁的机制,互斥锁与读写的区别?

参考回答

  1. 互斥锁机制:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。

  2. 互斥锁和读写锁:

    (1) 读写锁区分读者和写者,而互斥锁不区分

    (2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

35、 说说什么是信号量,有什么作用?

参考回答

  1. 概念:信号量本质上是一个计数器,用于多进程对共享数据对象的读取,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

  2. 原理:由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),具体的行为如下:

    (1)P(sv)操作:如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行(信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位)。

    (2)V(sv)操作:如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1(若此时信号量的值为0,则进程进入挂起状态,直到信号量的值大于0,若进程被唤醒则返回至第一步)。

  3. 作用:用于多进程对共享数据对象的读取,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

36、 进程、线程的中断切换的过程是怎样的?

参考回答

上下文切换指的是内核(操作系统的核心)在CPU上对进程或者线程进行切换。

  1. 进程上下文切换

    (1)保护被中断进程的处理器现场信息

    (2)修改被中断进程的进程控制块有关信息,如进程状态等

    (3)把被中断进程的进程控制块加入有关队列

    (4)选择下一个占有处理器运行的进程

    (5)根据被选中进程设置操作系统用到的地址转换和存储保护信息

     切换页目录以使用新的地址空间

     切换内核栈和硬件上下文(包括分配的内存,数据段,堆栈段等)

    (6)根据被选中进程恢复处理器现场

  2. 线程上下文切换

    (1)保护被中断线程的处理器现场信息

    (2)修改被中断线程的线程控制块有关信息,如线程状态等

    (3)把被中断线程的线程控制块加入有关队列

    (4)选择下一个占有处理器运行的线程

    (5)根据被选中线程设置操作系统用到的存储保护信息

     切换内核栈和硬件上下文(切换堆栈,以及各寄存器)

    (6)根据被选中线程恢复处理器现场

37、 多线程和单线程有什么区别,多线程编程要注意什么,多线程加锁需要注意什么?

参考回答

  1. 区别:

    (1)多线程从属于一个进程,单线程也从属于一个进程;一个线程挂掉都会导致从属的进程挂掉。

    (2)一个进程里有多个线程,可以并发执行多个任务;一个进程里只有一个线程,就只能执行一个任务。

    (3)多线程并发执行多任务,需要切换内核栈与硬件上下文,有切换的开销;单线程不需要切换,没有切换的开销。

    (4)多线程并发执行多任务,需要考虑同步的问题;单线程不需要考虑同步的问题。

  2. 多线程编程需要考虑同步的问题。线程间的同步方式包括互斥锁、信号量、条件变量、读写锁。

  3. 多线程加锁,主要需要注意死锁的问题。破坏死锁的必要条件从而避免死锁。

答案解析

  1. 死锁: 是指多个进程在执行过程中,因争夺资源而造成了互相等待。此时系统产生了死锁。比如两只羊过独木桥,若两只羊互不相让,争着过桥,就产生死锁。

  2. 产生的条件:死锁发生有四个必要条件:

    (1)互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问,只能等待,直到进程使用完成后释放该资源;

    (2)请求保持条件:进程获得一定资源后,又对其他资源发出请求,但该资源被其他进程占有,此时请求阻塞,而且该进程不会释放自己已经占有的资源;

    (3)不可剥夺条件:进程已获得的资源,只能自己释放,不可剥夺;

    (4)环路等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  3. 如何解决:

    (1)资源一次性分配,从而解决请求保持的问题

    (2)可剥夺资源:当进程新的资源未得到满足时,释放已有的资源;

    (3)资源有序分配:资源按序号递增,进程请求按递增请求,释放则相反。

答案解析

举个例子,比如:如果此时有两个线程T1和T2,它们分别占有R1和R2资源

此时,T1请求R2资源的同时,T2请求R1资源。

这个时候T2说:你把R1给我,我就给你R2

T1说:不行,你要先给我R2,我才能给你R1

那么就这样,死锁产生了。如下图:

38、 说说sleep和wait的区别?

参考回答

  1. sleep

    sleep是一个延时函数,让进程或线程进入休眠。休眠完毕后继续运行。

    在linux下面,sleep函数的参数是秒,而windows下面sleep的函数参数是毫秒。

    windows下面sleep的函数参数是毫秒。

2.wait

wait是父进程回收子进程PCB资源的一个系统调用。进程一旦调用了wait函数,就立即阻塞自己本身,然后由wait函数自动分析当前进程的某个子进程是否已经退出,当找到一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞,直到有一个出现为止。

3.区别:

(1)sleep是一个延时函数,让进程或线程进入休眠。休眠完毕后继续运行。

(2)wait是父进程回收子进程PCB(Process Control Block)资源的一个系统调用。

39、进程和线程相比,为什么慢?

参考回答

  1. 进程系统开销显著大于线程开销;线程需要的系统资源更少。

  2. 进程切换开销比线程大。多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。

  3. 进程通信比线程通信开销大。进程通信需要借助管道、队列、共享内存,需要额外申请空间,通信繁琐;而线程共享进程的内存,如代码段、数据段、扩展段,通信快捷简单,同步开销更小。

40、简述epoll和select的区别,epoll为什么高效?

参考回答

  1. 区别:

    (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;而epoll保证了每个fd在整个过程中只会拷贝一次。

    (2)每次调用select都需要在内核遍历传递进来的所有fd;而epoll只需要轮询一次fd集合,同时查看就绪链表中有没有就绪的fd就可以了。

    (3)select支持的文件描述符数量太小了,默认是1024;而epoll没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048。

  2. epoll为什么高效:

    (1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。

    (2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,而且把当前进程往等待队列上挂也只挂一次,这也能节省不少的开销。

41、 说说多路IO复用技术有哪些,区别是什么?

参考回答

  1. select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。

  2. 区别:

    (1)poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。

    (2)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。

    (3)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,而且把当前进程往等待队列上挂也只挂一次,这也能节省不少的开销。

42、 简述同步与异步的区别,阻塞与非阻塞的区别?

参考回答

  1. 同步与异步的区别:

    同步:是所有的操作都做完,才返回给用户结果。即写完数据库之后,再响应用户,用户体验不好。

    异步:不用等所有操作都做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好。

  2. 阻塞与非阻塞的区别:

    阻塞:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的检查这个函数有没有返回,必须等这个函数返回后才能进行下一步动作。

    非阻塞:非阻塞等待,每隔一段时间就去检查IO事件是否就绪。没有就绪就可以做其他事情。

43、 请介绍一下5种IO模型

参考回答

  1. 阻塞IO:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的检查这个函数有没有返回,必须等这个函数返回后才能进行下一步动作。

  2. 非阻塞IO:非阻塞等待,每隔一段时间就去检查IO事件是否就绪。没有就绪就可以做其他事情。

  3. 信号驱动IO:Linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO信号,然后处理IO事件。

  4. IO多路复用:Linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检查。知道有数据可读或可写时,才真正调用IO操作函数。

  5. 异步IO:Linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。用户可以直接去使用数据。

44、 请说一下socket网络编程中客户端和服务端用到哪些函数?

参考回答

  1. 服务器端函数:

    (1)socket创建一个套接字

    (2)bind绑定ip和port

    (3)listen使套接字变为可以被动链接

    (4)accept等待客户端的链接

    (5)write/read接收发送数据

    (6)close关闭连接

  2. 客户端函数:

    (1)创建一个socket,用函数socket()

    (2)bind绑定ip和port

    (3)连接服务器,用函数connect()

    (4)收发数据,用函数send()和recv(),或read()和write()

    (5)close关闭连接、

45、 简述网络七层参考模型,每一层的作用?

参考回答

OSI七层模型

功能

对应的网络协议

TCP/IP四层概念模型

应用层

文件传输,文件管理,电子邮件的信息处理

HTTP、TFTP, FTP, NFS, WAIS、SMTP

应用层

表示层

确保一个系统的应用层发送的消息可以被另一个系统的应用层读取,编码转换,数据解析,管理数据的解密和加密。

Telnet, Rlogin, SNMP, Gopher

应用层

会话层

负责在网络中的两节点建立,维持和终止通信。

SMTP, DNS

应用层

传输层

定义一些传输数据的协议和端口。

TCP, UDP

传输层

网络层

控制子网的运行,如逻辑编址,分组传输,路由选择

IP, ICMP, ARP, RARP, AKP, UUCP

网络层

数据链路层

主要是对物理层传输的比特流包装,检测保证数据传输的可靠性,将物理层接收的数据进行MAC(媒体访问控制)地址的封装和解封装

FDDI, Ethernet, Arpanet, PDN, SLIP, PPP,STP。HDLC,SDLC,帧中继

数据链路层

物理层

定义物理设备的标准,主要对物理连接方式,电气特性,机械特性等制定统一标准。

IEEE 802.1A, IEEE 802.2到IEEE 802.

数据链路层

46、 简述 TCP 三次握手和四次挥手的过程

参考回答

三次握手

1)第一次握手:建立连接时,客户端向服务器发送SYN包(seq=x),请求建立连接,等待确认

2)第二次握手:服务端收到客户端的SYN包,回一个ACK包(ACK=x+1)确认收到,同时发送一个SYN包(seq=y)给客户端

3)第三次握手:客户端收到SYN+ACK包,再回一个ACK包(ACK=y+1)告诉服务端已经收到

4)三次握手完成,成功建立连接,开始传输数据

四次挥手

1)客户端发送FIN包(FIN=1)给服务端,告诉它自己的数据已经发送完毕,请求终止连接,此时客户端不发送数据,但还能接收数据

2)服务端收到FIN包,回一个ACK包给客户端告诉它已经收到包了,此时还没有断开socket连接,而是等待剩下的数据传输完毕

3)服务端等待数据传输完毕后,向客户端发送FIN包,表明可以断开连接

4)客户端收到后,回一个ACK包表明确认收到,等待一段时间,确保服务端不再有数据发过来,然后彻底断开连接

47、 说说 TCP 2次握手行不行?为什么要3次

参考回答

  1. 为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤

  2. 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

48、 简述 TCP 和 UDP 的区别,它们的头部结构是什么样的

参考回答

  1. TCP协议是有连接的,有连接的意思是开始传输实际数据之前TCP的客户端和服务器端必须通过三次握手建立连接,会话结束之后也要结束连接。而UDP是无连接的

    TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到。

    TCP协议所需资源多,TCP首部需20个字节(不算可选项),UDP首部字段只需8个字节。

    TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率

    TCP是一对一的连接,而UDP则可以支持一对一,多对多,一对多的通信。

    TCP面向的是字节流的服务,UDP面向的是报文的服务。

49、 简述 TCP 连接 和 关闭的具体步骤

参考回答

  1. TCP通过三次握手建立链接

1)第一次握手:建立连接时,客户端向服务器发送SYN包(seq=x),请求建立连接,等待确认

2)第二次握手:服务端收到客户端的SYN包,回一个ACK包(ACK=x+1)确认收到,同时发送一个SYN包(seq=y)给客户端

3)第三次握手:客户端收到SYN+ACK包,再回一个ACK包(ACK=y+1)告诉服务端已经收到

4)三次握手完成,成功建立连接,开始传输数据

  1. 通过4次挥手关闭链接

1)客户端发送FIN包(FIN=1)给服务端,告诉它自己的数据已经发送完毕,请求终止连接,此时客户端不发送数据,但还能接收数据

2)服务端收到FIN包,回一个ACK包给客户端告诉它已经收到包了,此时还没有断开socket连接,而是等待剩下的数据传输完毕

3)服务端等待数据传输完毕后,向客户端发送FIN包,表明可以断开连接

4)客户端收到后,回一个ACK包表明确认收到,等待一段时间,确保服务端不再有数据发过来,然后彻底断开连接

50、 说说 TCP 可靠性保证

参考回答

TCP主要提供了检验和、序列号/确认应答、超时重传、最大消息长度、滑动窗口控制等方法实现了可靠性传输。

51、 简述 TCP 滑动窗口以及重传机制

参考回答

  1. 滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。

    TCP的滑动窗口解决了端到端的流量控制问题,允许接受方对传输进行限制,直到它拥有足够的缓冲空间来容纳更多的数据。

  2. TCP在发送数据时会设置一个计时器,若到计时器超时仍未收到数据确认信息,则会引发相应的超时或基于计时器的重传操作,计时器超时称为重传超时(RTO) 。另一种方式的重传称为快速重传,通常发生在没有延时的情况下。若TCP累积确认无法返回新的ACK,或者当ACK包含的选择确认信息(SACK)表明出现失序报文时,快速重传会推断出现丢包,需要重传。

52、 说说滑动窗口过小怎么办

参考回答

  1. 我们可以假设窗口的大小是1,也是就每次只能发送一个数据,并且发送方只有接受方对这个数据进行确认了以后才能发送下一个数据。如果说窗口过小,那么当传输比较大的数据的时候需要不停的对数据进行确认,这个时候就会造成很大的延迟。

53、 说说如果三次握手时候每次握手信息对方没收到会怎么样,分情况介绍

参考回答

  1. 如果第一次握手消息丢失,那么请求方不会得到ack消息,超时后进行重传

  2. 如果第二次握手消息丢失,那么请求方不会得到ack消息,超时后进行重传

  3. 如果第三次握手消息丢失,那么Server 端该TCP连接的状态为SYN_RECV,并且会根据 TCP的超时重传机制,会等待3秒、6秒、12秒后重新发送SYN+ACK包,以便Client重新发送ACK包。而Server重发SYN+ACK包的次数,可以设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5.如果重发指定次数之后,仍然未收到 client 的ACK应答,那么一段时间后,Server自动关闭这个连接。

    client 一般是通过 connect() 函数来连接服务器的,而connect()是在 TCP的三次握手的第二次握手完成后就成功返回值。也就是说 client 在接收到 SYN+ACK包,它的TCP连接状态就为 established (已连接),表示该连接已经建立。那么如果 第三次握手中的ACK包丢失的情况下,Client 向 server端发送数据,Server端将以 RST包响应,方能感知到Server的错误。

54、 简述什么是 MSL,为什么客户端连接要等待2MSL的时间才能完全关闭

参考回答

  1. MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

  2. 为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。

  • 两个理由:

    • 保证客户端发送的最后一个ACK报文段能够到达服务端。

      这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。

    • 防止“已失效的连接请求报文段”出现在本连接中。

      客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。

55、 说说什么是 TCP 粘包和拆包?

参考回答

  1. TCP是个“流”协议,所谓流,就是没有界限的一串数据。大家可以想想河里的流水,是连成一片的,其间并没有分界线。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。

56、 说说 TCP 与 UDP 在网络协议中的哪一层,他们之间有什么区别?

参考回答

TCP和UDP协议都是传输层协议。二者的区别主要有:

  1. 基于连接vs无连接

  • TCP是面向连接的协议。

  • UDP是无连接的协议。UDP更加适合消息的多播发布,从单个点向多个点传输消息。

  1. 可靠性

  • TCP提供交付保证,传输过程中丢失,将会重发。

  • UDP是不可靠的,不提供任何交付保证。(网游和视频的丢包情况)

  1. 有序性

  • TCP保证了消息的有序性,即使到达客户端顺序不同,TCP也会排序。

  • UDP不提供有序性保证。

  1. 数据边界

  • TCP不保存数据边界。

    • 虽然TCP也将在收集所有字节之后生成一个完整的消息,但是这些信息在传给传输给接受端之前将储存在TCP缓冲区,以确保更好的使用网络带宽。

  • UDP保证。

    • 在UDP中,数据包单独发送的,只有当他们到达时,才会再次集成。包有明确的界限来哪些包已经收到,这意味着在消息发送后,在接收器接口将会有一个读操作,来生成一个完整的消息。

  1. 速度

  • TCP速度慢

  • UDP速度快。应用在在线视频媒体,电视广播和多人在线游戏。

  1. 发送消耗

  • TCP是重量级。

  • UDP是轻量级。

    • 因为UDP传输的信息中不承担任何间接创造连接,保证交货或秩序的的信息。

    • 这也反映在用于报头大小。

  1. 报头大小

  • TCP头大。

    • 一个TCP数据包报头的大小是20字节。

    • TCP报头中包含序列号,ACK号,数据偏移量,保留,控制位,窗口,紧急指针,可选项,填充项,校验位,源端口和目的端口。

  • UDP头小。

    • UDP数据报报头是8个字节。

    • 而UDP报头只包含长度,源端口号,目的端口,和校验和。

  1. 拥塞或流控制

  • TCP有流量控制。

    • 在任何用户数据可以被发送之前,TCP需要三数据包来设置一个套接字连接。TCP处理的可靠性和拥塞控制。

  • UDP不能进行流量控制。

  1. 应用

  • 由于TCP提供可靠交付和有序性的保证,它是最适合需要高可靠并且对传输时间要求不高的应用。

  • UDP是更适合的应用程序需要快速,高效的传输的应用,如游戏。

  • UDP是无状态的性质,在服务器端需要对大量客户端产生的少量请求进行应答的应用中是非常有用的。

  • 在实践中,TCP被用于金融领域,如FIX协议是一种基于TCP的协议,而UDP是大量使用在游戏和娱乐场所。

10.上层使用的协议

  • 基于TCP协议的:Telnet,FTP以及SMTP协议。

  • 基于UDP协议的:DHCP、DNS、SNMP、TFTP、BOOTP。

57、 说说浏览器从输入 URL 到展现页面的全过程

参考回答

  • 1、输入地址

  • 2、浏览器查找域名的 IP 地址

  • 3、浏览器向 web 服务器发送一个 HTTP 请求

  • 4、服务器的永久重定向响应

  • 6、服务器处理请求

  • 7、服务器返回一个 HTTP 响应

  • 8、浏览器显示 HTML

  • 9、浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)

58、 简述 HTTP 和 HTTPS 的区别?

参考回答

  1. HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

    HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

    HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

  2. HTTP与HTTPS的区别   

  • https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。   

  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。   

  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

59、 简述 HTTP 1.0,1.1,2.0 的主要区别

参考回答

http/1.0 :

  1. 默认不支持长连接,需要设置keep-alive参数指定

  2. 强缓存expired、协商缓存last-modified\if-modified-since 有一定的缺陷

http 1.1 :

  1. 默认长连接(keep-alive),http请求可以复用Tcp连接,但是同一时间只能对应一个http请求(http请求在一个Tcp中是串行的)

  2. 增加了强缓存cache-control、协商缓存etag\if-none-match 是对http/1 缓存的优化

http/2.0 :

  1. 多路复用,一个Tcp中多个http请求是并行的 (雪碧图、多域名散列等优化手段http/2中将变得多余)

  2. 二进制格式编码传输

  3. 使用HPACK算法做header压缩

  4. 服务端推送

60、 构造函数为什么不能被声明为虚函数

参考回答

  1. 从存储空间角度:虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,就需要到vtable 中调用,可是对象还没有实例化,没有内存空间分配,如何调用。(悖论)

  2. 从使用角度:虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

  3. 从实现上看,vbtl 在构造函数调用后才建立,因而构造函数不可能成为虚函数。从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数。

61、 析构函数为什么不能被声明为虚函数

参考回答

1.虚析构:将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。如果基类的析构函数不是虚函数,在特定情况下会导致派生来无法被析构。

(1)用派生类类型指针绑定派生类实例,析构的时候,不管基类析构函数是不是虚函数,都会正常析构

(2)用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。为什么会出现这种现象呢,个人认为析构的时候如果没有虚函数的动态绑定功能,就只根据指针的类型来进行的,而不是根据指针绑定的对象来进行,所以只是调用了基类的析构函数;如果基类的析构函数是虚函数,则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。

2、C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。

61、 进程相关命令

参考回答

1、查看进程

    ps    aux/ajx

    a:显示终端上的所有进程,包括其他用户的进程

    u:显示进程的详细信息

    x:显示没有控制终端的进程

    j:列出与作业控制相关的信息

2、SATA参数意义(ps    aux命令中)

D:不可中断

R:正在运行,或在队列中的进程

S:处于休眠状态

T:停止或被追踪

Z:僵尸进程

W:进入内存交换

X:死掉的进程

<:高优先级

N:低优先级

s:包含子进程

+:位于前台的进程组

3、实时显示进程动态

top

在top命令执行后,可以按以下按键对显示的结果进行排序

M:依据内存使用量排序

P:依据CPU占有率排序

T:依据进程运行时间长短排序

U:依据用户名来筛选进程

K:输入指定的PID杀死进程

4、杀死进程

kill    -9    进程号

62、 进程间通讯

参考回答

1、Linux进程间通讯方式:

2、匿名管道

(1)管道的特点

[1]管道其实是一个在内核内存中维护的缓冲器,这个缓冲器的存储能力是有限的,不同的操作系统大小不一定相同。

[2]管道拥有文件的特质:读操作、写操作,匿名管道没有文件实体,有名管道有文件实体但不存储数据。可以按照操作文件的方式对管道进行操作。

[3]一个管道是一个字节流,使用管道时不存在消息或者消息边界的概念,从管道读取数据为进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是多少

[4]通过管道传递的数据是顺序的,从管道中读取出来的字节的顺序和它们被写入管道的顺字是完全一样的。

[5]在管道中的数据的传递方向是单向的,一端用于写入,一端用于读取,管道是半双工的。

[6]从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据,在管道中无法使用 lseek()来随机的访问数据

[7]匿名管道只能在具有公共祖先的进程 (父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

(2)匿名管道的使用

[1]创建匿名管道

#include <unistd.h>

int pipe(int pipefd[2]);

[2]查看管道缓冲大小命令

ulimit -a

[3]查看管道缓冲大小函数

#include <unistd.h>

long fpathconf(int fd, int name);

3、有名管道

(1)有名管道

【1】匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道 (EIFO) ,也叫命名管道、EIEO文件。

【2】有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 EIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 EIFO 相互通信,因此,通过 EIFO不相关的进程也能交换数据

【3】一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/0系统调用了 (如read()、write()和close()) 。与管道一样,EIFO 也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。EIFO的名称也由此而来:先入先出

【4】有名管道 (EIFO)和匿名管道 (pipe) 有一些特点是相同的,不一样的地方在于1、FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中2、当使用 EIFO 的进程退出后,EIEO 文件将继续保存在文件系统中以便以后使用3、EIEO有名字,不相关的进程可以通过打开有名管道进行通信。

(2)有名管道的使用

【1】通过命令创建有名管道

            mkfifo 名字

【2】通过函数创建有名管道

            #include <sys/types.h>

            #include <sys/stat.h>

                int mkfifo(const char *pathname, mode t mode);(pathname:文件名(要打开的文件路径)(mode&umask做抹去一部分权限的操作)

【3】一旦使用 mkfifo 创建了一个 FIFO,就可以使用 open 打开它,常见的文件I/0 函数都可用于 fifo。如: close、read、write、unlink 等。

【4】FIFO 严格遵循先进先出 (First in Eirst out),对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如 lseek()等文件定位操作

3、内存映射

(1)内存映射

【1】内存映射(Memory-mapped I/0) 是将磁盘文件的数据映射到内存,用户通过修改内存映射内存就能修改磁盘文件

(2)内存映射相关系统调用

#include <sys/mman.h>

void *mmap(void *addr, size_t length,int prot,int flags,int fd, off_t offset);

int munmap(void *addr, size_t length);

4、信号

(1)信号的概念

【1】信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

【2】发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下:

①对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输入ctrl+C通常会给进程发送一个中断信号。

②硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。比如执行一条异常的机器语言指令,诸如被 0 除,或者引用了无法访问的内存区域。

③系统状态变化,比如 alarm 定时器到期将引起 SIGALRM 信号,进程执行的 CPU时间超限,或者该进程的某个子进程退出。

④运行kill命令或者调用kill函数

【3】使用信号的两个主要目的是

①让进程知道已经发生了一个特定的事情。

②强迫进程执行它自己代码中的信号处理程序。

【4】信号的特点

①简单

②不能携带大量信息

③满足某个特定条件才发送

④优先级比较高

【5】查看系统定义的信号列表: kill -l

【6】前 31 个信号为常规信号,其余为实时信号

(2)信号的5种默认处理动作

【1】查看信号的详细信息: mansignal

【2】信号的 5中默认处理动作

Term            终止进程

Ign              当前进程忽略掉这个信号

Core            终止进程,并生成一个core文件

stop            暂停当前进程

Cont            继续执行当前被暂停的进程

【3】信号的几种状态:产生、未决、递达

【4】SIGKILL和 SIGSTOP 信号不能被捕捉、阻塞或者忽略,只能执行默认动作

5、信号集

(1)信号集

【1】许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t。

【2】在 PCB 中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为未决信号集”。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB中的这两个信号集进行修改。

【3】信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的这一段时间

【4】信号的“阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生

【5】信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

6、共享内存

(1)共享内存使用步骤

【1】调用 shmget() 创建一个新共享内存段或取得一个既有共享内存段的标识符 (即由其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符。

【2】使用 shmat()来附上共享内存段,即使该段成为调用进程的虚拟内存的一部分

【3】此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存程序需要使用由 shmat() 调用返回的 addr 值,它是502S向进程的虚拟地址空间中该共享内存段的起点的指针。

【4】调用 shmdt() 来分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步。

【5】调用 shmct1() 来删除共享内存段。只有当当前所有附加内存段的进程都与之分离之后内存段才会销毁。只有一个进程需要执行这一步

63、 线程

参考回答

1、线程概述

(1)线程概述

【1】与进程(process) 类似,线程(thread)是允许应用程序并发执行多个任务的一种机制。一个进程可以包含多个线程。同一个程序中的所有线程均会独立执行相同程序,且共享同一份全局内存区域,其中包括初始化数据段、未初始化数据段,以及堆内存段。 (传统意义上的 UNIX 进程只是多线程程序的一个特例,该进程只包含一个线程)

【2】进程是 CPU 分配资源的最小单位,线程是操作系统调度执行的最小单位。

【3】线程是轻量级的进程 (LWP: Light weight Process),在 Linux 环境下线程的本质仍是进程

【4】查看指定进程的 LWP 号: ps -Lf pid

(2)线程与进程的区别

【1】进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换

【2】调用 fork()来创建进程的代价相对较高,即便利用写时复制技术,仍热需要复制诸如内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销依然不菲

【3】线程之间能够方便、快速地共享信息。只需将数据复制到共享 (全局或堆)变量中即可

【4】创建线程比创建进程通常要快 10 倍甚至更多。线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表。

(3)线程之间共享和非共享资源

【1】共享资源

①进程 ID和父进程ID

②进程组 ID和会话 ID

③用户 ID 和用户组 ID

④文件描述符表

⑤信号处置

⑥文件系统的相关信息: 文件权限掩码umask)、当前工作目录

⑦虚拟地址空间 (除栈、.text)

【2】非共享资源

①线程ID

②信号掩码

③线程特有数据

④error 变量

⑤实时调度策略和优先级

⑥栈,本地变量和函数的调用链接信息

(4)线程操作

pthread_t pthread_self(void);(功能:获取当前的线程的线程ID)

int pthread equal(pthread_t tl,pthread_t t2);(功能:比较两个线程ID是否相等)

int pthread_creatI(pthread_t *thread, const pthread attr_t *attr,void x(*start routine)(void *), void *arg);(功能:创建一个子线程)(thread: 传出参数,线程创建成功后,子线程的线程ID被写到该变量;attr : 设置线程的属性,一般使用默认值,NULL;start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码;arg : 给第三个参数使用,传参)

void pthread exit(void *retval);(功能:终止一个线程,在哪个线程中调用,就表示终止哪个线程)(retval:需要传递一个指针,作为一个返回值,可以在pthread_join()中获取到)

int pthread join(pthread_t thread,void **retval);(功能:和一个已经终止的线程进行连接 )

int pthread detach(pthread_t thread);(功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统。)

int pthreadl cancel(pthread_t thread);(功能: 取消线程(让线程终止))(返回线程的pid)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值