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.