莉凡网

linux多线程编程(linux多线程编程实验)

放牛AI工具

本文转载自 Linux内核那些事

来源:xusong.lie

  链接:https://mp.weixin.qq.com/s/hF8_kBOqiQRzq0zM9QwqmQ

  当对一个socket进行read/write操作时,如果socket还没有就绪(也就是说不可读或写),这时会阻塞在read/write操作,从而导致程序停止服务。这时可以使用多路复用IO来解决这个问题。

  什么是“多路复用IO”呢?

  “多路复用IO”是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程可以进行IO操作。多路复用IO技术有很多种,例如:epoll、kqueue、poll和select等。下文将使用select来进行解说,这是因为select是多路复用IO中最简单的实现,而且与其他实现原理相似。

  select的使用方法如下图:

  

  程序会阻塞在select()系统调用,而select()会监听所有被添加到fd_set中的文件句柄,如果其中有文件可以进行读写操作(或者select调用超时),此时便会从select()调用返回,继续执行select()调用之后的代码(对文件进行读写操作)。

linux多线程编程(linux多线程编程实验)

  所以select相当于一个消息通知,当监听的文件句柄可以读写时会通知进程可以进行读写操作。那么就不会出现进行阻塞在某个文件读写操作中。

  下面说说select系统调用在内核中的实现(下文使用Linux1.0版本进行分析,原因是Linux1.0的实现比较直观和简单)。

  当在程序中调用select()时,进程会进入到内核态,此时会调用内核的sys_select()函数。sys_select()源码如下:

  

  sys_select()先会复制用户空间的参数到内核空间,然后会调用do_select()函数来做接下来的操作。do_select()函数源码如下:

  

  do_select()函数的主要工作是循环所有监听的文件句柄,然后调用check()函数来检查其是否就绪,然后就绪check()函数会返回1,否则返回0并添加到等待队列中。如果其中有文件句柄就绪,那么do_select()直接返回,否则会调用schedule()函数进行进程调度(让其他进行运行)。当从schedule()返回后,再次对所有监听的文件句柄进行检查。

  下面是check()函数的源码:

linux多线程编程(linux多线程编程实验)

  

  对于check()函数,有一点要说明的是fops这个结构,这个结构是文件句柄对应的一些操作回调(例如读写回调),对应socket来说就是socket_file_ops。

  socket_file_ops的定义如下:

  

  所以可以看到,check()函数调用的select其实就是sock_select()函数。对于网络型的socket来说,sock_select()最终会调用inet_select()函数。而对于TCP类型的socket来说,inet_select()又会调用tcp_select()函数。所以调用链为:

  sys_select()

  _do_select()

  _sock_select()

  _ inet_select()

  _ tcp_select()

  所以我们最终还是要来分析tcp_select()这个函数:

  

  tcp_select()函数会把当前进程添加到socket对应的等待队列中,然后查询socket是否就绪(tcp_readable()函数)或者发生错误。如果是就返回1,否则返回0。

  分析到这里,我们已经知道select()系统调用的基本流程,那么什么时候select()调用会返回呢?当socket的状态发生变化(变为可读或者可写),会调用sock结构体的state_change ()回调函数,而state_change ()回调函数指向的是def_callback1(),代码如下:

  

  从代码中可以看到,这个函数就是唤醒阻塞在socket的进程。此时就会从select()调用中返回。

放牛AI工具

本文链接:https://www.hello-linux.com/linux/718.html

版权声明:本网站内容均来源于网络,如涉及侵权,请联系作者!

发表评论

还没有评论,快来说点什么吧~

联系客服
公众号
公众号
公众号
返回顶部