博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NIO的selector.wakeup的实现
阅读量:3730 次
发布时间:2019-05-22

本文共 2935 字,大约阅读时间需要 9 分钟。

更多干货

 NIO中的Selector封装了底层的系统调用,其中wakeup用于唤醒阻塞在select方法上的线程,它的实现很简单,在linux上就是创建一 个管道并加入poll的fd集合,wakeup就是往管道里写一个字节,那么阻塞的poll方法有数据可读就立即返回。证明这一点很简单,strace即 可知道:

 

Java代码  
  1. public class SelectorTest {  
  2.     public static void main(String[] args) throws Exception {  
  3.         Selector selector = Selector.open();  
  4.         selector.wakeup();  
  5.     }  
  6. }  
 

     使用strace调用,只关心write的系统调用

 

Java代码  
  1. sudo strace -f -e write java SelectorTest  

 

     输出:

 

Java代码  
  1. Process 29181 attached  
  2. Process 29182 attached  
  3. Process 29183 attached  
  4. Process 29184 attached  
  5. Process 29185 attached  
  6. Process 29186 attached  
  7. Process 29187 attached  
  8. Process 29188 attached  
  9. Process 29189 attached  
  10. Process 29190 attached  
  11. Process 29191 attached  
  12. [pid 29181] write(36"\1"1)          = 1  
  13. Process 29191 detached  
  14. Process 29184 detached  
  15. Process 29181 detached  
 

    有的同学说了,怎么证明这个write是wakeup方法调用的,而不是其他方法呢,这个很好证明,我们多调用几次:

 

Java代码  
  1. public class SelectorTest {  
  2.     public static void main(String[] args) throws Exception {  
  3.         Selector selector = Selector.open();  
  4.         selector.wakeup();  
  5.         selector.selectNow();  
  6.         selector.wakeup();  
  7.         selector.selectNow();  
  8.         selector.wakeup();  
  9.     }  
  10. }  
 

    修改程序调用三次wakeup,心细的朋友肯定注意到我们还调用了两次selectNow,这是因为在两次成功的select方法之间调用wakeup多 次都只算做一次,为了显示3次write,这里就每次调用前select一下将前一次写入的字节读到,同样执行上面的strace调用,输出:

 

Java代码  
  1. Process 29303 attached  
  2. Process 29304 attached  
  3. Process 29305 attached  
  4. Process 29306 attached  
  5. Process 29307 attached  
  6. Process 29308 attached  
  7. Process 29309 attached  
  8. Process 29310 attached  
  9. Process 29311 attached  
  10. Process 29312 attached  
  11. Process 29313 attached  
  12. [pid 29303] write(36"\1"1)          = 1  
  13. [pid 29303] write(36"\1"1)          = 1  
  14. [pid 29303] write(36"\1"1)          = 1  
  15. Process 29313 detached  
  16. Process 29309 detached  
  17. Process 29306 detached  
  18. Process 29303 detached  
 

     果然是3次write的系统调用,都是写入一个字节,如果我们去掉selectNow,那么三次wakeup还是等于一次:

Java代码  
  1. public class SelectorTest {  
  2.     public static void main(String[] args) throws Exception {  
  3.         Selector selector = Selector.open();  
  4.         selector.wakeup();  
  5.         selector.wakeup();  
  6.         selector.wakeup();  
  7.     }  
  8. }  
 

  

   输出:

 

Java代码  
  1. Process 29331 attached  
  2. Process 29332 attached  
  3. Process 29333 attached  
  4. Process 29334 attached  
  5. Process 29335 attached  
  6. Process 29336 attached  
  7. Process 29337 attached  
  8. Process 29338 attached  
  9. Process 29339 attached  
  10. Process 29340 attached  
  11. Process 29341 attached  
  12. [pid 29331] write(36"\1"1)          = 1  
  13. Process 29341 detached  
  14. Process 29337 detached  
  15. Process 29334 detached  
  16. Process 29331 detached  
 

      wakeup方法的API说明没有欺骗我们。wakeup方法的API还告诉我们,如果当前Selector没有阻塞在select方法上,那么本次 wakeup调用会在下一次select阻塞的时候生效,这个道理很简单,wakeup方法写入一个字节,下次poll等待的时候立即发现可读并返回,因 此不会阻塞。
     具体到源码级别,在linux平台上的wakeup方法其实调用了pipe创建了管道,wakeup调用了的interrupt方法:

Java代码  
  1. public  void interrupt()   
  2.   
  3. {  
  4.         interrupt(outgoingInterruptFD);  
  5. }  
 

    实际调用的是interrupt(fd)的native方法,查看EPollArrayWrapper.c可见清晰的write系统调用:

Java代码  
  1. JNIEXPORT void JNICALL  
  2. Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd)  
  3. {  
  4.     int fakebuf[1];  
  5.     fakebuf[0] = 1;  
  6.     if (write(fd, fakebuf, 1) < 0) {  
  7.         JNU_ThrowIOExceptionWithLastError(env,"write to interrupt fd failed");  
  8.     }  
  9. }  
 

    写入一个字节的fakebuf。有朋友问起这个问题,写个注记在此。strace充分利用对了解这些细节很有帮助。

转载地址:http://hdlnn.baihongyu.com/

你可能感兴趣的文章
案例:使用正则表达式的爬虫
查看>>
os模块:文件所在目录位置
查看>>
操作文件时报错:zipfile.BadZipFile: File is not a zip file
查看>>
ubuntu基础常用命令(1)
查看>>
Ubuntu基础常用命令(03)——关机重启、vi 完~
查看>>
输入\数据转换类型\运算符\判断语句
查看>>
Linux 命令大全.速速收藏,不足欢迎补充
查看>>
多任务(进程线程)
查看>>
flask连接mysql-pycharm版
查看>>
nginx优化配置
查看>>
超简单nginx配置从入门到入土
查看>>
gunicorn
查看>>
raid
查看>>
lvm
查看>>
linux开机启动流程
查看>>
搭建nfs服务器
查看>>
centos7 最简单安装zabbix4.4.6的方法
查看>>
编译和解释的区别
查看>>
搭建lvs集群NAT模式
查看>>
使用Zabbix4.4自定义模板监控 MySQL 性能
查看>>