Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Утилита lsof - инструмент администратора

(C) В.А.Костромин

Во время работы в Linux мне иногда приходилось сталкиваться с одной проблемкой. Установил я, скажем, диск CD-ROM в привод, смонтировал его, потом в процессе работы запустил кучу разных приложений, про CD в приводе уже и забыл. Затем мне потребовалось что-то прочитать с другого диска, я жму на кнопочку извлечения диска на панели CD-ROM, однако реакции никакой не следует. А в ответ на команду

[user]$ umount /mnt/cdrom/
выдается такое сообщение:
umount: /mnt/cdrom: device is busy
Приложений у меня открыто много, так что я уже и не помню, какое из них открыло файлы на CD и тем самым препятствует размонтированию диска. И иногда даже последовательный перебор всех открытых окон не позволял понять, что же надо закрыть. Я помнил, правда, что есть статья С.Лапшанского с описанием решения аналогичной проблемы, да все руки не доходили заняться этим. Но вот надоело мне в конце концов все время наступать на одни и те же грабли, и решил я разобраться с CD-ROM и его размонтированием. Вначале просмотрел я свой каталог на сайте http://rus-linux.net и статью Лапшанского [2] разыскал, но оказалось, что рассматривается в ней аналогичная проблема во FreeBSD, и решается она с помощью утилиты fstat. Я, конечно, попытался вызвать эту команду в своей системе на основе дистрибутива ASP Linux 7.3, но такой команды в моей системе не обнаружилось. Команда man fstat сообщила, что есть такой системный вызов fstat, который выдает информацию об указанном файле, но писать собственную утилиту на основе этого системного вызова мне, прямо признаюсь, не по силам.

Сейчас я даже уже и не вспомню, откуда я это знал, но где-то в глубинах подсознания мелькнула мысль, что для решения этой проблемы можно использовать утилиту lsof. Кстати, один из читателей моей книги [3] в своем отзыве на книгу в качестве недостатка указал на то, что эта утилита в книге не описана. Я и решил попытаться применить lsof для решения своей задачи, и начал, естественно, с изучения справки, выдаваемой командой man lsof. А кроме того, поискал информацию в Интернет и нашел статьи [4-8]. Все эти материалы и послужили основой для настоящей статьи.

Для чего служит команда lsof

Относительно lsof справка man как раз сообщает, что lsof есть сокращение от LiSt of Open Files, и что утилита эта служит для вывода информации о том, какие файлы используются теми или иными процессами. Причем утилита эта имеется в очень многих версиях и диалектах UNIX, включая Linux версии 2.1.72 и выше, а также в HP-UX, AIX, NextStep, Apple Darwin для Power Macintosh, SCO UnixWare, Solaris, FreeBSD, NetBDS, OpenBSD и так далее.

Создателем программа lsof является Victor A. Abell, его домашняя страничка расположена по адресу http://people.freebsd.org/~abe/, где вы можете найти исходные коды программы. Их можно также скачать с FTP-сайта ftp://vic.cc.purdue.edu/pub/tools/unix/lsof. О том, как скомпилировать и установить программу, вы можете прочитать в статье [8].

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

Кстати, если вы будете экспериментировать, имейте в виду, что будучи запущенной простым пользователем, эта команда выдает информацию только о процессах, запущенных этим пользователем. Поэтому во многих случаях вы можете не получить от нее никакого ответа. Если вы хотите получить вразумительный ответ на любой свой запрос, ее необходимо запускать от имени root-а (можно воспользоваться следующей формой запуска sudo lsof). Кроме того, команда отрабатывает достаточно медленно, так что будьте терпеливы и не щелкайте раньше времени <Ctrl>+<C> - программа не зависла, просто идет поиск информации.

А теперь смотрим листинг 1 (или результат работы программы на вашем экране).

Листинг 1. Вывод команды lsof.

