使用系统调用来实现Unix中的cat命令

5

我的操作系统课程作业要求我使用系统调用实现类Unix的cat命令(不能使用scanf或printf)。以下是我目前的代码:

(感谢他人的回答,我已经改进了代码)

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>



main(void)
{


   int fd1; 
   int fd2;

   char *buffer1;
   buffer1 = (char *) calloc(100, sizeof(char));


   char *buffer2;
   buffer2 = (char *)calloc(100, sizeof(char));

   fd1 = open("input.in", O_RDONLY);    
   fd2 = open("input2.in", O_RDONLY);


   while(eof1){ //<-lseek condition to add here
   read (fd1, buffer1, /*how much to read here?*/ );
   write(1, buffer1, sizeof(buffer1)-1);     
   }


   while (eof2){ 

    read (fd2,buffer2, /*how much to read here?*/);  
    write(1, buffer2, sizeof(buffer2)-1);

    }

}

我看到的示例只展示了读取已知字节数的内容。我不知道每个读取的文件将有多少字节,那么如何指定读取的最后一个参数呢?


我的回答对你之前的问题可能有帮助,因为你想要使用系统调用来实现cat - jschmier
5个回答

5
  • 在你能够读取缓冲区之前,你必须分配一个缓冲区。可以在堆栈上(最简单)或使用mmap
  • perror是一个复杂的库函数,而不是系统调用。
  • exit在Linux上不是系统调用。但是_exit是。
  • 不要比你读取之前拥有的字节数更多地写入
  • 总之:阅读所有这些系统调用的文档。

编辑:这是我的代码,只使用系统调用。错误处理有些有限,因为我不想重新实现perror

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

static int
cat_fd(int fd) {
  char buf[4096];
  ssize_t nread;

  while ((nread = read(fd, buf, sizeof buf)) > 0) {
    ssize_t ntotalwritten = 0;
    while (ntotalwritten < nread) {
      ssize_t nwritten = write(STDOUT_FILENO, buf + ntotalwritten, nread - ntotalwritten);
      if (nwritten < 1)
        return -1;
      ntotalwritten += nwritten;
    }
  }

  return nread == 0 ? 0 : -1;
}

static int
cat(const char *fname) {
  int fd, success;

  if ((fd = open(fname, O_RDONLY)) == -1)
    return -1;

  success = cat_fd(fd);

  if (close(fd) != 0)
    return -1;

  return success;
}


int
main(int argc, char **argv) {
  int i;

  if (argc == 1) {
    if (cat_fd(STDIN_FILENO) != 0)
      goto error;
  } else {
    for (i = 1; i < argc; i++) {
      if (cat(argv[i]) != 0)
        goto error;
    }
  }
  return 0;

error:
  write(STDOUT_FILENO, "error\n", 6);
  return 1;
}

perror和exit不重要。只需避免使用printf和scanf。 - andandandand
@omgzor - 那你真的没有“仅使用系统调用实现Unix的cat命令”。 - jschmier
我还是把它们拿出来了,以免在回复中进一步提及这个事情。 - andandandand

3
你需要读取尽可能多的字节,以适应缓冲区大小。现在,你还没有一个缓冲区,只有一个指向缓冲区的指针。它没有被初始化。这就像鸡生蛋、蛋生鸡的问题一样,因此你也不知道要读取多少字节。
创建一个缓冲区。

用多大的尺寸?c =(char *)calloc(100,sizeof(char)); - andandandand
这是我在指南上所写的内容。它指定了100个字符,如果您不知道文件有多少个字符,您会在calloc中写什么? - andandandand
1
通常情况下,您可以从更大的缓冲区中受益,但100就足够了。只需使用循环将一块数据读入该缓冲区,然后将该块数据写入stdout(read()会告诉您实际填充到缓冲区中的数量,因此请勿在每个循环中写入超过该数量的数据)。 - nos
无论您喜欢哪种大小。大缓冲区:更快但更厚。小缓冲区:更瘦但更慢。 - Amadan
calloc 不是一个系统调用。 - Roland Illig

2

通常不需要一次性读取整个文件。选择与主机操作系统的内存页面大小相同或倍数的缓冲区大小是一个好方法。1或2个页面大小可能已经足够。

使用过大的缓冲区实际上会导致程序运行更差,因为它们会对虚拟内存系统造成压力并导致分页。


2
您可以使用openfstatmmapmadvisewrite来制作一个非常高效的cat命令。
如果特定于Linux,则可以使用openfstatfadvisesplice来制作更高效的cat命令。
建议调用是为了指定SEQUENTIAL标志,这将告诉内核对文件进行积极的预读取。
如果您希望对系统其余部分保持礼貌并最小化缓冲区缓存使用,则可以将复制分成32兆字节左右的块,并在已经读取的部分上使用DONTNEED标志进行建议。
注意:
以上内容仅适用于源是文件的情况。如果fstat未能提供大小,则必须回退到使用已分配的缓冲区和readwrite。您也可以使用splice

1
使用stat函数来查找在读取文件之前的文件大小。或者,您可以读取块,直到获取EOF。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接