【问题标题】:Using system calls to implement the unix cat command使用系统调用实现 unix cat 命令
【发布时间】:2010-10-05 18:14:16
【问题描述】:

对于我的操作系统课程,我的任务是使用系统调用(没有 scanf 或 printf)实现 Unix 的 cat 命令。这是我到目前为止得到的:

(感谢回复编辑)

#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);

    }

}

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

【问题讨论】:

标签: c system-calls cat


【解决方案1】:
  • 在您可以read 进入缓冲区之前,您必须分配一个。在堆栈上(最简单)或使用mmap
  • perror 是一个复杂的库函数,不是系统调用。
  • exit 不是 Linux 上的系统调用。但是_exit 是。
  • write 的字节数不要超过 read 之前的字节数。
  • 或者,一般来说:阅读所有这些系统调用的文档。

编辑:这是我的代码,仅使用系统调用。错误处理有些有限,因为我不想重新实现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;
}

【讨论】:

  • 错误和退出无关紧要。只有 printf 和 scanf 要避免。
  • @omgzor - 那么你真的不是“仅通过系统调用实现 Unix 的 cat 命令”
  • 我还是把它们删了,以避免在回复中进一步提及这件事。
【解决方案2】:

您需要读取尽可能多的字节以适合缓冲区。现在,你还没有缓冲区,你得到的只是一个指向缓冲区的指针。那没有初始化为任何东西。鸡和蛋,因此您也不知道要读取多少字节。

创建一个缓冲区。

【讨论】:

  • 尺寸是多少? c = (char *) calloc(100, sizeof(char));
  • 这就是我的指南。它指定 100 个字符,如果你不知道文件有多少个字符,你会在 calloc 上写什么?
  • 您通常可以从更大的缓冲区中受益,但 100 就可以了。只需使用循环将一个片段读入该缓冲区,然后将该片段写入标准输出(read() 会告诉您它实际填充到缓冲区中的数量,因此每个循环中写入的内容不要超过)
  • 您喜欢哪种尺寸。大缓冲区:更快但更胖。小缓冲区:更精简但更慢。
  • calloc 不是系统调用。
【解决方案3】:

通常不需要一口气读完整个文件。选择与主机操作系统的内存页面大小相同或倍数的缓冲区大小是一个不错的方法。 1 或 2 X 的页面大小可能就足够了。

使用太大的缓冲区实际上会导致您的程序运行得更糟,因为它们会给虚拟内存系统施加压力并可能导致分页。

【讨论】:

    【解决方案4】:

    您可以使用openfstatmmapmadvisewrite 来制作一个非常高效的 cat 命令。

    如果特定于 Linux,您可以使用 openfstatfadvisesplice 来制作更高效的 cat 命令。

    advise 调用是指定 SEQUENTIAL 标志,这将告诉内核对文件进行积极的预读。

    如果您希望对系统的其余部分保持礼貌并尽量减少缓冲区缓存的使用,您可以以 32 兆字节左右的大小进行复制,并在已读取的部分上使用建议 DONTNEED 标志。

    注意:

    上述方法仅在源是文件时才有效。如果 fstat 无法提供大小,那么您必须回退到使用分配的缓冲区和readwrite。你也可以使用splice

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2013-07-27
        • 1970-01-01
        • 1970-01-01
        • 2012-07-16
        • 1970-01-01
        • 2021-02-06
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        相关资源
        最近更新 更多