CS144-Note1
CS144-Lab Note-1
CS144 lab,about IO basic
read and write
read和write一次只能下发一个IO请求,并将数据读写到一个指定的缓冲区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#define PER_IO (16)
void do_readv(int fd, size_t size, size_t block_size)
{
void* _buff[PER_IO];
struct iovec _iovec[PER_IO];
size_t _cnt = (size / block_size) / PER_IO;
for (int i = 0; i < PER_IO; i++) {
posix_memalign(&_buff[i], 4096, block_size);
memset(_buff[i], 0xff, block_size);
_iovec[i].iov_base = _buff[i];
_iovec[i].iov_len = block_size;
}
for (int i = 0; i < _cnt; i++) {
readv(fd, &_iovec[0], PER_IO);
}
}
void do_writev(int fd, size_t size, size_t block_size)
{
void* _buff[PER_IO];
struct iovec _iovec[PER_IO];
size_t _cnt = (size / block_size) / PER_IO;
for (int i = 0; i < PER_IO; i++) {
posix_memalign(&_buff[i], 4096, block_size);
memset(_buff[i], 0xff, block_size);
_iovec[i].iov_base = _buff[i];
_iovec[i].iov_len = block_size;
}
for (int i = 0; i < _cnt; i++) {
writev(fd, &_iovec[0], PER_IO);
}
}
void do_read(int fd, size_t size, size_t block_size)
{
void* _buff = nullptr;
size_t _cnt = size / block_size;
posix_memalign(&_buff, 4096, block_size);
memset(_buff, 0xff, block_size);
char* _dest = (char*)_buff;
uint64_t _pos = 0;
for (int i = 0; i < _cnt; i++) {
read(fd, _dest, block_size);
// _pos += block_size;
}
}
void do_write(int fd, size_t size, size_t block_size)
{
void* _buff = nullptr;
size_t _cnt = size / block_size;
posix_memalign(&_buff, 4096, block_size);
memset(_buff, 0xff, block_size);
uint64_t _pos = 0;
char* _src = (char*)_buff;
for (int i = 0; i < _cnt; i++) {
write(fd, _src, block_size);
// _pos += block_size;
}
}
int main(int argc, char** argv)
{
int _scan;
char* _name = argv[1];
size_t _size = atol(argv[2]) * 1024 * 1024;
size_t _block_size = atol(argv[3]);
printf("[%s][size:%zu][bs:%zu]\n", _name, _size, _block_size);
int _fd = open(_name, O_RDWR | O_DIRECT, 0666);
assert(_fd > 0);
close(_fd);
return 0;
}
另外为什么有了malloc还要posix_memalign:
- 更严格的对齐, malloc返回一个指针(内存已经对齐),这样就基本可以和任何元类型一起使用(常见的是8 byte对齐),但当你需要其他的边界对齐的时候(4k对齐,操作磁盘之类的)
还有一个问题是最近的体系结构中,cpu读一次缓存(cache line)一般是64 char。所以这种时候其实还得注意下一个结构体的大小,之前PHP中的hashtable从74->56,但是提升巨大,多半都是从这里来的
Tcp keepalive
TCP的keepalive(保活机制)是一种网络协议中的功能,用于确保TCP连接在长时间没有数据传输时仍然保持活跃状态。这个机制主要有以下几个目的:
检测连接状态:定期检查连接是否仍然有效,即使长时间没有数据传输。
防止连接超时:某些网络设备(如防火墙或NAT设备)可能会在一定时间后关闭看似不活跃的连接,keepalive可以防止这种情况发生。
清理死连接:如果对端已经崩溃或网络中断,keepalive可以帮助及时发现并关闭这些失效的连接。
工作原理:
当TCP连接在一定时间内没有数据传输时,系统会发送一个特殊的keepalive探测包。
如果对端正常,会回复一个ACK包。
如果在多次尝试后仍未收到回复,系统会认为连接已断开,并关闭该连接。
配置参数:
在Linux系统中,TCP keepalive通常有三个主要参数:
tcp_keepalive_time:发送第一个keepalive探测包的等待时间,默认是7200秒(2小时)。
tcp_keepalive_intvl:两个探测包之间的间隔时间,默认是75秒。
tcp_keepalive_probes:没有回应时,最大尝试次数,默认是9次。
在 Linux 系统中,发现一条 dead TCP 连接的最长时间取决于 TCP keepalive 的配置参数, 默认下,
- 系统等待 7200 秒才发送第一个 keepalive 探测包。
- 如果没有响应,会再等待 75 秒发送第二个探测包。
- 这个过程最多重复 9 次。
因此,最长时间 = 7200 + (75 * 9) = 7875 秒 ≈ 2 小时 11 分 15 秒
在实际网络环境中,路由器、防火墙等中间设备可能会在更短的时间内关闭看似不活跃的连接。
使用场景:
长连接应用:如数据库连接、消息推送服务等。
网络环境不稳定:在可能出现临时网络中断的环境中。
负载均衡:帮助负载均衡器维护活跃连接池。
注意事项:
增加网络流量:虽然keepalive包很小,但在大规模系统中可能会增加显著的网络开销。
配置权衡:过于频繁的keepalive可能会增加不必要的网络负担,而间隔太长又可能无法及时检测连接状态。
应用层keepalive:某些应用可能实现自己的keepalive机制,这可能比TCP层的keepalive更适合特定需求。
安全考虑:在某些情况下,keepalive可能被用于保持未经授权的连接,因此需要在安全策略中考虑这一点。