今天一个同事问我关于write(2)在写入文件的时候问题,问题是这样的:
当磁盘剩余空间不能将一次write调用希望写入的数据写完的时候,write是直接返回-1,然后设置errno为ENOSPC,还是先写入一部分数据,将剩余空间占满然后再下一次调用的时候返回错误?
当时发现好像没有考虑过这样的问题,Google了一下,好像没有找到关于这个细节的说明。
于是今天决定自己试试看。用dd建立一个64M大小的文件,然后弄上不同的文件系统用loop挂载,去写满试试看。
测试程序如下:
#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 %hu\n", **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 %hu\n", *(*buff - 1));
printf("return value = %zd,\n", err);
printf("the file offset is %lld to %lld\n",
(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 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 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 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 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 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 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只要空间不够,就立即返回错误了。
不知道这个特性是什么标准规定的?或者是没有规定的?