一个完整的 Linux 操作系统体系架构通常由下列几个核心层级组成:
- Applications:在操作系统上安装并运行的用户态应用程序
- Shell:支持编程的命令行解析器
- Libs:操作系统标准库函数
- System Calls:暴露给用户态的内核态系统调用接口
- Kernel:操作系统的核心,真正对接硬件平台的软件程序
所以,为什么要划分核心态和用户态?简单来说:
- 禁止用户程序和底层硬件平台直接交互
- 禁止用户程序直接访问任意内存地址空间
用户程序除了通过系统调用主动触发模式切换之外,还可能会被动的进行。总的来说模式切换有两种触发手段:
-
(软中断)系统调用:这时用户态进程要传递很多变量或参数值给内核,内核态运行时也要保存用户进程的一些寄存器值和变量等等。所谓的「进程上下文」,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值以及运行环境等。
-
(硬中断)外围设备中断:硬件可以通过触发中断信号令内核调用中断处理程序从而进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。所谓的「中断上下文」,就是硬件传递过来的这些参数和内核需要保存的当前被中断执行的进程环境。
答案:从代码和system call去理解
BIOimport
java.io.BufferedReader;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.InputStreamReader;
import
java.net.ServerSocket;
import
java.net.Socket;
public
class
TestSocket {
public
static
void
main(String[] args)
throws
IOException {
ServerSocket server =
new
ServerSocket(
8090
);
System.out.printf(
"step1:new ServerSocket(80)"
);
while
(
true
){
final
Socket client = server.accept();
System.out.printf(
"step2:client\t"
+client.getPort());
new
Thread(()->{
try
{
InputStream in = client.getInputStream();
BufferedReader reader =
new
BufferedReader(
new
InputStreamReader(in));
while
(
true
){
System.out.printf(reader.readLine());
}
}
catch
(IOException e){
}
}).start();
}
}
}
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5
fcntl(5, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0
setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
..
bind(5, {sa_family=AF_INET, sin_port=htons(8090), sin_addr=inet_addr(
"0.0.0.0"
)}, 16) = 0
listen(5, 50)
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1
import
java.io.IOException;
import
java.net.InetSocketAddress;
import
java.nio.ByteBuffer;
import
java.nio.channels.ServerSocketChannel;
import
java.nio.channels.SocketChannel;
import
java.util.LinkedList;
public
class
TestSocketNIO {
public
static
void
main(String[] args)
throws
IOException {
LinkedList socketChannels =
new
LinkedList();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(
new
InetSocketAddress(
8091
));
serverSocketChannel.configureBlocking(
false
);
System.out.printf(
"step1:new ServerSocket(80)"
);
while
(
true
){
SocketChannel client = serverSocketChannel.accept();
if
(client ==
null
){
System.out.printf(
"client null"
);
}
else
{
client.configureBlocking(
false
);
int
port = client.socket().getPort();
System.out.printf(
"client .port :"
+port);
socketChannels.add(client);
}
ByteBuffer buff = ByteBuffer.allocateDirect(
4096
);
for
(SocketChannel socketChannel : socketChannels){
int
num = socketChannel.read(buff);
if
(num>
0
){
buff.flip();
byte
[] a =
new
byte
[buff.limit()];
buff.get(a);
String b =
new
String(a);
System.out.printf(socketChannel.socket().getPort()+
":"
+b);
buff.clear();
}
}
}
}
}
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 4
2711 setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
2712 lseek(3, 64757815, SEEK_SET) = 64757815
...
2724 bind(4, {sa_family=AF_INET, sin_port=htons(8091), sin_addr=inet_addr(
"0.0.0.0"
)}, 16) = 0
2725 listen(4, 50) = 0
2726 getsockname(4, {sa_family=AF_INET, sin_port=htons(8091), sin_addr=inet_addr(
"0.0.0.0"
)}, [16]) = 0
2727 getsockname(4, {sa_family=AF_INET, sin_port=htons(8091), sin_addr=inet_addr(
"0.0.0.0"
)}, [16]) = 0
2728 fcntl(4, F_GETFL) = 0x2 (flags O_RDWR)
2729 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
...
accept(4, 0x2b08f01387e0, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2884 accept(4, 0x2b08f013b1e0, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2885 accept(4, 0x2b08f0147680, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2886 accept(4, 0x2b08f013b1e0, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2887 accept(4, 0x2b08f0147680, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2888 accept(4, 0x2b08f013b1e0, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
2889 accept(4, 0x2b08f0147680, 0x2b08ecb87710) = -1 EAGAIN (Resource temporarily unavailable)
select poll epoll
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 4
2695 setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
2696 fcntl(4, F_GETFL) = 0x2 (flags O_RDWR)
2697 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
bind(4, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr(
"0.0.0.0"
)}, 16) = 0
listen(4, 50) = 0
epoll_create(256) = 8
2823 epoll_ctl(8, EPOLL_CTL_ADD, 6, {EPOLLIN, {u32=6, u64=17515013522285658118}}) = 0
2824 lseek(3, 52829082, SEEK_SET) = 52829082
2825
read
(3,
"PK\3\4\n\0\0\10\0\0\274\205\367N,\270\332\313\v\10\0\0\v\10\0\0!\0\0\0"
, 30) = 30
2826 lseek(3, 52829145, SEEK_SET) = 52829145
2827
read
(3,
"\312\376\272\276\0\0\0004\0M\n\0\23\0005\t\0\22\0006\t\0\22\0007\7\0008\t\0\22\0"
..., 2059) = 2059
2828 lseek(3, 31933647, SEEK_SET) = 31933647
2829
read
(3,
"PK\3\4\n\0\0\10\0\0\274\205\367ND[\333w\217\3\0\0\217\3\0\0000\0\0\0"
, 30) = 30
2830 lseek(3, 31933725, SEEK_SET) = 31933725
2831
read
(3,
"\312\376\272\276\0\0\0004\0&\n\0\7\0\32\t\0\6\0\33\n\0\6\0\34\7\0\35\n\0\4\0"
..., 911) = 911
2832 lseek(3, 31926658, SEEK_SET) = 31926658
2833
read
(3,
"PK\3\4\n\0\0\10\0\0\274\205\367N\277`e.\247\7\0\0\247\7\0\0$\0\0\0"
, 30) = 30
2834 lseek(3, 31926724, SEEK_SET) = 31926724
2835
read
(3,
"\312\376\272\276\0\0\0004\0F\n\0\7\0008\t\0\4\0009\n\0\4\0:\7\0;\t\0\4\0"
..., 1959) = 1959
2836 write(1,
"\347\255\211\345\276\205\344\272\213\344\273\266\345\217\221\347\224\237\343\200\202\343\200\202"
, 24) = 24
2837 write(1,
"\n"
, 1) = 1
2838 lseek(3, 31934636, SEEK_SET) = 31934636
2839
read
(3,
"PK\3\4\n\0\0\10\0\0\274\205\367N\3005\227\350\35\3\0\0\35\3\0\0.\0\0\0"
, 30) = 30
2840 lseek(3, 31934712, SEEK_SET) = 31934712
2841
read
(3,
"\312\376\272\276\0\0\0004\0%\t\0\4\0\32\n\0\5\0\33\n\0\30\0\34\7\0\35\7\0\36\7"
..., 797) = 797
2842 lseek(3, 66534570, SEEK_SET) = 66534570
2843
read
(3,
"PK\3\4\n\0\0\10\0\0\273\205\367N\300\tR\330\242\0\0\0\242\0\0\0\36\0\0\0"
, 30) = 30
2844 lseek(3, 66534630, SEEK_SET) = 66534630
2845
read
(3,
"\312\376\272\276\0\0\0004\0\t\7\0\7\7\0\10\1\0\tinterrupt\1\0\25("
..., 162) = 162
2846 epoll_ctl(8, EPOLL_CTL_ADD, 4, {EPOLLIN, {u32=4, u64=289558586198065156}}) = 0
2847 epoll_wait(8,
调用epoll_create时,做了以下事情:
- 内核帮我们在epoll文件系统里建了个file结点;
- 在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket;
- 建立一个list链表,用于存储准备就绪的事件