NIO: Linux/IO fundamentals

Core conclusions:


1. Different JDK versions have different implementations


2. If you do not set the socket to nonblocking, accept will block until data arrives


The call to poll blocks until the registered event has occurred and returns the fd of the event that occurred



Environment to prepare

centOS 7

Jdk1.5.0 – jdk1.8.0

strace

The test code

BIOServer.java

import java.io.IOException;
import java.net.ServerSocket;

public class BIOServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        while (true) {
            server.accept();
            System.out.println("= = = = = = = = = = ="); }}}Copy the code

Test steps

1. After compiling bioserver. Java, start the command line and listen on port 8080

2. Simulate Telnet localhost 8080 on the client and disconnect immediately after connection

Strace listens for function calls from Java processes

Java5

The strace call stack

// Enable fd 3188 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3 3189 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 // Bind port 8080 to FD3, return success 3190bind(3, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("0.0.0.0"}, 16) = 0 // listen fd3 3191 listen(3, 50) = 0 3197 getTimeofday ({tv_sec=1588789268, tv_usec=224692}, NULL) = 0 3198 gettimeofday({tv_sec=1588789268, tv_usec=224993}, NULL) = 0 3199 gettimeofday({tv_sec=1588789268, Tv_usec =225263}, NULL) = 0 Accept (3, {sa_family=AF_INET, sin_port=htons(40862), sin_addr=inet_addr("127.0.0.1")}, [16]) = 5
3199 gettimeofday({tv_sec=1588789268, tv_usec=225263}, NULL) = 0
3201 gettimeofday({tv_sec=1588789270, tv_usec=619848}, NULL) = 0
3208 gettimeofday({tv_sec=1588789270, tv_usec=623749}, NULL) = 0
3209 write(1, "= = = = = = = = = = =", 11)             = 11
3210 write(1, "\n", 1)                       = 1
3211 accept(3, 0xffe9a4ec, [16])             = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
3212 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
3213 futex(0xf79429c0, FUTEX_WAKE_PRIVATE, 1) = 1
3214 rt_sigreturn({mask=[QUIT]})             = 102
3215 accept(3,  <unfinished ...>)            = ?Copy the code


View man Manual

If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until  a
 connection is present.  If the socket is marked nonblocking and no pending connections are present on the queue, accept() fails with
 the error EAGAIN or EWOULDBLOCK.Copy the code

If nonblocking is not set to the socket, accept will block until a link appears

conclusion

Bio in java5 is implemented through accept blocking


Java6

The strace call stack

// Open the socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 6 13615 FCNTL (6, F_GETFL) = 0x2 (flags O_RDWR) 13616 fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK) = 0 13617 setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 13618 gettimeofday({tv_sec=1588790897, tv_usec=258322}, NULL) = 0 13619 gettimeofday({tv_sec=1588790897, tv_usec=277413}, NULL) = 0 13620 gettimeofday({tv_sec=1588790897, Tv_usec =277603}, NULL) = 0 // bind to FD6 8080 13621bind(6, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("0.0.0.0"}, 16) = 0 // listen(6, 50) = 0 getTimeofday ({tv_sec=1588790897, tv_usec=278363}, Gettimeofday ({tv_sec=1588790897, tv_usEC =287641}, NULL) = 0 Returns a POLLIN fd 13625 poll ([{fd = 6, events = POLLIN | POLLERR}], 1, 1) = 1 ([{fd = 6, 13626 Accept (6, {sa_family=AF_INET, sin_port=htons(40868), sin_addr=inet_addr("127.0.0.1")}, [16]) = 8
13627 fcntl(8, F_GETFL)                       = 0x2 (flags O_RDWR)
13628 fcntl(8, F_SETFL, O_RDWR)               = 0
13629 gettimeofday({tv_sec=1588790899, tv_usec=835776}, NULL) = 0
13630 gettimeofday({tv_sec=1588790899, tv_usec=837031}, NULL) = 0
13631 gettimeofday({tv_sec=1588790899, tv_usec=837294}, NULL) = 0
13632 gettimeofday({tv_sec=1588790899, tv_usec=837659}, NULL) = 0
13633 gettimeofday({tv_sec=1588790899, tv_usec=838010}, NULL) = 0
13634 write(1, "= = = = = = = = = = =", 11)             = 11
13635 write(1, "\n", 1) = 1 / after/read the data, fd, 6 again into the poll data waiting in the 13636 poll ([{fd = 6, events = POLLIN | POLLERR}], 1, 1 < unfinished... = >)? 13637 +++ exited with 130 +++Copy the code

PollfdArray [0] pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray pollfdArray

Calling poll blocks the thread, and when a client connects, poll returns an integer representing how many sockets in the array received data. In the case of the first connection, the return value is 1.

