一、系统调用
- 解释下系统调用:系统调用,就是操作系统内核提供给用户进程的接口。
- 系统调用的目的:一些敏感的操作,让内核去完成,比如IO操作。用户进程是没有权限去执行的。操作系统内核具有最高的权限,可以使用计算机的所有资源。用户进程想要执行敏感操作,需要借助内核去完成。
- 用户态和内核态概念:多数计算机有两种运行状态,用户态和内核态。
操作系统内核运行在内核态,而其他进程运行在用户态。内核态模式下,操作系统具有对于硬件的完全访问权限。用户态进程想要执行敏感操作时,可通过系统调用,陷入到内核态,即将数据传入内核态,让内核去执行,然后内核将执行结果在返回给用户进程。 - 为什么说并发会谈到系统调用呢?因为从用户态陷入到内核态会导致数据的拷贝,我们设计一个高并发的程序,就也应该把这些因素考虑在内。
- 你能解释下:观看主播时,你听到主播的声音,经历了多少次数据的拷贝吗?首先音频数据包被你的电脑的网卡接收,然后将数据拷贝到内核中,再从内核中拷贝到tcp缓冲区中,在从tcp缓冲区中拷贝到应用程序中,假设应用程序里没有发生数据拷贝,应用程序解析完还要拷贝到内核中,然后内核在拷贝给声卡。
二、高级IO
1.IO多路复用
IO多路复用可以监听多个文件描述符,先构造一张我们感兴趣的文件描述符表,然后调用一个函数(select、poll、epoll)直到文件描述符表中至少有一个准备好进行IO时,该函数才返回。
select、poll、epoll都实现了IO多路复用,之所以有这三个鬼,是因为他们的出现时间是又先后顺序的。
这篇文章解释的很生动,易懂,强推!!。IO 多路复用是什么意思? - 罗志宇的回答 - 知乎
https://www.zhihu.com/question/32163005/answer/55772739
- select
缺点:
1.数据拷贝开销大,每次select的执行,用户进程和内核都会改变文件描述符表,然后拷贝给对方。
2.可以监听的文件描述符的数量有限,1024个。
3.当内核监听到有文件描述符可以进行IO了,她不会直接告诉用户进程是哪个文件描述符,需要用户进程自己去遍历。几十几百个连接还好,几十万的连接遍历起来,那真是酸爽的不要不要的。。。。 - poll
poll解决了select文件描述符数量的限制,但他还是需要遍历,时间复杂度很高。而且每次调用都要进行内核和用户进程间的数据的拷贝。 - epoll
epoll是基于poll的改进。
优点:
1.首先采用mmap,将用户进程的一段内存映射到内核的一段内存上,从而避免了数据的拷贝操作。
2 .当有文件描述符可以进行IO时,他会直接告诉进程是哪个文件描述符,在也不需要遍历了。。
3 .epoll的设计采用红黑树,红黑树很高效这是毋庸置疑的吧,所以epoll也高效,这么解释没问题吧,哈哈
2.非阻塞IO(NIO)
非阻塞IO可以实现一个线程同时处理多个sock连接,首先他read下sock1,有数据直接读出来,没有数据直接返回,然后read下sock2,有数据直接读出来,没有数据直接返回,以此类推。所以,不难想到,非阻塞IO应该与轮询机制一起使用。很多时候其实都没有数据到达,服务器却一直在轮询,这太浪费cpu资源了。
3.异步IO(AIO)
利用异步IO技术,进程告诉内核:当文件描述符read好可以进行IO时,用一个信号通知它。
缺点:
1.信号的数量有限,远远小于可用的文件描述符的数量,所以没法用一个信号对应一个文件描述符,还得遍历,看哪个文件描述符能进行IO操作。
2.可移植性差,一些系统提供的异步IO接口不一致
三、并发和并行
1.并发是指一段时间内处理多个事件。
2.并行是指同一时刻处理多个事件。
问题:应用程序设置多少个线程合适啊?
我们在进行并发设计的时候,要充分利用并行的特性。并行是依靠物理层的处理器。如果线程数量过多,就会造成一个处理器上频繁的线程切换,处理速度反而变慢了。如果线程数量太少,我们就没能充分的发挥出多处理器的性能。
CPU密集型计算任务:IO操作少,大部分操作都需要利用cpu的任务。
I/O密集型计算任务:IO操作多的任务。
由于在IO的时候,线程不占用cpu资源,可让出cpu,让其他线程工作。
线程多少合适?
对于CPU密集型计算任务,线程数 = CPU核心数 + 1;
对于I/O密集型计算任务,线程数 = 2 * CPU核心数;
对于普通任务,线程数 = N(CPU核心数) * (1 + WT(线程等待时间) / ST(线程时间运行时间))。