简体   繁体   中英

Disk write does not work with malloc in C

I did write to disk using C code.

First I tried with malloc and found that write did not work (write returned -1):

fd = open('/dev/sdb', O_DIRECT | O_SYNC | O_RDWR);
void *buff = malloc(512);
lseek(fd, 0, SEEK_SET);
write(fd, buff, 512);

Then I changed the second line with this and it worked:

void *buff;
posix_memalign(&buff,512,512);

However, when I changed the lseek offset to 1: lseek(fd, 1, SEEK_SET); , write did not work again.

First, why didn't malloc work?

Then, I know that in my case, posix_memalign guarantees that start address of memory alignment must be multiple of 512. But, should not memory alignment and write be a separate process? So why I could not write to any offset that I want?

From the Linux man page for open(2):

The O_DIRECT flag may impose alignment restrictions on the length and address of user-space buffers and the file offset of I/Os.

And:

Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file offset must all be multiples of the logical block size of the filesystem. Under Linux 2.6, alignment to 512-byte boundaries suffices.

The meaning of O_DIRECT is to "try to minimize cache effects of the I/O to and from this file", and if I understand it correctly it means that the kernel should copy directly from the user-space buffer, thus perhaps requiring stricter alignment of the data.

Maybe the documentation doesn't say, but it's quite possible that write and reads to/from a block device is required to be aligned and for entire blocks to succeed (this would explain why you get failure in your first and last cases but not in the second). If you use linux the documentation of open(2) basically says this:

The O_DIRECT flag may impose alignment restrictions on the length and address of user-space buffers and the file offset of I/Os. In Linux alignment restrictions vary by file system and kernel version and might be absent entirely. However there is cur‐ rently no file system-independent interface for an application to discover these restrictions for a given file or file system. Some file systems provide their own interfaces for doing so, for example the XFS_IOC_DIOINFO operation in xfsctl(3).

Your code shows lack of error handling. Every line in the code contains functions that may fail and open , lseek and write also reports the cause of error in errno . So with some kind of error handling it would be:

fd = open('/dev/sdb', O_DIRECT | O_SYNC | O_RDWR);

if( fd == -1 ) {
    perror("open failed");
    return;
}

void *buff = malloc(512);

if( !buff ) {
    printf("malloc failed");
    return;
}

if( lseek(fd, 0, SEEK_SET) == (off_t)-1 ) {
    perror("lseek failed");
    free(buff);
    return;
}

if( write(fd, buff, 512) == -1 ) {
    perror("write failed");
    free(buff);
    return;
}

in that case you would at least get a more detailed explaination on what goes wrong. In this case I suspect that you get EIO (Input/output error) from the write call.

Note that the above maybe isn't complete errorhandling as perror and printf themselves can fail (and you might want to do something about that possibility).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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