PollfdArray [0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray[0] : pollfdArray

Take a look at the definition of poll in the MAN manual:

NAME
       poll, ppoll - wait for some event on a file descriptor

SYNOPSIS
       #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);
DESCRIPTION
      poll() performs a similar task to select(2): it waits for one of a setOf file descriptors to become ready to perform I/O. ………… ..................... ..................... If none of the events requested (and no error) has occurredfor any of the file descriptors, then poll() blocks  until  one  of  the
       events occurs.

       The  timeout argument specifies the minimum number of milliseconds that poll() will block.  (This interval will be rounded up to the
       system clock granularity, and kernel scheduling delays mean that the blocking interval may overrun by a small amount.)  Specifying a
       negative  value  in timeout means an infinite timeout.  Specifying a timeout of zero causes poll() to return immediately, even if no
       file descriptors are ready.Copy the code



The man manual can be concluded as follows:

Poll is a similar method to select

2. When no event occurs, poll blocks until an event occurs

3. The timeout parameter specifies that poll blocks for a specified number of milliseconds, specifying a negative number to indicate an infinite timeout



validation

Suppose that after the server is started, no client has established a connection and the system call should block on the poll method

[root@f00e68119764 tmp]# tail -f out.3257
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=95678478}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=95961778}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=96243778}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=96531678}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=96818478}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=97112578}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=97404578}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=97692278}) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=49698, tv_nsec=98007178}) = 0
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1Copy the code

When a client establishes a connection with 8080, the log is rolled and an Accept call is displayed

  18228 accept(5, {sa_family=AF_INET, sin_port=htons(40884), sin_addr=inet_addr("127.0.0.1")}, [16]) = 6
  18229 fcntl(6, F_GETFL)                       = 0x2 (flags O_RDWR)
  18230 fcntl(6, F_SETFL, O_RDWR)               = 0
  18231 clock_gettime(CLOCK_MONOTONIC, {tv_sec=50008, tv_nsec=62405578}) = 0
  18232 clock_gettime(CLOCK_MONOTONIC, {tv_sec=50008, tv_nsec=62713278}) = 0
  18252 clock_gettime(CLOCK_MONOTONIC, {tv_sec=50008, tv_nsec=67718778}) = 0
  18253 write(1, "= = = = = = = = = = =", 11)             = 11
  18254 clock_gettime(CLOCK_MONOTONIC, {tv_sec=50008, tv_nsec=68673978}) = 0
  18255 write(1, "\n", 1)                       = 1
  18256 poll([{fd=5, events=POLLIN|POLLERR}], 1, -1Copy the code

The log stays with the poll method to verify that the guess is correct

conclusion

1. In JDK6, the BIO is implemented by poll and accept

2. The poll method blocks

Java7/8

The strace call stack

18774 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 6
18775 fcntl(6, F_GETFL)                       = 0x2 (flags O_RDWR)
18776 fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
18777 setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
18778 gettimeofday({tv_sec=1588793691, tv_usec=279644}, NULL) = 0
18779 gettimeofday({tv_sec=1588793691, tv_usec=279906}, NULL) = 0
18780 gettimeofday({tv_sec=1588793691, tv_usec=280172}, NULL) = 0
18781 bind(6, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
18782 listen(6, 50)                           = 0
18783 gettimeofday({tv_sec=1588793691, tv_usec=281027}, NULL) = 0
18784 gettimeofday({tv_sec=1588793691, tv_usec=281295}, NULL) = 0
18785 gettimeofday({tv_sec=1588793691, tv_usec=291874}, NULL) = 0
18786 poll([{fd=6, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=6, revents=POLLIN}])
18787 accept(6, {sa_family=AF_INET, sin_port=htons(40874), sin_addr=inet_addr("127.0.0.1")}, [16]) = 7
18788 fcntl(7, F_GETFL)                       = 0x2 (flags O_RDWR)
18789 fcntl(7, F_SETFL, O_RDWR)               = 0
18790 gettimeofday({tv_sec=1588793691, tv_usec=912271}, NULL) = 0
18791 gettimeofday({tv_sec=1588793691, tv_usec=912551}, NULL) = 0
18792 write(1, "= = = = = = = = = = =", 11)             = 11
18793 gettimeofday({tv_sec=1588793691, tv_usec=913462}, NULL) = 0
18794 write(1, "\n", 1)                       = 1
18795 poll([{fd=6, events=POLLIN|POLLERR}], 1, -1 <unfinished ...>) = ?Copy the code

You can see that jdK7 and JDK8 are implemented in the same way as in JDK6

The memo

  • Different JDK versions have different implementations

  • If nonblocking is not set to the socket, accept blocks until data arrives

  • Calls to poll are blocked until the registered event has occurred, and the FD of the event that occurred is returned


A series of

NIO: Linux/IO fundamentals

NIO sees and says (2) – The two BIO in Java

NIO sees and says (3) — different IO models

NIO: Java NIO

NIO also said that (v) : Do it, do it today, understand Buffer

Pay attention to my

If you are reading on wechat, please click the link to follow me. If you are reading on PC, please scan the code to follow me. Welcome to communicate with me and point out mistakes at any time.



Copyright notice: This article is originally published by xiaoyan. Please contact the author for republication. It is published on InfoQ and public account.