write(2)在磁盘满的时候的行为

今天一个同事问我关于write(2)在写入文件的时候问题,问题是这样的:

当磁盘剩余空间不能将一次write调用希望写入的数据写完的时候,write是直接返回-1,然后设置errno为ENOSPC,还是先写入一部分数据,将剩余空间占满然后再下一次调用的时候返回错误?

当时发现好像没有考虑过这样的问题,Google了一下,好像没有找到关于这个细节的说明。

于是今天决定自己试试看。用dd建立一个64M大小的文件,然后弄上不同的文件系统用loop挂载,去写满试试看。

测试程序如下:

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdbool.h>

#define BUF_LEN 2011

static unsigned short src_buff[BUF_LEN];

static void
init_buf(unsigned short *buff)
{
    int i;

    for (i = 0; i < BUF_LEN; i++)
        buff[i] = i;            /* 缓冲区每个数据内容都唯一 */
}

static bool
add_data(int fd, unsigned short **buff, size_t  cnt)
{
    ssize_t err;
    off_t   cur_pos, last_pos;
    bool    ret = true;

    last_pos    = lseek(fd, 0, SEEK_CUR);
    printf("the first data is %hun", **buff);
    err = write(fd, *buff, cnt * sizeof(**buff));
    if (err < 0) {
        perror("write error");
        ret = false;
    } else if (err == 0) {
        perror("File tail");
    } else {
        *buff   += err / sizeof(**buff); /* 必须写入完整的buff元素 */
    }
    cur_pos = lseek(fd, -1 * (err % sizeof(**buff)), SEEK_CUR); /* 抛弃不完整的写入 */
    printf("the last data is %hun", *(*buff - 1));
    printf("return value = %zd,n", err);
    printf("the file offset is %lld to %lldn",
           (long long int)last_pos,
           (long long int)cur_pos);
    printf("n");

    return ret;
}

int main(int argc, char *argv[])
{
    unsigned short *buf_pos = src_buff;
    int             fd;
    char           *filename    = "test.dat";

    if (argc == 2) {
        filename    = argv[1];
    } /* 默认打开的文件 */

    fd  = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_SYNC, 0644);
    if (fd == -1) {
        perror("open failure");
        goto err;
    }

    init_buf(src_buff);
    do {
        if (buf_pos - src_buff >= BUF_LEN)
            buf_pos = src_buff;
    } while (add_data(fd, &buf_pos, src_buff + BUF_LEN - buf_pos));

    return EXIT_SUCCESS;
 err:
    return EXIT_FAILURE;
}

然后测试,结束前的输出如下。
reiserfs下:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 33430864 to 33434886

the first data is 0
the last data is 380
return value = 762,
the file offset is 33434886 to 33435648

the first data is 381
write error: No space left on device
the last data is 380
return value = -1,
the file offset is 33435648 to 33435647

ext4下:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 59461248 to 59465270

the first data is 0
the last data is 228
return value = 458,
the file offset is 59465270 to 59465728

the first data is 229
write error: No space left on device
the last data is 228
return value = -1,
the file offset is 59465728 to 59465727

jfs下:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 65699370 to 65703392

the first data is 0
the last data is 271
return value = 544,
the file offset is 65703392 to 65703936

the first data is 272
write error: No space left on device
the last data is 271
return value = -1,
the file offset is 65703936 to 65703935

xfs下:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 58648804 to 58652826

the first data is 0
the last data is 946
return value = 1894,
the file offset is 58652826 to 58654720

the first data is 947
write error: No space left on device
the last data is 946
return value = -1,
the file offset is 58654720 to 58654719

ntfs-3g:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 64488748 to 64492770

the first data is 0
the last data is 1422
return value = 2846,
the file offset is 64492770 to 64495616

the first data is 1423
write error: No space left on device
the last data is 1422
return value = -1,
the file offset is 64495616 to 64495615

vfat下:

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 66950212 to 66954234

the first data is 0
the last data is 2010
return value = 4022,
the file offset is 66954234 to 66958256

the first data is 0
write error: No space left on device
the last data is 0
return value = -1,
the file offset is 66958256 to 66958255

貌似除了vfat以外,都是尽量将磁盘写满。但是vfat只要空间不够,就立即返回错误了。

不知道这个特性是什么标准规定的?或者是没有规定的?

Leave a Comment

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据