BIO系统调用strace查看IO阻塞
BIO服务端例子
服务端监听8090端口,每一个客户端用一个线程处理,不断的获取客户端的输入数据并打印
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author mubi
* @Date 2020/6/25 18:54
*/
public class TestSocket {
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = new ServerSocket(8090);
System.out.println("step1 new ServerSocket(8090)");
while(true){
final Socket client = serverSocket.accept();
System.out.println("step2 client:" + client.getPort());
new Thread(()->{
try{
InputStream in = client.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
while (true){
System.out.println(bufferedReader.readLine());
}
}catch (Exception e){
e.printStackTrace();
}
}).start();
}
}
}
nc命令去连接服务端
nc localhost 8090
strace
命令追踪系统调用
javac TestSocket.java
strace -ff -o ./stracefile java TestSocket
ServerSocket启动过程
- socket系统调用, 得到文件描述符,如 fd5
- bind端口
- listen,文件描述符
- accept(阻塞状态), 有客户端连接得到新的socket, 产生文件描述符 如 fd6,fd7
查看进程下所有线程
通过进程ID号能知道有多少个线程(linux的/proc/<pid>/task
目录下)
补充:linux创建线程是clone系统调用,在主线程的stracefile中可以看到
clone(child_stack=0x7fd8d8508ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_ PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fd8d85099d0, tls=0x7fd8d8509700, child_tidptr=0x7fd8d85099d0) = 4787
netstat查看tcp相关
有Listen
,ESTABLISHED
状态的TCP
文件描述符的查看
在工作目录会看到很多stracefile,以.线程id
结尾;本地nc命令起的56544
端口的客户端连接,vim stracefile.4772
(4772是服务端开启的主线程),可以找到accept
语句
accept(5, {sa_family=AF_INET6, sin6_port=htons(56544), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 6
fcntl(6, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(6, F_SETFL, O_RDWR) = 0
lseek(3, 62493071, SEEK_SET) = 62493071
read(3, "PK\3\4\n\0\0\10\0\0000\211|L9\267\215\270R\6\0\0R\6\0\0;\0\0\0", 30) = 30
lseek(3, 62493160, SEEK_SET) = 62493160
read(3, "\312\376\272\276\0\0\0004\0:\7\0!\n\0\v\0\"\t\0\10\0#\n\0\1\0$\t\0\v\0"..., 1618) = 1618
write(1, "step2 client:56544", 18) = 18
man 2 accept
了解到 accept 的返回:系统会产生一个文件描述符,关联接收到的socket文件
RETURN VALUE
On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On error, -1 is returned, and errno is set appropriately.
从上可以看出是文件描述符6
的一个文件,即代表了连接上的一个客户端socket(在/proc/<pid>/fd
目录下可见,pid为Java程序进程ID,本地同jps
命令看到的程序运行ID):
- strace给客户端起的线程,会阻塞在
recvfrom
接收数据上(查看上文clone产生的线程tid=4787),vim stracefile.4787
可以看到阻塞在recvfrom(6,
上,即阻塞在read
客户端socket fd上
set_robust_list(0x7fd8d85099e0, 24) = 0
gettid() = 4787
rt_sigprocmask(SIG_BLOCK, NULL, [QUIT], 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [HUP INT ILL BUS FPE SEGV USR2 TERM], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [QUIT], NULL, 8) = 0
futex(0x7fd8f000b354, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x7fd8f000b350, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
futex(0x7fd8f000b328, FUTEX_WAKE_PRIVATE, 1) = 1
sched_getaffinity(4787, 32, [1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) = 32
sched_getaffinity(4787, 32, [1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) = 32
(0x7fd8d8409000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd8d8409000
mprotect(0x7fd8d8409000, 12288, PROT_NONE) = 0
lseek(3, 30054856, SEEK_SET) = 30054856
read(3, "PK\3\4\n\0\0\10\0\0006\211|L\24w\0067E\3\0\0E\3\0\0\27\0\0\0", 30) = 30
lseek(3, 30054909, SEEK_SET) = 30054909
read(3, "\312\376\272\276\0\0\0004\0-\t\0\6\0\34\n\0\7\0\35\t\0\32\0\36\n\0\37\0\33\n\0"..., 837) = 837
lseek(3, 30056639, SEEK_SET) = 30056639
read(3, "PK\3\4\n\0\0\10\0\0B\211|L\305SF\t\265\r\0\0\265\r\0\0 \0\0\0", 30) = 30
lseek(3, 30056701, SEEK_SET) = 30056701
read(3, "\312\376\272\276\0\0\0004\0\242\n\0Y\0Z\n\0-\0[\t\0,\0\\\t\0,\0]\t\0"..., 3509) = 3509
recvfrom(6, "helloABC\n", 8192, 0, NULL, NULL) = 9
ioctl(6, FIONREAD, [0]) = 0
write(1, "helloABC", 8) = 8
write(1, "\n", 1) = 1
recvfrom(6,