考虑以下C函数:
static void
write_buf_to_disk(int fd, void *buf, __u64 size) {
const char* char_buf = buf;
__u64 written = 0;
while (size > 0) {
ssize_t res = write(fd, char_buf, size);
if (res == -1) {
if (errno == EINTR) {
// Write interrupted before anything written.
continue;
}
err(EXIT_FAILURE, "write");
}
written += res;
char_buf += res;
size -= res;
}
}
该函数读取了buf
的字节,直到编写了请求的字节数为止。size
的类型不受我的控制,并且必须是__u64
。
我认为这是由于ssize_t
和__u64
之间的摩擦而引起的。
ssize_t
来自一个相当模糊的posix扩展,AFAIC保证为:
- 至少16位
- 签名
- 与
size_t
相同的大小
因此,从理论上讲,ssize_t
可能是(我知道)512位宽,这意味着written += res
调用了不确定的行为。
一个人如何以便携式方式防止这种情况?
res
不高于 write
的第三个参数,因此您要做的就是限制write
的第三个参数不超过res
的最大正值(ssize_t
)可以存储。
换句话说,替换
ssize_t res = write(fd, char_buf, size);
size_t block_size = SSIZE_MAX;
if (block_size > size)
block_size = size;
ssize_t res = write(fd, char_buf, block_size);
您得到:
static void
write_buf_to_disk(int fd, void *buf, __u64 size) {
const char* char_buf = buf;
size_t block_size = SSIZE_MAX;
while (size > 0) {
if (block_size > size)
block_size = size;
ssize_t res = write(fd, char_buf, block_size);
if (res == -1) {
if (errno == EINTR)
continue;
err(EXIT_FAILURE, "write");
}
char_buf += res;
size -= res;
}
}
in
ssize_t res = write(fd, buf, size);
即使ssize_t
的宽度为512位,如您所建议的,编译器将write
(64位)的结果促进了该尺寸。因此比较仍然可以。
在
中written += res;
编译器会给您发出警告,但是数字为64位,以计算书面字节的数量确实很大(〜10 19 bytes max)。因此,即使添加是从512位到64位。
,您也不太可能错过任何写作。您也可以在功能开头的ssize_t
分配大小
write_buf_to_disk(int fd, void *buf, __u64 size64) {
ssize_t size = size64;
将使身体的其余部分与系统功能保持一致。
C11标准说(7.19):
size_t
和ptrdiff_t
使用的类型不应具有整数转换等级 除非实施支持对象,否则大于signed long int
足够大以使这一必要。
因此,除非您在512位处理器上运行,否则size_t
和ssize_t
不太可能是512位。
现在,您极有可能不是对于任何内存或磁盘大小,都需要超过64位。这限制了您在write
语句中可以拥有的数据量。