Использование системных вызовов для реализации команды unix cat

Для моего класса ОС у меня есть задание реализовать команду 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);

    }

}

Примеры, которые я видел, показывают чтение только с известным количеством байтов. Я не знаю, сколько байтов будет иметь каждый из прочитанных файлов, так как мне указать последний параметр чтения?


person andandandand    schedule 05.10.2010    source источник
comment
Мой ответ на ваш предыдущий вопрос может быть уместно в вашем стремлении реализовать cat, используя только системные вызовы.   -  person jschmier    schedule 05.10.2010


Ответы (5)


  • Прежде чем вы сможете 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;
}
person Roland Illig    schedule 05.10.2010
comment
ошибка и выход не имеют значения. следует избегать только printf и scanf. - person andandandand; 05.10.2010
comment
@omgzor - тогда вы действительно не реализуете команду cat Unix исключительно с помощью системных вызовов - person jschmier; 05.10.2010
comment
Я все равно удалил их, чтобы избежать дальнейшего упоминания об этом в ответах. - person andandandand; 05.10.2010

Вам нужно прочитать столько байтов, сколько поместится в буфере. Прямо сейчас у вас еще нет буфера, все, что у вас есть, это указатель на буфер. Это ничем не инициализировано. Курица и яйцо, поэтому вы также не знаете, сколько байтов читать.

Создайте буфер.

person Hans Passant    schedule 05.10.2010
comment
С каким размером? c = (char *) calloc(100, sizeof(char)); - person andandandand; 05.10.2010
comment
Это то, что есть у меня в руководстве. Он указывает 100 символов. Если вы не знаете, сколько символов в файле, что бы вы написали в calloc? - person andandandand; 05.10.2010
comment
Обычно вы можете извлечь выгоду из большего буфера, но 100 подойдет. Просто используйте цикл для чтения фрагмента в этот буфер и запишите этот фрагмент в стандартный вывод (read() сообщит вам, сколько он фактически заполнил буфер, поэтому не записывайте больше этого в каждом цикле) - person nos; 05.10.2010
comment
Какой размер вам нравится. Большой буфер: быстрее, но жирнее. Небольшой буфер: компактнее, но медленнее. - person Amadan; 05.10.2010
comment
calloc не является системным вызовом. - person Roland Illig; 05.10.2010

Обычно нет необходимости читать весь файл залпом. Хороший способ выбрать размер буфера, равный или кратный размеру страницы памяти основной операционной системы. 1 или 2 размера страницы, вероятно, достаточно.

Использование слишком больших буферов может на самом деле привести к ухудшению работы вашей программы, потому что они создают нагрузку на систему виртуальной памяти и могут вызвать подкачку.

person nategoose    schedule 05.10.2010

Вы можете использовать open, fstat, mmap, madvise и write для создания очень эффективной команды cat.

Если это специфично для Linux, вы можете использовать open, fstat, fadvise и splice, чтобы сделать еще более эффективную команду cat.

Вызовы совета должны указать флаги SEQUENTIAL, которые сообщат ядру о необходимости агрессивного упреждающего чтения файла.

Если вы хотите быть вежливым с остальной частью системы и свести к минимуму использование буферного кеша, вы можете сделать свою копию кусками по 32 мегабайта или около того и использовать флаги DONTNEED для уже прочитанных частей.

Примечание:

Вышеприведенное будет работать только в том случае, если источником является файл. Если fstat не может предоставить размер, вы должны вернуться к использованию выделенного буфера и read, write. Вы также можете использовать splice.

person Zan Lynx    schedule 05.10.2010

Используйте функцию stat, чтобы определить размер ваших файлов, прежде чем вы их прочитаете. Кроме того, вы можете читать фрагменты, пока не получите EOF.

person nmichaels    schedule 05.10.2010