面试经
1、makefile文件对作用
一个工程中的源文件不计其数,按照类型、功能、模块分别为放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译、哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个shell脚本一样,其中也可以执行操作系统的命令,makefile带来的好处就是自动化编译,一旦写好,只需要一个makefile命令,整个工程完全可以自动化编译,极大的提高了软件开发的效率。
2、linux进程内存空间分为哪几个段,作用分别是什么?
1、代码区(Text)
主要用于存放功能代码,函数指针指向该区域,比如函数名
2、只读常量区(Text)
存放的字符串常量和用const修饰的,并且已经初始化的全局变量和静态局部变量
3、全局区/数据区(data)
存放已经初始化的全局变量和静态局部变量(这个区的数据都有存储空间、生命周期是整个程序)
4、bss段
存放还没有初始化的全局变量和静态局部变量,bss段会在main函数执行前清零
5、堆区
主要表示使用malloc、calloc、realloc、free等用于手动申请的动态内存,需要手动释放
6、栈区
主要存放非静态局部变量(包括函数的参数),块变量,有操作系统自动处理
网络部分
1、OSI七层网络模型及每层的作用?
物理层:建立、维护、断开物理连接,数据形式是电流。保证正确传输比特流(比特流)
数据链路层:建立逻辑连接、进行物理地址寻址、差错校验等功能。负责结点到结点之间的数据传输(数据帧)
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。负责源主机到目的主机数据分组(数据包)(主机到主机)
传输层:定义传输数据的协议端口号,以及流控和差错校验(数据段)。为数据提供一种安全可靠的传输方式(端口到端口)
会话层:建立、管理、终止会话。对话控制(message)
表示层:数据的表示、安全、压缩。处理两个系统间交换信息的语法与语义问题(message)
应用层:网络服务与最终用户的接口。提供用户通过用户代理或网络接口使用网络服务(message)
2、Linux四层网络模型
网络接口层:提供TCP/IP协议的数据结构和实际物理硬件之间的接口
网络层:解决的是计算机到计算机间的通信问题
1、处理来自传输层的分组发送请求,收到请求后将分组装入IP数据报,填充报头,选择路径,然后将数据报发往适当的网络接口。
2、处理数据报。
3、处理网络控制报文协议、即处理路径、流量控制、阻塞等。
传输层:解决的是计算机程序到计算机程序之间的通信问题,它提供两种端到端的通信服务
应用层:网络服务与最终用户的接口。提供用户通过用户代理或网络接口使用网络服务
3、公网的五类ip
A 类: 0.xx.xx.xx~127.xx.xx.xx
B 类: 128.xx.xx.xx~191.xx.xx.xx
C 类:192.xx.xx.xx~223.xx.xx.xx
D 类:224.xx.xx.xx~239.xx.xx.xx(组播地址)
E 类: 240.xx.xx.xx~255.xx.xx.xx(保留未使用的网段)
127.0.0.0到127.255.255.255是保留地址,用做循环测试用的
4、私网的三类ip
前三类公网ip就是私网的三种ip
5、NAT的作用
个人理解:NAT又名网络地址转换,将内网的ip地址转换(映射)成外网的ip地址,可与外界通信。
好处:1、解决ip地址不足的问题 2、隐藏网络内部的计算机ip,能够有效的避免来自网络外部的攻击,有一定的安全防护
弊端:1、影响性能。 NAT会增加交换延迟,因为数据包报头中每个IP地址的转换需要时间。2、具有NAT功能下的主机并没有建立真正的端对端的连接,需要将未更改的数据包从源传输到目标。 由于NAT会更改端到端地址,因此某些使用IP地址的APP应用程序将被阻止。端到端的IP可追溯性也会丢失3、无状态协议可能会中断
6、ip地址和端口号的作用?常见的特殊端口号?
源主机通过ip地址找到目标计算机,通过端口找到应用程序
常见的端口号:20 (FTP数据口) 21(FTP控制) 22 SSH远程登录协议 23 TELNET 80 HTTP
22 SSH 53 DNS 3306 MySQL 6379 Rides 25 Telnet 110 pop3
7、子网掩码的作用?
在IP地址中区分网络ID和主机ID的一个数值,与ip地址一起使用,确定哪些位用于表示网络地址和主机地址
- 划分网络:在计算机网络中,子网掩码用于将 IP 地址分成网络 ID 和主机 ID 两部分,以便实现对不同网络的划分。通过划分不同的网络,可以更有效地管理网络资源,减少网络拥塞和冲突。
- 提高网络安全性:通过使用子网掩码,可以将一个大型网络划分成多个小型网络,从而实现网络的隔离和安全性。在每个子网中,可以设置不同的访问控制策略和安全策略,以保护网络中的敏感数据和资源。
- 优化网络性能:通过合理配置子网掩码,可以减少网络广播和冲突,从而提高网络性能和可靠性。通过划分合适的子网,可以使数据包在网络中更快地传输和到达目的地,提高网络的响应速度和吞吐量。
8、DNS协议的作用?ARP协议的作用?
DNS域名解析技术,将域名解析成IP地址,方便人们不用记忆更方便的访问互联网
ARP将IP地址解析为物理地址,以保证通信的顺利进行
9、TCP协议的作用?
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通讯协议,确保数据的可靠传输,利用重发技术和拥塞控制机制,向应用程序提供可靠的通信连接
10、TCP如何保证可靠有序的传输?
可靠传输需要解决的问题:包丢了、数据被破坏了、包重复、乱序
1)、校验和(数据完整性)
TCP每一段报文都有校验和,保证报文不被破坏或篡改,如果在收到的报文在校验过程中有差错,TCP将丢弃这个报文段和不确认收到此报文段
2)、序列号与确认应答(重复、乱序)
TCP发送的每一个包都有一个序列号,这可以让接收方知道自己已经收到了哪些包,哪些包丢失了,重复的包也可以根据序号丢弃,并且根据序号将包排序,同时每一个发送的包都会返回一个确认应答消息,来确保消息被接受
3)、重传机制(包丢失)
TCP实现可靠传输的方式之一,是通过序列号与确认应答,当发送端的数据到达接受主机时,接受端主机会返回一个确认应答消息,表示已经收到消息,如果包丢失了,可以用重传机制解决
重传机制分为超时重传、快速重传、SACK、DSACK
超时重传 :在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据。
快速重传 :超时重传的问题是要等超时时间后才会重传。快速重传不以时间为驱动,而是以数据驱动重传,服务器如果收到乱序的包,也给客户端回复 ACK,比如收到乱序的包 6,7,8,9 时,服务器全都发 ACK = 5,这样客户端就知道5丢失了,当客户端收到三个相同的 ACK 报文时,会在超时之前,重传丢失的报文段,而不需要等到计时器超时。
4)、滑动窗口
TCP每发送一个数据,就需要等待对方进行ACK确认应答,会影响传输的速率。在发送数据的时候,最好是将所有的数据全部发送出去,然后一起确认,引入窗口概念。滑动窗口并不是固定的,它主要是根据接收方的接收情况,动态去调整窗口大小,然后来控制发送方的数据流量
流量控制:匹配接受方的流量。流量控制是针对接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。即防止发送方的数据填满接收方的缓存区。流量控制由滑动窗口实现,滑动窗口即保证了分组无差错、有序接受、也实现了流量控制。主要的方式是接收方返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送
拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。即防止发送方的数据填满整个网络。在网络出现拥堵时减少数据包的发送,网络恢复后它又会增加数据包的发送,这就是拥塞控制。
拥塞控制的算法(慢启动、拥塞避免算法、拥塞发生算法、快速恢复算法)
11、三次握手、四次挥手的过程
三次握手:
刚开始客户端处于 closed 的状态,服务端处于 listen 状态。然后
1、第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 SN(c)。此时客户端处于 SYN_Send 状态。
2、第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 *SYN_REVD* 的状态。
3、第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
4、服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。
三次握手的作用:
1、确认双方的接受能力、发送能力是否正常。
2、指定自己的初始化序列号,为后面的可靠传送做准备。
关于三次握手的其它问题
1、(ISN)是固定的吗
三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。
如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
2、什么是半连接队列
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
3、三次握手过程中可以携带数据吗
很多人可能会认为三次握手都不能携带数据,其实第三次握手的时候,是可以携带数据的。也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据页没啥毛病。
四次挥手
刚开始双方都处于 establised 状态,假如是客户端先发起关闭请求,则:
1、第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。
2、第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。
3、第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
4、第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态
5、服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
这里特别需要主要的就是*TIME_WAIT*这个状态了,这个是面试的高频考点,就是要理解,为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭。这其中的原因就是,要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态。
12、为什么TCP是四次会挥手
其实是客户端和服务端的两次挥手,也就是客户端和服务端分别释放连接的过程。可以看到,客户端在发送完最后一次确认之后,还要等待至少一个报文来回的时间。主要有两个原因,一个是为了让B端能够按照正常步骤进入CLOSED状态,二是为了防止已经失效的请求连接报文出现在下次连接中。
13、UDP协议的特点及应用场景有哪些?
特点:
面向无连接:在发送数据之前,不需要先建立连接,因此没有连接建立和断开的过程。
不可靠性:UDP 传输的数据并不会进行校验和确认,也不会重复发送,无法保证数据的可靠性。如果某个数据包在传输过程中丢失或损坏,接收方将无法得到这个数据包。
无序性:UDP 协议是无序的,发送的数据可能会经过不同的路径到达目标地址,因此接收方可能无法按照发送顺序对数据进行组装。
简单性:UDP 协议比 TCP 协议更加简单、轻量,因此传输效率更高。
使用场景
弱要求可靠性的实时应用:包括实时音视频、网络游戏等应用,这些应用对于延迟很敏感,而对于数据的可靠性要求并不高。
局域网内的应用:UDP 协议的传输速度较快,适合在局域网内进行数据传输,例如视频会议等应用。
14、TCP和UDP区别

