При закрытии подписчики были переданы в рассылку "LinuxCenter News Channel: новости Linux" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Информационный Канал Subscribe.Ru |
Linux Gazette на русском | Выпуск #181 | Тираж 10654 экз.
Здравствуйте! Вашему вниманию предлагается перевод статьи,
которая может представлять интерес для начинающих программистов. В
ней описан пример использования системного вызова sendfile,
являющегося эффективным механизмом передачи файлов по сети и
представляющего интерес для тех, кто занимается разработкой сетевых
приложений.
Спасибо за перевод Андрею Киселёву!
Александр Куприн
Приложения-серверы, такие как web-серверы, тратят огромное количество времени на передачу файлов, хранящихся на жестком диске, клиентам, работающим с сервером через web-браузер. Простой алгоритм передачи данных может выглядеть примерно так:
открыть исходный файл (на диске) открыть файл назначения (сетевое соединение) пока файл не передан: прочитать блок данных из исходного файла в буфер записать данные из буфера в файл назначения закрыть оба файла
Процедуры чтения и записи данных обычно используют системные вызовы read и write, соответственно, либо библиотечные функции, которые являются своего рода "обертками" для этих системных вызовов.
Если следовать вышеприведенному алгоритму, то получается так, что данные копируются несколько раз, прежде чем они "уйдут" в сеть. Каждый раз, когда вызывается read, данные копируются с жесткого диска в буфер ядра (обычно посредством DMA). Затем буфер копируется в буфер приложения. Затем вызывается write и данные из буфера приложения опять копируются в буфер ядра и лишь потом этот буфер отправляется в сеть. Каждый раз, когда приложение обращается к системному вызову, происходит переключение контекста между пользовательским режимом и режимом ядра, а это весьма "дорогостоящая" операция. И чем больше в программе будет обращений к системным вызовам read и write, тем больше времени будет потрачено на выполнение переключений контекста исполнения.
Операции копирования данных из области ядра в область приложения и обратно, в данном случае, излишни, поскольку сами данные в приложении не изменяются и не анализируются. Многие операционные системы, такие как Windows NT, FreeBSD и Solaris предоставляют в распоряжение программиста системный вызов, который выполняет передачу файла за одно обращение. Ранние версии Linux часто критиковали за отсутствие подобной возможности, в результате, начиная с версии 2.2.x, такой вызов появился. Теперь он широко используется такими серверными приложениями как Apache и Samba.
Реализация sendfile различна для разных операционных систем. Поэтому, в данной статье мы будем говорить о версии sendfile в Linux. Обратите внимание: утилита sendfile не то же самое, что системный вызов sendfile.
Чтобы использовать sendfile в своих программах, вы должны подключить заголовочный файл <sys/sendfile.h>, в котором находится описание прототипа функции-вызова:
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);Функция принимает следующие входные параметры:
В Linux файловый дескриптор может соответствовать как обычному файлу так и устройству, например -- сокету. На сегодняшний день, реализация sendfile требует, чтобы исходный файловый дескриптор соответствовал обычному файлу или устройству, поддерживаемому mmap. Это означает, например, что исходный файл не может быть сокетом. Файл назначения может быть и сокетом, и это обстоятельство широко используется приложениями.
Рассмотрим простой пример работы с системным вызовом sendfile. В листинге ниже приведен текст программы fastcp.c, которая выполняет простое копирование файла.
С целью упрощения листинг сильно сокращен. Полный текст программы вы можете взять здесь. В него включен дополнительный код проверки ошибок и директивы препроцессора, необходимые для компиляции.
Листинг 1: fastcp.c 1 int main(int argc, char **argv) { 2 int src; /* дескриптор исходного файла */ 3 int dest; /* дескриптор файла назначения */ 4 struct stat stat_buf; /* сведения об исходном файле */ 5 off_t offset = 0; /* смещение от начала исходного файла */ 6 7 /* проверить -- существует ли исходный файл и открыть его */ 8 src = open(argv[1], O_RDONLY); 9 /* запросить размер исходного файла и права доступа к нему */ 10 fstat(src, &stat_buf); 11 /* открыть файл назначения */ 12 dest = open(argv[2], O_WRONLY|O_CREAT, stat_buf.st_mode); 13 /* скопировать файл */ 14 sendfile (dest, src, &offset, stat_buf.st_size); 15 /* закрыть файлы и выйти */ 16 close(dest); 17 close(src); 18 }
В строке 8 открывается исходный файл, имя которого передается программе, как первый аргумент командной строки. В строке 10 программа получает дополнительные сведения о файле, с помощью fstat, таким образом мы получаем длину файла и права доступа к нему, которые понадобятся нам позднее. В строке 12 открывается на запись файл назначения. В строке 14 производится вызов sendfile, которому передаются файловые дескрипторы, смещение от начала исходного файла (в данном случае -- 0) и количество байт для копирования, которое соответствует размеру исходного файла. И в строках 16 и 17, после выполнения копирования, файлы закрываются.
Попробуйте скомпилировать полную версию программы и поэкспериментируйте с файлами различного типа и посмотрите -- как работает с ними вызов sendfile:
Первый пример достаточно прост, но он не дает представления о том, как выполнять копирование в сокет. Второй пример показывает как выполняется операция передачи файла через сеть. Текст программы достаточно велик, поскольку в данном случае она включает код инициализации и работы с сокетами, и потому он не включен в текст статьи. Полный текст примера вы сможете взять здесь.
Программа называется server и выполняет следующие действия:
Программа "server" ожидает подключений на порту 1234. Номер порта выбран совершенно случайно и для себя вы можете выбрать другой номер, указав его в командной строке. Запустите программу командой "./server". В качестве клиентского приложения можно использовать программу telnet. Запустите ее из другой консоли, не забыв при этом указать имя хоста и номер порта (например, "telnet localhost 1234"). После появления сообщения об установке соединения, введите имя существующего файла, например /etc/hosts. Программа server должна передать содержимое файла клиенту и закрыть соединение.
По завершении сеанса связи программа server остается работать, в результате вы опять сможете подключиться к ней и запросить другой файл. Если программе передать в качестве имени файла слово "quit", то она завершит свою работу. Если в вашем распоряжении есть две машины, объединенные сетью, то можете попробовать выполнить передачу файлов через сеть.
Обратите внимание: данная реализация сервера очень упрощена -- программа может работать только с одним клиентом одновременно, а проверка на ошибки очень поверхностна. Кроме того, отсутствует оптимизация работы с уровнем TCP, поскольку данный вопрос далеко выходит за рамки статьи.
Системный вызов sendfile значительно повышает скорость передачи файлов по сети и широко используется сетевыми приложениями, такими как ftp и web серверы. Если вы занимаетесь разработкой серверных приложений, то вам определенно пригодится этот системный вызов. Если нет -- то при творческом подходе к проблеме, вы сможете самостоятельно найти весьма интересные применения этому вызову.
В заключение обсуждения системного вызова sendfile, я предлагаю вам ответить на вопрос: "Почему нет соответствующего вызова с именем receivefile?".
Джефф (Jeff) пишет о Linux и для Linux начиная с 1992 года. Он работает в корпорации Xandros в столице Канады -- городе Оттава.
Команда переводчиков:
Александр Куприн, Александр Михайлов, Александр Саввин, Андрей Киселев,
Андрей Романчев, Владимир Меренков, Владимир Средних, Иван Песин, Игорь Яровинский, Павел Соколов,
Роман Шумихин, Сергей Скороходов, Юрий Прушинский, Юрий Султанов
Со всеми предложениями, идеями и комментариями обращайтесь к Александру Куприну (ru_classic at mail.ru). Убедительная просьба: указывайте сразу, не возражаете ли Вы против публикации Ваших отзывов в рассылке.
Архив рассылки: http://gazette.linux.ru.net/mirror/
Сайт рассылки: http://gazette.linux.ru.net
Перевод можно найти по адресу: http://gazette.linux.ru.net/lg91/tranter.html
http://subscribe.ru/
http://subscribe.ru/feedback/ |
Адрес подписки |
Отписаться |
В избранное | ||