COMMAND     PID    USER   FD   TYPE     DEVICE     SIZE       NODE NAME
init          1    root  cwd    DIR        3,3     4096          2 /
init          1    root  txt    REG        3,3    27036     161176 /sbin/init
init          1    root  mem    REG        3,3   103044     160493 /lib/ld-2.3.2.so
init          1    root   10u  FIFO        3,3               71954 /dev/initctl
portmap    1528     rpc    3u  IPv4       1656                 UDP *:sunrpc 
xinetd     1649    root    0r   CHR        1,3               65970 /dev/null
rpc.mount  1682    root    5u  unix 0xc34cea80               10311 socket
nmbd       1790    root    6u  IPv4       2456                 UDP *:netbios-ns 
sh        23252     kos    4u   CHR        5,0               70758 /dev/tty
sh        23252     kos    6u   CHR      136,3                   5 /dev/pts/3
sh        23252     kos    8w  FIFO        0,5              840800 pipe
sh        23253     kos  cwd    DIR        3,3     4096     160487 /usr/share/man
sh        23253     kos  rtd    DIR        3,3     4096          2 /
sh        23253     kos  txt    REG        3,3   626028     160664 /bin/bash

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

  • cwd - текущий рабочий каталог;
  • ltx - текст разделяемой библиотеки;
  • mxx - hex memory-mapped type number xx.
  • m86 - DOS Merge mapped file;
  • mem - файл, загруженный в память (memory-mapped file), чаще всего – библиотека,
  • mmap - memory-mapped device;
  • pd - родительский каталог;
  • rtd - корневой каталог;
  • txt – текст программы (код и данные);
  • v86 - VP/ix mapped file.

Номер файлового дескриптора сопровождается символом, указывающим режим, в котором файл был открыт:

  • r - файл открыт для чтения;
  • w – файл открыт для записи;
  • u - файл открыт для чтения и для записи;
  • пробел – режим доступа неизвестен и файл не блокирован;
  • ‘-’ - режим доступа неизвестен, но на файл установлена блокировка.

В последнем случае за дефисом следует еще один символ, определяющий тип блокировки (подробнее смотри man lsof).

В следующем столбце указан тип файла. Чаще других встречаются файлы одного из следующих типов: REG - обычный файл, DIR - каталог, BLK - файл блочного устройства, CHR - файл символьного устройства, LINK - файл символической ссылки, INET – Internet-сокет, UNIX – доменный сокет UNIX. Но на справочной странице список типов гораздо обширнее.

Вслед за типом указывается устройство, на котором расположен файл, размер файла, номер индексного дескриптора и имя файла.

Опции команды

Поскольку просматривать вывод команды lsof, запущенной без параметров, достаточно неудобно, можно сократить объем выдаваемых данных, конкретизировав ваш запрос, что достигается за счет использования опций. Опций у этой утилиты довольно много и большинство из них действует по принципу ограничения вывода. Если вы указываете, например, опцию -U, то будут выведены только данные о сокетах UNIX, а все другие файлы игнорируются. Если указать две или более опций, то их действие определяется правилом ИЛИ, то есть выводится информация, определяемая каждой из опций. Например, команда

/usr/sbin/lsof -U -u kos

выдаст данные о всех открытых UNIX-сокетах и всех файлах, принадлежащих процессам, запущенным пользователем “kos”.

Если же необходимо скомбинировать действие опций по принципу логического И, то это делается путем специальной опции -a. Например, команда

/usr/sbin/lsof -a -U -u kos

выдаст список только сокетов UNIX, принадлежащих процессам, владельцем которых является пользователь “kos”'. Обратите внимание на то, что опция -a стоит не между объединяемыми ею другими опциями. Впрочем, ее можно ставить где угодно, все равно все перечисленные в командной строке опции будут работать по принципу “логическое И”.

Но если указывается, например, несколько однотипных опций, то вначале к ним применяется операция логического ИЛИ, а затем уже работает опция -a. Пример: по команде

/usr/sbin/lsof -i@aaa.bbb -i@ccc.ddd -a -ufff,ggg

будет выведен список файлов, принадлежащих ЛИБО пользователю “fff”, ЛИБО пользователю “ggg” И имеющих сетевые соединения ЛИБО к хосту aaa.bbb ЛИБО к хосту ccc.ddd.

Опции команды lsof можно записывать одну за другой без пробелов, предваряя это список всего одним дефисом, то есть вместо

/usr/sbin/lsof -a -b -C

можно указать просто

/usr/sbin/lsof -abC

Однако при этом надо побеспокоиться о том, чтобы не было неоднозначности в интерпретации опций. Но я не буду детально рассматривать возможные причины неоднозначности, если у вас возникает желание использовать эту возможность, смотрите соответствующую man-страницу.

Отмечу только, что существует ряд опций, которые позволяют изменить вывод программы lsof. Например, опция -R заставляет lsof дополнительно выводить данные об идентификаторе родительского процесса. Очень интересной может оказаться опция -F. Запущенная с этой опцией утилита lsof выдает информацию не в виде таблицы, а в виде последовательности отдельных строк. В таком случае вывод можно перенаправить на вход другой программы, которая будет каким-либо образом обрабатывать полученную информацию. После опции -F можно указать, какие именно поля будут присутствовать в выводе. Например, если командная строка имела вид

