简体   繁体   English

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

[英]Correct way to write X bytes to a file descriptor where X is a unsigned 64-bit integer

Consider the following C function: 考虑以下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;
    }                                                                              
}

The function reads bytes out of buf until the requested number of bytes have been written. 该函数从buf读取字节,直到已写入请求的字节数为止。 The type of size is out of my control, and must be a __u64 . size类型超出了我的控制范围,必须为__u64

I don't think this is portable due to friction between ssize_t and __u64 . 由于ssize_t__u64之间的摩擦,我认为这不是可移植的。

ssize_t comes from a rather vague POSIX extension which AFAICS guarantees to be: ssize_t来自一个模糊的POSIX扩展,AFAICS保证它是:

  • at least 16-bits wide 至少16位宽
  • signed
  • the same size as a size_t size_t大小相同

So in theory ssize_t could be (unlikely, I know) 512 bits wide, meaning that written += res invokes undefined behaviour. 因此,从理论上讲, ssize_t可能是(不太可能,我知道)512位宽,这意味着written += res调用未定义的行为。

How does one guard against this in a portable fashion? 如何以一种可移植的方式防止这种情况发生?

res will be no higher than write 's third argument, so all you have to do is constrain the 3rd argument of write to be no larger than the largest positive value that res ( ssize_t ) can store. res将不高于write的第三个参数,因此,所有你需要做的就是限制的第三个参数write到不低于最大正值大resssize_t )可以存储。

In other words, replace 换句话说,替换

ssize_t res = write(fd, char_buf, size);

with

size_t block_size = SSIZE_MAX;
if (block_size > size)
   block_size = size;

ssize_t res = write(fd, char_buf, block_size);

You get: 你得到:

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);

even if ssize_t would be 512 bits wide, as you suggested, the compiler promotes the result of write (64 bits) to that size. 即使您建议的ssize_t为512位宽,编译器也会将write结果(64位)提升为该大小。 So the comparison would still work. 因此,比较仍然可以进行。

In

written += res;

the compiler would give you a warning, but 64 bits to count the number of bytes written is really gigantic (~10 19 bytes max). 编译器会警告您,但是64位来计数写入的字节数确实是巨大的(最多10个19字节)。 So you're unlikely to miss any write even though the addition is from a 512 bits to a 64 bits. 因此,即使从512位增加到64位,您也不会丢失任何写入。

You could also assign the size to a ssize_t at the beginning of the function 您也可以在函数开始时将大小分配给ssize_t

write_buf_to_disk(int fd, void *buf, __u64 size64) { 
   ssize_t size = size64;

that would make the rest of the body in line with the system functions. 这将使身体的其余部分与系统功能保持一致。

The C11 standard says (7.19): C11标准说(7.19):

The types used for size_t and ptrdiff_t should not have an integer conversion rank greater than that of signed long int unless the implementation supports objects large enough to make this necessary. 除非实现支持足够大的对象以使其成为必需,否则用于size_tptrdiff_t的类型的整数转换等级不应大于有signed long int的整数转换等级。

So size_t and ssize_t are unlikely to be 512 bits, unless you run on a 512-bit processor. 因此,除非您在512位处理器上运行,否则size_tssize_t不太可能是512位。

Right now you are extremely likely not to need more than 64 bits for any memory or disk size. 现在,你极有可能并不需要任何内存或磁盘大小超过64位。 That limits the amount of data you can have in a write statement. 这限制了您在write语句中可以拥有的数据量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM