2022-03-13
niopublic void aaa(){ //堆 ByteBuffer.allocate(); //直接内存,创建慢,读写快,因为如果放在直接内存里,那只需要从用户的进程缓存往内核的套接字缓存区copy,但是如果在堆里,还要先从堆copy到用户的进程缓存区 为什么要多一次堆到 进程缓存(直接内存)的 copy呢,堆直接到内核的套接字缓存有什么问题? 因为gc垃圾回收,会移动堆内存,而在copy到内核套接字缓存的时候,不能有任何内存变动 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(666); SocketChannel socketChannel; //表明看他是read,其实是从channel里read写往bytebuffer socketChannel.read(byteBuffer); //从bytebuffer里读取数据写到channel里 socketChannel.write(byteBuffer) //如果我自己要往bytebuffer里写分俩中 byteBuffer.put(xxx) //自己移动当前position byteBuffer.put(index,xxx) //指定插入index, 需要手动移动position,最好别用 //读的时候一定要掉用flip方法,要不position位置不变,不是从0开始的 byteBuffer.flip(); }写事件什么注册
每个socketchannel都有个读写换冲突,当读里面有数据了selector就会通知我们有读事件
当写数据缓存里有剩余,就会写事件通知我们可以写
这样就导致,如果写的东西不大或者写完了,一直会提示我们有写事件
所以应该是写大东西的时候,缓冲区大小比如4k,不够了,先注册写事件,写一部分,等缓存空闲内存到了多少,再继续写,写完要取消写事件
keep-alive 和多路复用的泣别
对于一个链接而言 keep-alive是串行的
多路复用是并行的
select poll epoll的区别
select最大支持1024个链接
poll 基本和select相同,不过他的最大链接数用的链表而不是数组
epoll 基本只受内存限制,同时当链接很多的时候,select和poll的性能急剧下降,而epoll 只受活跃链接影响,猜想前面是轮询事件获取数据,后面是主动通知数据过来,因此只受处理数据事件的影响
水平检测和边缘检测
水平(level)检测lt ,来了数据通知你,没读完一直通知你,select poll epoll
边缘(Edge)检测et ,来了新数据通知你,读没读完不管epoll(默认也是开启水平喔)
0拷贝
参考一次读文件进行网络发送举例
普通拷贝
1,用户下达指令,切换到内核, 切换1次,
2,dma 从磁盘拷贝数据到内核文件缓冲区,dma拷贝,拷贝1次
3,内核文件缓冲区拷贝应用程序缓冲区,cpu拷贝,拷贝2次
5,切换到用户太,切换2次
6,用户执行send方法,切换到内核态,切换3次
7,将数据从应用程序缓冲区读到内核套接字缓冲区,cpu拷贝,拷贝3次
8,将数据从套接字缓冲区拷贝到网络 ,dma拷贝,拷贝4次
9,结束回到用户态 切换4次
即,用户态到内核态的拷贝为cpu拷贝,内核到磁盘网卡为dma拷贝
mmap
对比普通拷贝
2,3直接进行一次那映射,通过dma拷贝,直接讲数据映射到应用程序缓冲区,其他不变
因此为减少了一次拷贝,切换次数不变
sendfile
宗旨是从减少上下文切换考虑,想一下如果文件数据到套接字那边直接在内核层完成,不用用户态转一道会不会更好呢
1,用户下达指令 ,进入内核态,切换1次
2,内核将磁盘数据拷贝到内核文件读取缓冲区,dma 拷贝,拷贝1次
3,内核将内核文件缓冲区的数据给套接字发送缓冲区,cpu拷贝,拷贝2次
5,套接字缓冲区将数据给网扣,dma拷贝,拷贝3次
6,回到用户态,切换2次
发现了没,内存之间拷贝用cpu,内存和其他硬件之间的拷贝用dma
sendfile升级版本
在硬件支持的情况下,我们将上面的第3步,改为只提供文件读取缓冲期的地址和长度
直接从文件缓冲期里拷贝数据到网络,这就是实现了2次拷贝,2次上下文切换
slice 如果硬件不支持怎么办
linux系统提供管道pipe。让磁盘文件读取缓存区和套接字发送缓冲区相连,感觉有点共享内存的意思响应器 模式和观察模式的区别
如selector 他是根据对应事件分发给不通的对象,观察者模式是统一分发一样的数据
tcp拆包粘包
为什么拆包
因为链路层限制数据发送大小,所以上层会进行拆包发送
为什么粘包
nagle算法吧几次消息合到一起发送,减少应答次数,
怎么确定数据的完整性
1,头+内容体。头表明总共这条信息有多长
2,固定长度消息
3,特殊字符分割。这个主要用于文本,如ftp,进行换行符分割
序列化
考察一个序列化,需要观察这个几点
1,性能
2,体积
3,是否跨语言
Serializable 相对来说体积较大,转换性能较差,不能跨语言
xml体积>gson 体积,但是可以跨语言 ,xml解析起来应该更快
protobuf 同样可以跨语言。可能就是可读性差点
突然想起mmkv保存的方式,好像是总长度下一条信息长度下一条信息内容 下一条信息长度下一条信息内容下一条信息长度下一条信息内容....
同步 ,异步,阻塞,非阻塞
理解同步和阻塞的区别,
同步指的是结果是主动获取
异步指的是结果是回调或者信号回来
阻塞 在获取结果前,不能干别的事情
非阻塞,在获取结果前可以干别的事情
同步阻塞,我去买衣服,没有买到一直等。bio
同步非阻塞,我一直循环去拿比如
异步阻塞,我给你个电话,来衣服了告诉我,然后我守着电话,不去干别的事情,一直到有衣服,基本没人这么干
异步非阻塞,我给你个电话,来了衣服告诉,然后我去干别的事情,aio
多路复用在检查 和 具体某个channel读写的时候,都是阻塞的
epoll
select poll受限自己的数据结构,每次都要重新遍历所有等待队列才能知道哪个有数据
eventpoll包含俩个数据结构,一个是用来装载等待的socket,因为要容易查询,增加,删除所以用的红黑树,另一个用的一个双向链表来装载有数据过来的socket,叫rdlist
有消息是怎么知道的呢
来消息了,网卡会给cpu发中断信号,cpu处理完以后会走到协议层,解析完以后会吧数据给上层,怎么知道是哪个数据对于哪个 socket呢,通过端口号
select poll epoll wait的时候都会释放cpu资源
public boolean viewgroup_dispatchTouchEvent(MotionEvent ev) { boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { // Check for interception. final boolean intercepted; //只要走down 事件,或者有在down 事件里返回true的,即mFirstTouchTarget != null //简单理解,如果之前down事件是我消费的,子view没有消费,那我还拦截个锤子 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { //requestDisallowInterceptTouchEvent(true) 通过这个禁止父view拦截事件 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { intercepted = true; } if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { //这里是返回true的子view if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { //结束循环,并吧child 设置为mFirstTouchTarget newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } } } // Dispatch to touch targets. if (mFirstTouchTarget == null) { //这个方法子view传的是空,意思是如果没有子view处理了down的话,里面会直接走super.dispatchTouchEvent() ,即走viewgroup自己的touch方法 handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { //注意这里,如果之前有target了,其他的就不会走了,直接走之前的targets if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } } predecessor = target; target = next; } } } return handled; } public boolean view_dispatchTouchEvent(MotionEvent event) { boolean result = false; if (actionMasked == MotionEvent.ACTION_DOWN) { //停止滑动 stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; //先看 mOnTouchListener.onTouch 的结果 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } //都没有或者返回false if (!result && onTouchEvent(event)) { result = true; } } return result; }什么时候触发down事件
1,当vp接到down事件的时候会,会触发firstTouchTarget down事件
if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); }
2,vp接到cancel事件
3,view从vp移除、
触摸事件的消费过程是怎么样的,从前面代码可以看出
如果之前子view没有处理down,即返回不是为 true,那么他后面就不会接到其他事件了
锁
共享锁
可以参考 ReentrantReadWriteLock
原理是如果我是拿共享锁,我先判断我前一个是不是共享锁,是的话我也拿到啦,如果我前面一个是独占所,或者我本身作为独占锁,就拿不到
Condition
一个锁里可以多个Condition ,比如生成消费模型,用来暂停生产或者暂停消费,这样可以做到一个锁定点暂停,因为sync 指的是一个对象,你释放锁,可能还是自己抢到,condion可以保证是生产者或者消费者抢到
怎么做到呢
其实condition 有自己的队列
一但await, 就从执行队列移出来,放在自己队列里
一个sigal,就从自己队列移出来,放在等待队列里,注意是一个个移出来一个个执行
之前一直很好奇,为什么 CyclicBarrier 能同时释放x个线程向前跑,按道理就算他执行condition.signalAll被等待队列也是应该一个个执行才对呀,为什么会同时执行,
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException { final ReentrantLock lock = this.lock; lock.lock(); try { final Generation g = generation; int index = --count; if (index == 0) {// tripped boolean ranAction = false; try { final Runnable command = barrierCommand; if (command != null) command.run(); ranAction = true; //这里sigaall,并开始了一个新的generation nextGeneration(); return 0; } finally { if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out for (;;) { try { if (!timed) trip.await(); else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { if (g == generation && ! g.broken) { breakBarrier(); throw ie; } else { // We're about to finish waiting even if we had not // been interrupted, so this interrupt is deemed to // "belong" to subsequent execution. Thread.currentThread().interrupt(); } } if (g.broken) throw new BrokenBarrierException(); //这里进行了循环中断返回 if (g != generation) return index; if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { lock.unlock(); } }
其实原因很简单,我们只lock了一段,就算一个个执行,也是立即返回了index,很快就走完了,剩下真正的执行的内容,其实没有再lock里,lock这里只是阻塞,返回int就没了
页:
[1]