[user]$ /usr/sbin/lsof -F pcfn

в выводе lsof будет присутствовать идентификатор процесса (p), имя команды (c), файловый дескриптор (f) и ися файла.

Применение команды lsof

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

Пример 1. Кто работает с файлом или каталогом?

Начнем с решения той проблемы, которая была описана в начале статьи, и которая рассматривалась в упомянутой выше статье С.Лапшанского [2]. Итак, вы попытались размонтировать CD-ROM (захотели сменить диск). А в ответ получили сообщение “Device is busy”. Это означает, что какой-то процесс открыл файл, расположенный на этом устройстве. А чтобы узнать, какие файлы открыты в том или ином каталоге, служит опция +d команды lsof. Поэтому, чтобы узнать, кем занят диск, дайте команду следующего вида:

[user]$ /usr/sbin/lsof +d /mnt/cdrom

(у меня CD-ROM монтируется в каталог /mnt/cdrom). В ответ я получил такое сообщение:

COMMAND  PID USER   FD   TYPE DEVICE SIZE  NODE NAME
mc      1547  kos  cwd    DIR   11,0 2048 53248 /mnt/cdrom
bash    1556  kos  cwd    DIR   11,0 2048 53248 /mnt/cdrom

Как видите, сразу становится ясно, что дисковод используется пользователем kos, причем, видимо, какой-то подкаталог каталога /mnt/cdrom открыт в одной из панелей файлового менеджера Mifnight Commander (mc).

Надо отметить, что при использовании опции +d dir выдается информация только о тех файлах и подкаталогах, которые содержатся в самом каталоге dir, а не в его подкаталогах. Для решения рассматриваемой проблемы этого вполне достаточно, потому что если обычно в выводе команды указан и файл данного каталога. Но, если вы все же захотите получить список всех открытых файлов во всех вложенных подкаталогах каталога dir, надо воспользоваться опцией +D. Попробуем, например, посмотреть, какие файлы открыты в каталоге /var/ и всех его подкаталогах. В работающей системе всегда найдутся открытые файлы из этого каталога, поэтому на этом примере очень наглядно демонстрируется разница между опциями +d и +D:

[root]# /usr/sbin/lsof +D /var/

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

Если вы почему либо хотите указать не имя каталога или файла, а номер файлового дескриптора, вы можете воспользоваться командой lsof -f fd, где fd есть список файловых дескрипторов, разделенных запятыми. В списке можно использовать диапазоны: 1-3,5 и те буквенные сочетания, которые были упомянуты выше, например, cwd,ltx,mem.

Между прочим, когда я экспериментировал с командой lsof, выяснилось, что файловый менеджер Konqueror не “держит” устройство: несмотря на то, что в Konqueror открыт каталог, расположенный на примонтированном CD, операция размонтирования устройства выполняется без проблем. По-видимому, Konqueror (как, вероятно, и другие браузеры) скачивает файл и закрывает соединение (файл).

Пример 2. Какой процесс использует данный файл?

[root]# /usr/sbin/lsof /etc/passwd

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

Пример 3. Какие ресурсы использует процесс?

Вы уже видели в листинге 1, что каждый процесс открывает несколько файлов. Но когда я выполнил команду

[root]# /usr/sbin/lsof -c mc

я был поражен тем, как много файлов открыто этим процессом. Впрочем, я привел этот пример не для того, чтобы сообщить о своем удивлении. Это пример просто иллюстрирует применение опции - с string в команде lsof. Запущенная с этой опцией lsof выводит список файлов, отрытых процессами, выполняющими команды, название которых начинается строкой string (один или несколько символов).

Но строка string может соответствовать нескольким запущенным в данный момент командам. Если вы хотите конкретизировать ваш запрос, вы можете воспользоваться другой опцией: -p pid. Например:

[root]# /usr/sbin/lsof -p 409

Команда lsof -p pid выводит список файлов, открытых процессами, чьи идентификаторы (PID) находятся в списке pid, например, “3593” или “823,451” (отдельные идентификаторы разделяются запятыми, пробелы в списке недопустимы). С помощью этой команды вы сможете увидеть, например, какие библиотеки используются процессом. Номер нужного PID можно извлечь из результатов выполнения команды /usr/sbin/lsof -c string.

Аналогичным образом можно вывести список файлов, открытых процессами, чьи идентификаторы группы (PGID) находятся в разделенном запятыми списке gid. Это позволяет сделать команда lsof -g gid.

Пример 4. С какими файлами работает пользователь?

