简体   繁体   中英

C implement like cat with robustness and efficiency

I want to learn to implement the function like cat, which just take input from a file and print to stdout.

But I am not sure the line of write() is robust in all cases as it may write less than n . But I am not able to create a test case to make this case happen. How to make a test case so that it can result in less than n char be written? Also, how to modify the code accordingly to make the program robust (for this case, but also for other cases that I have not described)?

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
    const char *pathname = argv[1];
    int fd;
    if((fd = open(pathname, O_RDONLY)) == -1) {
        perror("open");
        return 1;
    }
#define BUF_SIZE 1024
    char buf[BUF_SIZE];
    ssize_t n;
    while((n = read(fd, &buf, BUF_SIZE)) > 0) {
        if(write(STDOUT_FILENO, &buf, n) == -1) {
            perror("write");
            return 1;
        }
    }
    if(n == -1) {
        perror("read");
        return 1;
    }
    if(close(fd) == -1) {
        perror("close");
        return 1;
    }
    return 0;
}

EDIT: I fixed the write() bug in the previous code based on the pipe-blocking test case mentioned by Armali. Can anybody check whether there are any other bugs?

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
    const char *pathname = argv[1];
    int fd;
    if((fd = open(pathname, O_RDONLY)) == -1) {
        perror("open");
        return 1;
    }
#define BUF_SIZE 2*65536
    char buf[BUF_SIZE];
    ssize_t r_n;
    while((r_n = read(fd, &buf, BUF_SIZE)) > 0) {
        ssize_t w_n;
        int i = 0;
        while((w_n = write(STDOUT_FILENO, buf+i, r_n)) < r_n) {
            if(w_n == -1) {
                perror("write");
                return 1;
            }
            r_n -= w_n;
            i += w_n;
        }
    }
    if(r_n == -1) {
        perror("read");
        return 1;
    }
    if(close(fd) == -1) {
        perror("close");
        return 1;
    }
    return 0;
}

How to make a test case so that it can result in less than n char be written?

Depending on the system, this hasn't to be difficult. man 2 write tells:

… partial writes can occur for various reasons; for example, because there was
insufficient space on the disk device to write all of the requested bytes, or because
a blocked write() to a socket, pipe, or similar was interrupted by a signal …

Let's focus on pipe writes, which block if the pipe buffer is full. This example targets Linux; the numbers may well be different on other systems. man 7 pipe tells about the Pipe capacity :

Since Linux 2.6.11, the pipe capacity is 16 pages (i.e., 65,536 bytes in a system with
a page size of 4096 bytes).

To make use of this for the sake of a test case, we have to increase your BUF_SIZE to a greater value, say 1024*128 . Then we can conduct the experiment:

~$ dd count=256 </dev/zero >zeros
256+0 records in
256+0 records out
131072 bytes (131 kB, 128 KiB) copied, 0.00357324 s, 36.7 MB/s
~$ strace -ewrite ./a.out zeros|(sleep 9; dd)
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072^Z
[1]+  Stopped                 strace -ewrite ./a.out zeros | ( sleep 9; dd )
~$ fg
strace -ewrite ./a.out zeros | ( sleep 9; dd )
) = 65536
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=482, si_uid=2001} ---
+++ exited with 0 +++
128+0 records in
128+0 records out
65536 bytes (66 kB, 64 KiB) copied, 0.00546081 s, 12.0 MB/s
  • First we prepare a file of 128 KiB.
  • Then we run the program with strace -ewrite to see the write operation; we pipe its output to (sleep 9; dd) to delay the reading. During this delay, we raise a STOP signal by pressing Ctrl-Z.
  • Finally, we continue the program with fg . Now we see that the write operation, which has been called with count 131072 , returns only 65536 bytes written.

Also, how to modify the code accordingly to make the program robust (for this case, but also for other cases that I have not described)?

For this case and similar cases, you'd do as David C. Rankin wrote:

There you would just loop until n bytes had been written keeping track of the number written with each call.

I can't say how to make a program robust for undescribed cases other than checking the returned values from all called library functions of which you can't be sure that they succeed.

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