正确的方法将X字节写入文件描述符,其中X是一个无符号的64位整数



考虑以下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_tptrdiff_t使用的类型不应具有整数转换等级 除非实施支持对象,否则大于signed long int 足够大以使这一必要。

因此,除非您在512位处理器上运行,否则size_tssize_t不太可能是512位。

现在,您极有可能不是对于任何内存或磁盘大小,都需要超过64位。这限制了您在write语句中可以拥有的数据量。

最新更新