Следующий вариант запуска той же команды:

[root]# /usr/sbin/lsof -u names

позволяет вывести список файлов, открытых всеми процессами, запущенными пользователями, чье имя или идентификатор содержится в списке names. Отдельные элементы списка разделяются запятыми, например, ”548,root” (пробелов быть не должно).

Пример 5. Кто подключился к вашему компьютеру?

Команда lsof очень полезна в том случае, когда вы хотите обеспечить безопасность работы вашего компьютера в сети. С ее помощью вы можете определить, какие порты открыты для приема входящих соединений и какая программа прослушивает каждый порт. После это можно закрыть ненужные порты, чтобы минимизировать риск проникновения в систему злоумышленника. Для того, чтобы узнать, какие процессы в данный момент прослушивают сетевые соединения, воспользуйтесь командой lsof с опцией -i :

[root]# /usr/sbin/lsof -i 

На листинге 2 показан результат выполнения этой команды на компьютере trend в моей домашней сети, состоящей всего из двух компьютеров.

Листинг 2.

COMMAND    PID USER   FD   TYPE DEVICE SIZE NODE NAME
portmap    557 root    3u  IPv4    969       UDP *:sunrpc 
portmap    557 root    4u  IPv4    972       TCP *:sunrpc (LISTEN)
rpc.statd  585 root    4u  IPv4   1029       UDP *:32768 
rpc.statd  585 root    6u  IPv4   1032       TCP *:32768 (LISTEN)
sshd       727 root    3u  IPv4   1176       TCP *:ssh (LISTEN)
lpd        783 root    6u  IPv4   1258       TCP *:printer (LISTEN)
rpc.rquot  813 root    3u  IPv4   1303       UDP *:990 
rpc.rquot  813 root    4u  IPv4   1308       TCP *:imaps (LISTEN)
rpc.mount  818 root    3u  IPv4   1321       UDP *:32769 
rpc.mount  818 root    4u  IPv4   1324       TCP *:32769 (LISTEN)
httpd      873 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      877 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      881 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      893 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      894 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      895 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      896 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      897 root   16u  IPv4   1422       TCP *:http (LISTEN)
httpd      898 root   16u  IPv4   1422       TCP *:http (LISTEN)
smbd      1300 root    9u  IPv4   1839       TCP *:netbios-ssn (LISTEN)
nmbd      1305 root    6u  IPv4   1842       UDP *:netbios-ns 
nmbd      1305 root    7u  IPv4   1843       UDP *:netbios-dgm 
nmbd      1305 root    8u  IPv4   1845       UDP trend.home.kos:netbios-ns 
nmbd      1305 root    9u  IPv4   1846       UDP trend.home.kos:netbios-dgm 
X         1413 root    1u  IPv4   1972       TCP *:x11 (LISTEN)
httpd     2190 root   16u  IPv4   1422       TCP *:http (LISTEN)
sshd      2192 root    4u  IPv4  35684       TCP trend.home.kos:ssh->old-lin.home.kos:1031 (ESTABLISHED)
sshd      2192 root    8u  IPv4  35704       TCP localhost.localdomain:x11-ssh-offset (LISTEN)
ssh       2464 root    3u  IPv4  71018       TCP trend.home.kos:32772->old-lin.home.kos:ssh (ESTABLISHED)

Порты, которые открыты для входящих соединений, обозначены меткой LISTEN. Метка ESTABLISHED показывает, что по данному порту соединение установлено. В листинге 2 вы можете видеть пример двух SSH-сессий. Первая сессия установлена хостом old-lin.home.kos с компьютером trend.home.kos. Она обслуживается процессом с PID 2192, который был порожден основным демоном sshd, PID 727. Процесс с PID 2464, наоборот, соответствует соединению, установленному клиентской программой ssh на компьютере trend.

Поскольку и в этом случае количество информации, выдаваемой командой lsof с опцией -i может оказаться очень велико, можно конкретизировать запрос. Для этого нужно использовать команду в следующем формате:

[root]# /usr/sbin/lsof -i adress

где после опции -i указывается еще параметр – интересующий вас порт, сервис или имя хоста. В общем случае параметр adress задается в следующем виде (квадратные скобки обозначают необязательность каждой части параметра):

[46][protocol][@hostname|hostaddr][:service|port]

Здесь “46” обозначает версию протокола IP. Цифру 6 можно указывать только в том случае, если ваше ядро поддерживает IPv6. Если не указано ни 4, ни 6, будут рассматриваться соединения по тому и другому варианту протокола. Задавать версию протокола можно указывая опцию в форме -i4 -i6 (это то же самое, что и указать просто -i). Если указывается только версия IP-протокола без указания других частей параметра address, отображаются только данные о тех процессах и файлах, которые работают по соответствующему протоколу.