进程线程协程部分+I/O多路复用+线程池部分
关于进程的描述
进程是程序运行的抽象,每个程序运行所占的大小不一样,所需要的的系统资源不一样,抽象成进程的方式统一管理。进程是操作系统分配的最小单位,每个进程都有自己独立的的内存空间,每个进程系统都分配了4个g的独立的内存空间,在linux下进程互不干扰,一个进程的消亡不会影响到其他进程,是一种安全的多任务方式。
进程的状态分为三个状态,就绪态、等待(阻塞)态、执行态,
就绪态:进程分配到了除cpu以外所有必要的资源,只要获得处理器就可以直接执行
执行态:当进程已经获得处理器时,程序在处理器上运行,此时称为执行态
阻塞态:正在执行的进程,由于等待某个时间发生而无法执行时,处于阻塞状态,造成阻塞状态的原因有很多种,例如等待I/O操作完成、等待信号等。
进程围绕着三个态形成了很多策略,先来先服务、时间片轮转、短任务优先、这些调度策略,在用户者的角度,不用去关注调度策略,只需要去创建进程,进程的调度算法由内核决定。
进程的创建方式:fork vfork,
fork创建子进程和父进程相比运行的先后顺序随机,一般情况下父进程先进行,vfork是先运行子进程在运行父进程;
fork创建新进程是父进程的副本,父进程和子进程各自拥有各自的地址空间,互不干扰,vfork创建的新进程和父进程共享地址空间,在子进程执行vfork之后的代码时,它会继续使用父进程的地址空间,直到调用exec函数族中的某个函数或者调用_exit函数为止;
vfork通常比fork更高效,因为它避免了复制整个地址空间的开销。
vfork的使用要求比较严格,子进程在调用vfork后不能修改地址空间中的内容,否则可能会导致不可预期的错误。而fork创建的子进程则不受这样的限制,它可以自由修改自己的地址空间。
system是库函数用于执行shell命令,system会调用fork产生子进程,由子进程调用/bin/sh -c string来执行string代表的命令,执行结束后随即返回原调用的进程
exec是根据文件名找到可执行文件,并用它来取代调用进程的内容,在调用进程内部执行一个可执行文件
进程的退出,异常退出:调用abort函数;收到某个信号使程序终止
正常退出:return,exit函数_exit函数,
Return是函数执行结束返回,把控制权交给调用函数
Exit是一个函数,有参数,执行完把控制权交给系统
Exit和_exit的区别
①、exit()函数是C标准库提供的函数,而_exit()函数是系统调用,直接由内核提供。
②、exit()函数在调用时会执行一系列清理工作,关闭打开的文件流等。而_exit()函数会立即终止进程,不进行任何清理工作。
③、exit()函数会刷新缓冲区,将缓冲区中的数据写入文件。而_exit()函数不会刷新缓冲区,可能会导致输出的数据不完整。
④、exit()函数可以返回一个整数值,作为进程的退出状态码,通常用于表示进程执行的结果。而_exit()函数没有返回值,直接终止进程。
库函数和系统调用的区别:
进程资源的释放,僵尸进程和孤儿进程
僵尸进程:是指子进程已经退出,但其父进程还没有来得及处理它的退出状态信息。在这种情况下,子进程被称为僵尸进程,它虽然不再运行,但仍然占用系统的进程表项和一些系统资源,如果大量的僵尸进程积累,就会导致系统资源耗尽,导致系统崩溃。
孤儿进程:孤儿进程是指父进程已经退出或者异常终止,而子进程仍然在运行的情况。这时候子进程会被称为孤儿进程,它的父进程ID变成1号进程(init),这个进程会接管孤儿进程的后续处理,防止孤儿进程一直运行占用资源。
子进程资源一定要及时回收,怎么解决僵尸进程,引入了进程等待,wait waitpid ,使用wait()或waitpid()系统调用回收子进程资源时,应该将其设置为非阻塞模式,以避免在等待子进程结束时阻塞父进程。
进程之间独立的内存空间(IPC),提供了很多的通信方式
1、信号
信号是一种向进程发送通知,告诉某件事情发生了的一种简单通信机制
信号的处理方式:1、忽略信号,对信号不做任何处理,有两个不能忽略,SIGKILL SIGSTOP
2、捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数
3、执行默认操作
信号的产生:另一个进程发送信号,内核发送信号,底层硬件发送信号
信号发送:kill raise alarm abort
信号处理API: signal 会根据指定的信号编号来设置该信号的处理函数
2、管道
无名管道:通信的进程通过共享这个管道,从而实现通信
管道只允许具有血缘关系的进程间通信,父子、兄弟进程间通信
管道只允许单向通信(可以利用两个无名管实现双向通信)
读管道,如果没有数据的话,读操作会阻塞;写数据,缓冲区写满会休眠
数据被读出,数据就会被管道删除
FIFO不同于管道之处:在于提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中
可以任意两个进程通信,实现双向通信的时候也需要两个有名管道
3、消息队列
消息队列的本质就是由内核创建的用于存放消息的链表,由于是存放消息的,所以我们就把这个链表称为消息队列。
消息队列的使用步骤:使用msgget函数创建消息队列
收发消息
使用msgctl函数,利用消息队列标识符删除消息队列
特点:传送有格式的消息流,多进程网状交叉通信时,消息队列是上上之选,能实现大规模数据的通信
4、共享内存
让同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,
使用:进行调用shmget函数创建新的或获取已有共享内存;进程调用shmat函数,将物理内存映射到自己的进程空间;shmdt函数,取消映射;调用shmctl函数释放开辟的那片物理内存空间
减少进入内核空间的次数,直接使用地址来读写缓存时,效率会更高,适用于大数据量的通信
5、信号量
当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况
信号量实现进程同步:有多少个进程需要同步,我们在集合中就需要创建对应数量的信号量
1、进程:进程是操作系统中独立执行单位,每个进程都有自己独立的的内存空间,
优点:隔离性强:不同的进程之间相互独立,一个进程的崩溃不会影响到其他进程的运行
稳定性高:进程之间分配独立的内存空间,一个进程的错误不会直接影响其他进程
可靠性高:操作系统可以为每个进城分配独立的资源,确保他们不会互相干扰
支持多核:不同的进程可以在不同的处理器核心上并行执行,充分利用多核处理器的性能
缺点:创建销毁代价大:创建和销毁进程的代价相对较大,涉及到资源分配、内存管理等复杂操作。
上下文切换开销大:由于进程切换需要保存和恢复大量的上下文信息,操作系统需要耗费较多的开销。
进程间通信复杂:进程之间的通信需要通过操作系统提供的进程间通信机制,如管道、共享内存、消息队列等,实现起来较为复杂。
2、线程:是进程内的一个执行流,它共享相同的地址空间和其他资源,并使用进程的上下文来进行执行。线程之间的通信更加简单和高效,因为它们共享同一进程的资源
优点:创建销毁代价小:线程的创建和销毁比进程轻量,只需分配一些栈空间和少量的管理结构。
上下文切换开销小:线程之间的上下文切换开销远小于进程的上下文切换,因为它们共享相同的地址空间。
共享资源:线程可以直接访问进程的共享资源,简化了进程间通信的复杂性。
响应速度快:线程的创建和切换速度快,能够更快地响应事件和处理任务。
缺点 :缺乏隔离性:线程之间共享相同的地址空间,一个线程的错误可能会影响到其他线程,造成整个进程崩溃。
安全性问题:由于多个线程共享数据和资源,必须保证线程之间的同步和互斥,否则可能引发数据竞争和不一致的结果。
可靠性较低:一个线程的bug或异常可能会导致整个进程的崩溃,因为它们共享相同的进程资源。
3、协程:它是一种轻量级的线程,不同于传统线程(Thread)和进程(Process)。协程允许程序在执行过程中暂停某个部分,并在需要时恢复执行,这种暂停和恢复的控制由程序员显式地管理,而不是由操作系统或调度器自动控制。(协程是一种强大的并发编程工具,它允许程序以协作的方式管理并发任务,减小了线程和进程模型中的开销,并且特别适用于处理IO密集型任务和异步编程。)
优点:协作性: 协程是协作性的,它们依赖于显式的暂停和恢复操作,因此不需要像线程一样频繁地进行上下文切换,从而减小了开销。
轻量级: 协程通常非常轻量级,因为它们不需要像线程那样分配独立的内存空间,多个协程可以在同一个线程内部运行。
共享状态: 协程可以共享相同的内存空间,这使得数据共享和通信相对容易,但也需要开发者小心处理并发问题。
适用于IO密集型任务: 协程非常适合处理IO密集型任务,如网络通信和文件操作,因为在等待IO完成的时候,协程可以释放CPU,执行其他任务。
编写异步代码: 协程可以用于编写异步代码,例如异步IO和事件驱动的编程模型。它们可以让开发者更容易地编写非阻塞的、高性能的异步代码
关于协程的理解
线程是操作系统抽象出来的执行流,由操作系统统一调度管理,而在一个线程中,抽象出多个多个执行流,由线程统一调度管理,线程之上抽象的执行流就是协程。
线程的调度由操作系统管理,是抢占式调度,而协程不同,协程需要互相配合,主动交出执行权,这也是协程的名字(协作式编程)
C++有一协程框架 叫做libco(未纳入C++标准),通过HOOK关键的系统函数来实现调度器的介入
由于每个进程的用户空间都是独立的,不能相互访问,这时就需要借助内核空间来实现进程间通信,原因很简单,每个进程都是共享一个内核空间。
Linux 内核提供了不少进程间通信的方式,其中最简单的方式就是管道,管道分为「匿名管道」和「命名管道」。
匿名管道顾名思义,它没有名字标识,匿名管道是特殊文件只存在于内存,没有存在于文件系统中,shell 命令中的「|」竖线就是匿名管道,通信的数据是无格式的流并且大小受限,通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能用于存在父子关系的进程间通信,匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。
命名管道突破了匿名管道只能在亲缘关系进程间的通信限制,因为使用命名管道的前提,需要在文件系统创建一个类型为 p 的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信。另外,不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。
消息队列克服了管道通信的数据是无格式的字节流的问题,消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以用户自定义的数据类型,发送数据时,会被分成一个一个独立的消息体,当然接收数据时,也要与发送方发送的消息体的数据类型保持一致,这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的,毕竟每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程。
共享内存可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销,它直接分配一个共享空间,每个进程都可以直接访问,就像访问进程自己的空间一样快捷方便,不需要陷入内核态或者系统调用,大大提高了通信的速度,享有最快的进程间通信方式之名。但是便捷高效的共享内存通信,带来新的问题,多进程竞争同个共享资源会造成数据的错乱。
那么,就需要信号量来保护共享资源,以确保任何时刻只能有一个进程访问共享资源,这种方式就是互斥访问。信号量不仅可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是一个计数器,表示的是资源个数,其值可以通过两个原子操作来控制,分别是 P 操作和 V 操作。
与信号量名字很相似的叫信号,它俩名字虽然相似,但功能一点儿都不一样。信号是异步通信机制,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),一旦有信号发生,进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SIGSTOP,这是为了方便我们能在任何时候结束或停止某个进程。
前面说到的通信机制,都是工作于同一台主机,如果要与不同主机的进程间通信,那么就需要 Socket 通信了。Socket 实际上不仅用于不同的主机进程间通信,还可以用于本地主机进程间通信,可根据创建 Socket 的类型不同,分为三种常见的通信方式,一个是基于 TCP 协议的通信方式,一个是基于 UDP 协议的通信方式,一个是本地进程间通信方式。
以上,就是进程间通信的主要机制了。你可能会问了,那线程通信间的方式呢?
同个进程下的线程之间都是共享进程的资源,只要是共享变量都可以做到线程间通信,比如全局变量,所以对于线程间关注的不是通信方式,而是关注多线程竞争共享资源的问题,信号量也同样可以在线程间实现互斥与同步:
- 互斥的方式,可保证任意时刻只有一个线程访问共享资源;
- 同步的方式,可保证线程 A 应在线程 B 之前执行;
3、I/O多路复用(得加东西)
将主动响应请求和处理请求变为通过“通知”来被动接受请求,然后处理请求(比如一个老师要检查30个同学的作业,原本是老师主动的一个一个检查学生,但是现在学生做好了可以举手让老师检查,从而化主动为被动),这种就是Io复用模型,Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。
面向过程和面向对象的区别?

浙公网安备 33010602011771号