C, sendfile() и send()?
sendfile() копирует данные между двумя файловыми дескрипторами в пространстве ядра. Где-то я видел, если вы пишете веб-сервер в C linux, вы должны использовать send() и recv() вместо использования write() и read(). Так что send() также использует пространство ядра?
Что бы я ни использовал для отправки - sendfile() или send() - на стороне клиента я буду использовать recv() правильно?
С другой стороны, справочная страница говорит: "Единственная разница между send() и write (2) - наличие флагов. С аргументом нулевого флага, send() эквивалентно записи (2)".
Ответы
Ответ 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.