Вместо “protocol” в параметре может стоять либо TCP, либо UDP (либо ничего).

“hostname” - имя хоста, а “hostaddr” - числовой адрес. В случае протокола IPv4 эта часть задается в форме десятично-точечной записи, в случае IPv6 – в форме чисел, разделенных двоеточиями и заключенных в скобки.

“service” - это название одного из сервисов, например, smtp, или список таких сервисов.

“port” - это номер порта или список таких номеров.

Чтобы сказанное стало понятнее, приведем несколько простых примеров.

[root]# lsof -i :80

В этом случае будет выдан список всех процессов, прослушивающих или установивших соединение через порт 80.

[root]# lsof -i :smtp

В этом примере будут перечислены все соединения, ассоциированные с почтовым протоколом SMTP.

[root]# lsof -i @rus-linux.net

В третьем случае будет выдан список всех входящих и исходящих соединений с хостом rus-linux.net.

[root]# lsof -i UDP@rus-linux.net:123

Эта команда покажет, какие процессы установили UDP-соединения с хостом rus-linux.net через порт 123 (ntp).

Очевидно, что указание параметра adress полезно только в том случае, когда вы заранее знаете, что именно вам нужно искать. Вы можете таким образом проверить, например, что какой-то конкретный сервис фактически работает и какой порт он прослушивает. Вы можете увидеть, подключился ли кто-то извне к вашему компьютеру по таким протоколам как SSH, Telnet, FTP или другим возможным способом.

Пример 6. Кто открыл файлы по NFS?

Если вы хотите узнать, открыты ли какие-то файлы, предоставленные удаленным пользователям по протоколу NFS, используйте опцию -N.

Пример 7. Наблюдаем за процессом копирования

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

[user]$ /usr/sbin/lsof | grep Photo

При этом нужно иметь в виду, что команда lsof делает как-бы “мгновенный снимок”, отображающий только лишь состояние процессов и соответствующих файлов в момент ее выполнения. Так что для “наблюдения за процессом” надо повторять ее запуск многократно. И в таком случае может оказаться полезной опция +r, которая служит для периодического повторения запуска команды через заданный промежуток времени.

Заключение

В конце статьи можно сформулировать следующий вывод: поскольку файл и процесс – два ключевых понятия в операционной системе, утилита lsof может оказаться незаменимым помощником администратора в тех случаях, когда надо разобраться, как работает система, а особенно в тех случаях, когда система скомпрометирована. И она тем полезнее, чем больше ваша система и чем больше вы с ней работаете, чем лучше вы понимаете, что она может. Когда вы освоите ее возможности, вы сможете создать отдельные скрипты для обработки тех огромных массивов информации, которые выдает эта программа. Можно, например, запускать команду lsof -i через определенные промежутки времени, чтобы сравнивать полученные данные. Если будут выявлены отличия в числе открытых процессов или другие отличия, удовлетворяющие заданному критерию, скрипт может сформировать сообщение администратору.

Мы рассмотрели только малую часть возможностей этой утилиты. Более подробную информацию ищите на соответствующей man-страничке.

Можно еще отметить, что для lsof разработана графическая оболочка, называемая GLSOF. Ее можно получить с сайта http://glsof.sourceforge.net/.

Ссылки:

  1. http://rus-linux.net
  2. С.Лапшанский, “Кто использует эти файлы”, перевод статьи Майкла Лукаса (URL: http://www.computerra.ru/softerra/freeos/22483/x).
  3. В.А.Костромин, “Linux для пользователя”, БХВ-Петербург, 2002 г.
  4. “Meet the Amazing Mr. Lsof” (URL: http://www.netadmintools.com/art176.html)
  5. Indiana University, Unix Workstation Support Group, "Unix for Advanced Users”. 15.9. List Open Files: lsof and fuser (URL: http://www.uwsg.iu.edu/UAU/uau.html).
  6. Thomas Nooning, “Track network connections with LSOF on Linux” (URL: http://www.builderau.com.au/program/unix/0,39024638,20268166,00.htm.
  7. Martin Zahn, “Introduction to lsof”, (URL: http://www.akadia.com/services/lsof_intro.html).
  8. “Installing, configuring and using lsof 4.50 to list open files on systems running Solaris 2.x” (URL: http://www.cert.org/security-improvement/implementations/i042.05.html)