0
\$\begingroup\$

I've just found out there is sendfile(). In the man page of sendfile(), it says:

sendfile() is more efficient than the combination of read(2) and write(2).

So, I wrote a function to copy a file using sendfile().

Is really my code more efficient than the combination of read(2) and write(2)?

/* leftsize is initialized with total size of source file. */
fd_src = open(src_fname, O_RDONLY);
if (fd_src<0){
    printf("open failed(%s)\n", src_fname);
    return -1;
}
fd_dest = creat(dest_fname, 0666);
if (fd_dest<0){
    printf("create failed(%s)\n", dest_fname);
    return -1;
}
while (leftsize > 0) {
    n = sendfile(fd_dest, fd_src, NULL, max_size);
    if (n<0) {
        printf("sendfile failed. ret:%d errno:%d\n", n, errno);
    }
    leftsize -= n;
}
\$\endgroup\$
1
\$\begingroup\$

The first paragraph of the man page says it all:

sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.

So, sendfile is quite efficient.

However, your loop may use sendfile incorrectly because you always try to transfer max_size and this could return an error (EOVERFLOW) on the last chunk of the file (i.e. you try to read more than remains in the input file).

This may work just fine if you handle the error case properly.

It also depends upon what max_size and leftsize actually are [which you didn't specify].

I'll assume that max_size is some reasonable chunk limit (e.g. max_size = 1024 * 1024;) and that leftsize is the size of the input file that comes from:

struct stat st;
fstat(fd_src,&st);
leftsize = st.st_size;

So, just to be safe, here is what I'd do:

while (leftsize > 0) {
    // get chunk size
    n = max_size;

    // clip transfer size to remainder of file
    if (n > leftsize)
        n = leftsize;

    n = sendfile(fd_dest, fd_src, NULL, n);
    if (n<0) {
        printf("sendfile failed. ret:%d errno:%d\n", n, errno);
    }

    leftsize -= n;
}
\$\endgroup\$
0
\$\begingroup\$

It's good that you are testing the return values to see if your function calls succeeded. However, it's good practice to use the standard error stream for your error messages:

if (fd_src<0){
    fprintf(stderr, "open failed(%s)\n", src_fname);
    return -1;
}

It's not clear whether the code is supposed to be in main() (next time, please post a complete function, and preferably a full, compilable program); if so, it's conventional to use small positive numbers for exit status. Negative numbers can get truncated - on my system, a return of -1 ends up as 255 when run from a shell. If you return +1 instead, that is received unscathed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.