Ошибка при открытии команды при попытке открыть очень большой файл - PullRequest
3 голосов
/ 15 января 2010

Я посещаю сетевой урок в школе и впервые использую C / GDB. Наша задача - создать веб-сервер, который будет взаимодействовать с клиентским браузером. Я в процессе и могу открывать файлы и отправлять их клиенту. Все идет хорошо, пока я не открою очень большой файл, а затем я обнаружил ошибку. Я не профессионал в C / GDB, поэтому мне жаль, если это заставляет меня задавать глупые вопросы и я не могу сам увидеть решение, но когда я смотрю на выброшенное ядро, я вижу, что моя ошибка сегмента приходит сюда:

if (-1 == (openfd = open(path, O_RDONLY)))

В частности, перед нами стоит задача открыть файл и отправить его в браузер клиента. Мой алгоритм гласит:

  1. Открыть / Ошибка перехвата
  2. Считать файл в буфер / Ошибка перехвата
  3. Отправить файл

Нам также было поручено убедиться, что сервер не падает при отправке очень больших файлов. Но моя проблема, кажется, с их открытием. Я могу отправить все мои меньшие файлы просто отлично. Размер файла составляет 29,5 МБ.

Весь алгоритм:

ssize_t send_file(int conn, char *path, int len, int blksize, char *mime) {
  int openfd; // File descriptor for file we open at path
  int temp; // Counter for the size of the file that we send
  char buffer[len]; // Buffer to read the file we are opening that is len big

  // Open the file
  if (-1 == (openfd = open(path, O_RDONLY))) {
    send_head(conn, "", 400, strlen(ERROR_400));
    (void) send(conn, ERROR_400, strlen(ERROR_400), 0);
    logwrite(stdout, CANT_OPEN);
    return -1;
  }

  // Read from file
  if (-1 == read(openfd, buffer, len)) {
    send_head(conn, "", 400, strlen(ERROR_400));
    (void) send(conn, ERROR_400, strlen(ERROR_400), 0);
    logwrite(stdout, CANT_OPEN);
    return -1;
  }
  (void) close(openfd);

  // Send the buffer now
  logwrite(stdout, SUC_REQ);
  send_head(conn, mime, 200, len);      
  send(conn, &buffer[0], len, 0);
  return len;
}

Я не знаю, если это просто факт, что я новичок в Unix / C. Извините, если это так. = (Но ваша помощь очень ценится.

Ответы [ 4 ]

4 голосов
/ 15 января 2010

Я подозреваю, что у вас есть переполнение стека (я должен получить бонусные баллы за использование этого термина на этом сайте).

Проблема в том, что вы выделяете буфер для всего файла в стеке одновременно. Для больших файлов этот буфер больше стека, и в следующий раз, когда вы попытаетесь вызвать функцию (и таким образом поместить некоторые параметры для нее в стек), программа завершится сбоем.

Сбой появляется на открытой строке, потому что выделение буфера в стеке фактически не записывает память, а просто меняет указатель стека. Когда ваш вызов open пытается перебрать параметры в стеке, вершина стека теперь переполняется, и это вызывает сбой.

Решение такое, как предлагают Platinum Azure или dreamlax, считывая по кусочкам файла за раз или выделяя буфер в куче, будет malloc или новый.

4 голосов
/ 15 января 2010

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

Разумнее выделить буфер определенного размера, скажем, 8192 байта (ну, в любом случае, это то, чем я обычно занимаюсь), и просто всегда читать и отправлять столько, сколько необходимо, до тех пор, пока вы не прочитаете ( ) операция возвращает 0 (и не определена с ошибкой) для конца потока.

3 голосов
/ 15 января 2010

Вместо использования массива переменной длины, возможно, попробуйте выделить память, используя malloc.

char *buffer = malloc (len);

...

free (buffer);

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

2 голосов
/ 15 января 2010

Вы размещаете буфер в стеке, и он слишком велик.

Когда вы выделяете память в стеке, все, что делает компилятор, - это уменьшает указатель стека настолько, чтобы освободить место (это сохраняет распределение переменных стека постоянным временем). Он не пытается коснуться какой-либо из этой накопленной памяти. Затем, когда вы вызываете open(), он пытается поместить параметры в стек и обнаруживает, что переполнил стек и умирает.

Вам нужно либо работать с файлом в чанках, отображать его в памяти (mmap()) или malloc() хранилище. Также, path должно быть объявлено const char*.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...