共计 3415 个字符,预计需要花费 9 分钟才能阅读完成。
每日一句
枝上柳绵吹又少。咫尺何处无芳草。
每日一句
You cannot swim for new horizons until you have courage to lose sight of the shore.
除非有勇气来到岸边,否则你永远游不到此岸。
概念
IO 是主存和外部设备(硬盘、终端和网络等)拷贝数据的过程。IO 是操作系统的底层性能实现,底层通过 I / O 指令进行实现。
以下是 5 品种 Unix 下可用的 I / O 模型
1. 阻塞式 I /O:Blocking IO
2. 非阻塞式 I /O:nonblocking IO
3. I/O 复用(Select,poll epoll):IO multiplexing
4. 信号驱动式 I /O(SIGIO):signal driven IO
5. 异步 I/O(posix 的 aio 系列函数):asynchromous IO
Blocking IO
在 Linux 中,默认状况下所有的 socket 都是 Blocking,一个典型的读操作流程大略是这样:
1. 通常波及期待数据从网络达到。当所有期待数据达到时,它被复制到内核中的某个缓冲区
2. 把数据从内核缓冲区复制到应用程序缓冲区
当用户过程调用了 recvfrom 这个零碎调用,kernel 就开始了 IO 的第一个阶段:筹备数据。对于 network IO 来说,很多时候数据在一开始还没有达到(比方,还没有收到一个残缺的 UDP 包)。这个时候 kernel 就要期待足够的数据到来。而在用户过程这边,整个过程会被阻塞。当 kernel 始终等到数据筹备好了,它就会将数据从 kernel 中拷贝到用户内存,而后 kernel 返回后果,用户过程才解除 block 的状态,从新运行起来。
所以,Blocking IO 的特点就是在 IO 执行的两个阶段都被 block 了
非阻塞式 I /O
Linux 下,能够用过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行操作时,流程如下:
从图中能够看出,当用户过程收回 read 操作时,如果 kernel 中的数据还没有筹备好,那么它并不会 block 用户过程,而是立即返回一个 error。从用户过程角度讲,它发动一个 read 操作后,并不需要期待,而是马上就失去了一个后果。用户过程判断后果是一个 error 时,它就晓得数据还没有筹备好,于是它能够再次 发送 read 操作。一旦 kernel 中的数据筹备好了,并且又再次收到了用户过程的 system call,那么它马上就将数据拷贝到了用户内存,而后返回。
所以,用户过程第一个阶段不是阻塞的, 须要一直的被动询问 kernel 数据好了没有;第二个阶段仍然总是阻塞的。
IO 多路复用(NIO)
select、epoll 的益处就在于单个 process 就能够同时解决多个网络链接的 IO。
IO 复用和同步阻塞实质一样,不过利用了新的 Select 零碎调用,由内核来负责原本是申请过程该做的轮训操作,看似不非阻塞 IO 还多了哥零碎调用开销,不过因为反对多路 IO 才算进步了效率
也就是一个能够监听多个。
它的基本原理就是 select、epoll 这个 function 会一直的轮询所负责的所有 socket,当某个 socket 有数据达到了,就告诉用户过程。它的流程如下图:
当用户线程调用 select,那么整个过程会被阻塞,而同时,kernel 会监督所有 select 负责的 socket =,当任何一个 socket 中的数据筹备好了,select 就会返回,这个时候用户过程会调用 read 操作,将数据 kernel 拷贝到用户过程。
首先开启套接字的信号驱动式 IO 性能,并且通过 sigaction(信号处理程序) 零碎调用装置一个信号处理函数,该函数调用将立刻返回,以后过程没有被阻塞,持续工作;当数据报筹备好的时候,内核为该过程产生 SIGIO 的信号,随后既能够在信号处理函数中调用 recvfrom 读取数据报,并且告诉主循环数据曾经筹备好期待解决;也能够间接告诉主循环让它读取数据报;(其实就是一个待读取的告诉和待处理的告诉),根本不会用到。
异步 IO(AIO)
多线程和多过程的模型尽管解决了并发的问题,然而零碎不能有限的减少线程,因为零碎的切换线程的开销恒大,所以,一旦线程数量过多,CPU 的工夫就花在线程的切换上,正真运行代码的工夫就会缩小,后果导致性能重大降落
因为咱们要解决的问题是 CPU 高速执行能力和 IO 设施的龟速重大不匹配,多线程和多过程只是解决这一个问题的一种办法。
另一种解决 IO 问题的办法是异步 IO,当代码须要执行一个耗时的 IO 操作时,他只收回 IO 指令,并不期待 IO 后果而后就去执行其余代码,一段时间后,当 IO 返回后果是,在告诉 CPU 进行解决咱们调用 aio_read 函数,给内核传递描述符,缓冲区指针,缓冲区大小,和文件偏移量,并且通知内核当整个操作实现时如何告诉咱们,该函数调用后,立刻返回,不会被阻塞
另一方面:从 kernel 的角度,当他收到一个 aio_read 之后,首先它立刻返回,所以不会对用户过程产生 block,而后 kernel 会期待数据筹备实现,而后将数据拷贝到用户内存(copy 由内核实现),当着所有实现后,kernel 会给用户过程发送一个 singal 或者执行下一个基于线程回调函数来实现此次 IO 处理过程,通知他 read 操作实现
美文佳句
岁月应是静好。近来,心神总是不宁,想必是被尘世间的引诱所困。
我想,要是远方的祥夫学生来到我这“梅知堂”,两人清茶一杯,盘腿而坐,或看他画梅,纸上的梅花好像兀自开在飞雪里,如是,那该是一幅怎么的墨梅图?
昨晚,梦里看到那个戴着小黑圆眼镜的祥夫背着黄色的大包正往江南赶路。是又到了梅花开的日子了吗?
面试题
HJ2 计算某字符呈现次数
题目链接
https://www.nowcoder.com/practice/a35ce98431874e3a820dbe4b2d0508b1
题目形容
写出一个程序,承受一个由字母、数字和空格组成的字符串,和一个字符,而后输入输出字符串中该字符的呈现次数。(不辨别大小写字母)
数据范畴:1 \le n \le 1000 \1≤n≤1000
示例
示例 1:
输出:ABCabc
A
输入:2
题解
题解一:模仿算法
import java.util.*;
public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);
String s = sc.nextLine().toLowerCase();
String inx = sc.nextLine().toLowerCase();
int cnt = 0;
for (int i = 0; i < s.length(); i++) {if (s.charAt(i) == inx.charAt(0)) {cnt ++;}
}
System.out.println(cnt);
}
}
redis 怎么保留一个对象?
1。将对象序列化后保留到 redis
2。将对象转化成 JSON 字符串后存储
3。将对象用 Hash 数据类型存储
参考:https://blog.csdn.net/qq_26545503/article/details/106123676
【概念】如何了解 Java 中的偏心锁和非偏心锁?
偏心锁:
多个线程依照申请锁的程序去取得锁,所有线程都在队列里排队,这样就保障了队列中的第一个先失去锁。
- 长处:所有的线程都能失去资源,不会饿死在队列中。
- 毛病:吞吐量会降落很多,队列外面除了第一个线程,其余的线程都会阻塞,cpu 唤醒阻塞线程的开销会很大。
非偏心锁
多个线程不依照申请锁的程序去取得锁,而是同时间接去尝试获取锁(插队),获取不到(插队失败),再进入队列期待(失败则乖乖排队),如果能获取到(插队胜利),就间接获取到锁。
- 长处:能够缩小 CPU 唤醒线程的开销,整体的吞吐效率会高点,CPU 也不用取唤醒所有线程,会缩小唤起线程的数量。
- 毛病:可能导致队列中排队的线程始终获取不到锁或者长时间获取不到锁,活活饿死。
利用场景
ReentrantLock 能够指定构造函数的 boolean 类型来创立偏心锁和非偏心锁(默认), 比方:偏心锁能够应用 new ReentrantLock(true) 实现。
你好,我是 yltrcc,日常分享技术点滴,欢送关注我:ylcoder