Подтвердить что ты не робот

C, sendfile() и send()?

sendfile() копирует данные между двумя файловыми дескрипторами в пространстве ядра. Где-то я видел, если вы пишете веб-сервер в C linux, вы должны использовать send() и recv() вместо использования write() и read(). Так что send() также использует пространство ядра?

Что бы я ни использовал для отправки - sendfile() или send() - на стороне клиента я буду использовать recv() правильно?

С другой стороны, справочная страница говорит: "Единственная разница между send() и write (2) - наличие флагов. С аргументом нулевого флага, send() эквивалентно записи (2)".

4b9b3361

Ответ 1

Если fd - дескриптор файла сокета, то эти системные вызовы идентичны:

  • send(fd, data, length, 0) совпадает с write(fd, data, length)
  • recv(fd, data, length, 0) совпадает с read(fd, data, length)

Итак, если вам не нужно задавать ненулевой параметр flags, не имеет значения, используете ли вы send/recv или write/read.

Системный вызов sendfile - это оптимизация. Если у вас есть сокет sockfd и обычный файл filefd, и вы хотите скопировать некоторые данные файла в сокет (например, если вы являетесь веб-сервером, обслуживающим файл), вы можете написать его следующим образом:

// Error checking omitted for expository purposes
while(not done)
{
    char buffer[BUFSIZE];
    int n = read(filefd, buffer, BUFSIZE);
    send(sockfd, buffer, n, 0);
}

Однако это неэффективно: это связано с тем, что ядро ​​копирует данные файла в пользовательское пространство (в вызове read), а затем копирует одни и те же данные обратно в пространство ядра (в вызове send).

Системный вызов sendfile позволяет нам пропустить все это копирование и заставить ядро ​​напрямую прочитать данные файла и отправить его в сокет одним махом:

sendfile(sockfd, filefd, NULL, BUFSIZE);

Ответ 2

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

Также вы должны отметить:

Аргумент in_fd должен соответствовать файлу, который поддерживает mmap (2) -подобные операции (т.е. это не может быть сокет).

Это означает, что вы не можете копировать из сокета (вы можете скопировать в сокет и до 2.6.33 вы должны скопировать в сокет).

Ответ 3

send указанный стандартом POSIX, в котором говорится:

Функция send() эквивалентна sendto() с нулевым указателем dest_len и write(), если флаги не используются.

sendfile зависит от Linux. Он сообщает ядру делать операции ввода-вывода с нулевой копией из файла в сокет. (Обратите внимание, что он работает только тогда, когда исходный файл fd является файлом, а местом назначения является сокет; для универсального ввода-вывода с нулевой копией для Linux читайте о splice().)

Обратите внимание, что редко приходится использовать Linux-специфический ввод-вывод с нулевой копией. Стандартный и переносимый цикл read + write (или send) с небольшим буфером пользовательского пространства (8K-16K) обычно сохраняет этот буфер в кеше L1, что делает его эквивалентным "нулевой копии" из системного ОЗУ точки зрения.

Поэтому, если ваше профилирование не показывает разницу для вашего конкретного приложения, придерживайтесь стандартных интерфейсов. Просто MHO.