- Идентификация открытых файлов и сетевых подключений в Linux — lsof (примеры)
- Вики IT-KB
- Инструменты пользователя
- Инструменты сайта
- Боковая панель
- Содержание
- Как проверить все открытые файлы пользователем или процессом в Linux
- Лимит ядра Linux
- Методика подсчёта открытых файлов
- Примеры получения данных
- Файловые дескрипторы Linux
- Таблица файловых дескрипторов
- Трассировка файлового программного интерфейса
- Файл дескриптор в Linux с примерами
- Тузик
- Файл дескриптор
- Redirect и Pipe
- Программируем уже запущенный процесс
- Немного усложним задачу
Идентификация открытых файлов и сетевых подключений в Linux — lsof (примеры)
Lsof расшифровывает как List Open Files (список открытых файлов). Команда формирует список дескрипторов открытых файлов по процессам и именам файлов. В Линукс все является файлами (очереди, сокеты, директории, устройства и т.д. ). Таким образом, с помощью Lsof, вы можете получить информацию о любых открытых файлов.
1. Введение в lsof
Также работает рекурсивный просмотр:
6. Просмотр файлов открытых конкретным пользователем
Для поиска файлов открытых определенным пользователем, используем ключ -u
Иногда необходимо вывести файлы открытые всеми пользователями, за исключением конкретного. Для этого используется -‘^’
Данная команда выводит список файлов открытых всеми пользователями, за исключением пользователя alex1812
7. Список всех файлов открытых конкретным процессом
Для вывода списка всех файлов открытых конкретным процессов используется ключ -p. Это может быть полезно для получения большей информации о самом процессе.
8. Уничтожить все процессы используемые конкретным пользователем
Если необходимо уничтожить все процессы, имеющие открытые файлы, конкретного пользователя, мы можем использовать ключ -t для просмотра id процессов и уничтожить их с помощью kill —
Данная команда уничтожит все процессы пользователя alex1812, которые имели открытые файлы.
Аналогичным образом, вы можете использовать ключ -t для других целей, например, чтобы получить список id процессов, которые открыли /var/log/syslog
9. Комбинирование ключей
По умолчанию, если вы используете более одного ключа в Lsof, то вывод команды будет объеденен по этим ключам. Например,
В этой команде мы используем два ключа -u и -c, таким образом команда одновременно выводит и список процессов принадлежащих пользователю alex1812, и процессы начинающиеся со слова init (принадлежащих любым пользователям).
Вы можете использовать ключ -a, чтобы совместить вывод по нескольким параметрам, например процессы принадлежащие пользователю alex1812 и начинающиеся с init:
10. Выполнение Lsof в режим повтора
Lsof поддерживает вывод в режиме повтора. То бишь сначала будет выведет список файлов с заданными параметрами, затем задержка на заданный интервал и опять вывод на основе этих же параметров.
Режим повтора может быть включен ключами +r и -r. Если используем +r, то режим повтора будет прерван в случае отсутствия открытых файлов. Ключ -r будет продолжать вывод с заданным интервалом вне зависимости от наличия открытых файлов.
Каждый цикл вывода будет разделен с помощью «=======». Мы также можем указать время задержки для обоих ключей.
В данном примере мы выводим список процессов принадлежащих alex1812 c именем x-session и задержкой повторного вывода в 5 секунд.
Поиск сетевых подключений
Сетевые соединения в Linux также являются файлами. Таким образом, мы можем найти информацию о них с помощью Lsof.
11. Просмотр всех сетевых подключений
Для просмотра всех сетевых подключений используем ключ -i
Вы также можете использовать -i4 и i6 для вывода только подключений по IPv4 или IPv6 соответственно.
12. Список всех сетевых файлов используемых конкретным процессом
В первом случае мы задаем процесс по его id, во втором по имени (по первым символам).
13. Список процессов прослушивающих определенный порт
Для этого используется ключ -i и номер порта через ‘:’
14. Список всех UDP и TCP соединений
Для вывода такого списка используем —
15. Просмотр всех NFS (Network File System) файлов
Для просмотра всех NFS файлов используем ключ -N, например просмотр всех NFS файлов открытых от пользователя alex1812 —
Надеюсь информация данной статьи будет кому-либо полезна, особенно тем, кто не любит читать man’ы =)
Источник
Вики IT-KB
Пошаговые руководства, шпаргалки, полезные ссылки.
Инструменты пользователя
Инструменты сайта
Боковая панель
Содержание
Как проверить все открытые файлы пользователем или процессом в Linux
В некоторых ситуациях на Linux могут возникать ошибки, связанные с превышением лимита использования файловых дескрипторов. Эти лимиты накладываются как самим ядром Linux, так и его программными модулями, например PAM.
Лимит ядра Linux
Узнать текущее значение максимального количества файловых дескрипторов, определяемое ядром Linux можно командой:
Этот лимит может быть изменён без перезагрузки системы (начинает действовать сразу и действует до перезагрузки):
Чтобы требуемое значение использовалось постоянно, то есть действовало и после перезагрузки, его необходимо определить в конфиг.файле /etc/sysclt.conf :
Методика подсчёта открытых файлов
Для получения информации о количестве всех открытых файлов всеми процессами в Linux некоторые «знатоки» предлагают использовать команду типа
Однако такая команда показывает гораздо большее значение, чем всего открыто файлов в системе на данный момент на самом деле. Это связано с тем, что по несколько раз в подсчёт попадают одни и теже открытые файлы, используемые разными процессами.
Поэтому проще для получения общего числа открытых файлов использовать данные ядра Linux
Первое число — общее количество занятых/используемых на данный момент времени файловых дескрипторов.
Второе число — количество выделенных процессам, но не используемых в данный момент дескрипторов.
Третье число — максимальное количество открытых дескрипторов
Примеры получения данных
Получить список TOP-20 процессов с самым большим количеством открытых файловых дескрипторов:
Подсчитать количество открытых файлов в разрезе процессов (в первой колонке будет выведен PID процесса, во второй количество открытых файлов этим процессом):
Посмотреть открытые файловые дескрипторы во всех процессах для отдельно взятого пользователя, например «apache»
Подсчитать количество открытых файлов в каждом процессе для отдельно взятого пользователя:
Тоже самое, только в реальном режиме времени:
Посмотреть открыте файловые дескриптры для отдельно взятого процесса (по PID процесса):
Подсчитать количество файловых дескриптров для отдельно взятого процесса:
Дополнительные источники информации:
Проверено на следующих конфигурациях:
Версия ОС |
---|
Debian GNU/Linux 8.10 (jessie) |
Автор первичной редакции:
Алексей Максимов
Время публикации: 09.06.2018 11:18
Источник
Файловые дескрипторы Linux
Основными операциями, предоставляемыми ядром операционной системы программам (а точнее — процессам) для работы с файлами, являются системные вызовы open read, write и close. В соответствии со своими именами, эти системные вызовы предназначены для открытия и закрытия файла, для чтения из файла и записи в файл. Дополнительный системный вызов ioctl (input output control) используется для управления драйверами устройств и, как следствие, применяется в основном для специальных файлов устройств.
При запросе процесса на открытие файла системным вызовом оpen производится его однократный (относительно медленный) поиск имени файла в дереве каталогов и для запросившего процесса создается так называемый файловый дескриптор (описатель, от англ, descriptor).
Файловый дескриптор «содержит» информацию, описывающую файл, например индексный дескриптор inode файла на файловой системе, номера major и minor устройства, на котором располагается файловая система файла, режим открытия файла, и прочую служебную информацию.
При последующих операциях read и write доступ к самим данным файла происходит с использованием файлового дескриптора (что исключает медленный поиск файла в дереве каталогов).
Файловые дескрипторы пронумерованы и содержатся в таблице открытых процессом файлов, которую можно получить при помощи диагностической программы lsof.
В обратную сторону получить список процессов, открывших тот или иной файл, можно при помощи программ lsof и fuser, что бывает полезно для идентификации программ, «занявших» файловую систему, подлежащую отмонтированию.
Таблица файловых дескрипторов
$ lsof -р $$
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
. . . . . . . . .
bash 17975 john 1u CHR 136,2 0t0 5 /dev/pts/2
# lsof /dev/log
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsyslogd 543 syslog 0u unix 0xefef5680 0t0 1338 /dev/log
# fuser /dev/log
# ps p 543
PID TTY STAT TIME COMMAND
543 ? Sl 0:43 rsyslogd -c5
# lsof /var/log/syslog
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsyslogd 543 syslog lw REG 252,0 29039 26214496 /var/log/syslog
В первом примере из листинга выше показано получение списка файловых дескрипторов (столбец FD) процесса командного интерпретатора bash пользователя john, на котором файловый дескриптор номер 1 описывает открытый на чтение и запись и специальный символьный CHR файл устройства /dev/pts/2.
Во втором примере показано получение информации о процессе, открывшем файловый сокет unix с именем /dev/log (файловый дескриптор номер 0 на чтение и запись u) и обычный файл REG с именем /var/log/sysog (файловый дескриптор номер 1 на запись w).
Пронаблюдать за использованием системных вызовов файлового программного интерфейса в момент выполнения программам позволяет системный трассировщик strace.
Трассировка файлового программного интерфейса
$ date
Вт. окт. 15 18:17:42 MSK % 2018
$ strace -fe open, close, read, write, ioctl date
open(«/etc/localtime», 0_RDONLY|0_CLOEXEC) = 3
$ file /etc/localtime
/etc/localtime: timezone data, version 2, 13 gmt time flags, 13 std time flags, no leap
seconds, 77 transition tines, 13 abbreviation char
[email protected]:
$ ls -la /dev/dvd
lrwxrwxrwx 1 root root 3 окт. 16 18:09 /dev/dvd -> sr0
$ strace -fe open,close, read,write,ioctl eject
. . . . . . . . .
open(«/dev/sr0», O_RDWR|O_N0NBLOCK) = 3
ioctl(3, CDROMEJECT, 0x804cb4e) = 0
closed(3) = 0
[email protected]:
$ strace -fe open, read,write,close,ioctl setleds -L +num +scroll
ioctl(0, KDGKBLED, 0xbfe4f4ff) = 0
ioctl(0, KDGETLED, 0xbfe4f4fe) = 0
ioctl(0, KDSETLED, 0x3) = 0
Предположив, что программа date показывает правильное московское время, потому что узнаёт заданную временную зону MSK из некоего конфигурационного файла операционной системы, при трассировке ее «работы можно установить его точное имя — /etc/localtime.
Аналогично предположив, что программа eject открывает лоток привода CD/DVD при помощи специального файла устройства, при трассировке можно узнать имя файла /dev/sr0, номер файлового дескриптора при работе с файлом 3 и команду CDROMEJECT соответствующего устройству
драйвера ioctl_List.
Трассировка команды setleds показывает, что она вообще не открывает никаких файлов, но пользуется файловым дескриптором о так называемого стандартного потока ввода (прикрепленного, к текущему терминалу) и командами kdgetled и kdsetled драйвера консоли console_ioctl.
Источник
Файл дескриптор в Linux с примерами
Однажды, на одном интервью меня спросили, что ты будешь делать, если обнаружишь неработающий сервис из-за того, что на диске закончилось место?
Конечно же я ответил, что посмотрю, чем занято это место и если возможно, то почищу место.
Тогда интервьюер спросил, а что если на разделе нет свободного места, но и файлов, которые бы занимали все место, ты тоже не видишь?
На это я сказал, что всегда можно посмотреть открытые файл дескрипторы, например командой lsof и понять какое приложение заняло все доступное место, а дальше можно действовать по обстоятельствам, в зависимости от того, нужны ли данные.
Интервьюер прервал меня на последнем слове, дополнив свой вопрос: «Предположим, что данные нам не нужны, это просто дебаг лог, но приложение не работает из-за того, что не может записать дебаг»?
«окей», — ответил я, «мы можем выключить дебаг в конфиге приложения и перезапустить его».
Интервьюер возразил: «Нет, приложение мы перезапустить не можем, у нас в памяти все еще хранятся важные данные, а к самому сервису подключены важные клиенты, которых мы не можем заставлять переподключаться заново».
«ну хорошо», сказал я, «если мы не можем перезапускать приложение и данные нам не важны, то мы можем просто очистить этот открытый файл через файл дескриптор, даже если мы его не видим в команде ls на файловой системе».
Интервьюер остался доволен, а я нет.
Тогда я подумал, почему человек, проверяющий мои знания, не копает глубже? А что, если данные все-таки важны? Что если мы не можем перезапускать процесс, и при этом этот процесс пишет на файловую систему в раздел, на котором нет свободного места? Что если мы не можем потерять не только уже записанные данные, но и те данные, что этот процесс пишет или пытается записать?
Тузик
В начале моей карьеры я пытался создать небольшое приложение, в котором нужно было хранить информацию о пользователях. И тогда я думал, а как мне сопоставить пользователя к его данным. Есть, например, у меня Иванов Иван Иваныч, и есть у него какие-то данные, но как их подружить? Я могу указать напрямую, что собака по имени «Тузик» принадлежит этому самому Ивану. Но что, если он сменит имя и вместо Ивана станет, например, Олей? Тогда получится, что наша Оля Ивановна Иванова больше не будет иметь собаки, а наш Тузик все еще будет принадлежать несуществующему Ивану. Решить эту проблему помогла база данных, которая каждому пользователю давала уникальный идентификатор (ID), и мой Тузик привязывался к этому ID, который, по сути, был просто порядковым номером. Таким образом хозяин у тузика был с ID под номером 2, и на какой-то момент времени под этим ID был Иван, а потом под этим же ID стала Оля. Проблема человечества и животноводства была практически решена.
Файл дескриптор
Проблема файла и программы, работающей с этим файлом, примерно такая же как нашей собаки и человека. Предположим я открыл файл под именем ivan.txt и начал в него записывать слово tuzik, но успел записать только первую букву «t» в файл, и этот файл был кем-то переименован, например в olya.txt. Но файл остался тем же самым, и я все еще хочу записать в него своего тузика. Каждый раз при открытии файла системным вызовом open в любом языке программирования я получаю уникальный ID, который указывает мне на файл, этот ID и есть файл дескриптор. И совершенно не важно, что и кто делает с этим файлом дальше, его могут удалить, его могут переименовать, ему могут поменять владельца или забрать права на чтение и запись, я все равно буду иметь к нему доступ, потому что на момент открытия файла у меня были права для его чтения и/или записи и я успел начать с ним работать, а значит должен продолжать это делать.
В Linux библиотека libc открывает для каждого запущенного приложения(процесса) 3 файл дескриптора, с номерами 0,1,2. Больше информации вы можете найти по ссылкам man stdio и man stdout
- Файл дескриптор 0 называется STDIN и ассоциируется с вводом данных у приложения
- Файл дескриптор 1 называется STDOUT и используется приложениями для вывода данных, например командами print
- Файл дескриптор 2 называется STDERR и используется приложениями для вывода данных, сообщающих об ошибке
Если в вашей программе вы откроете какой-либо файл на чтение или запись, то скорее всего вы получите первый свободный ID и это будет номер 3.
Список файл дескрипторов можно посмотреть у любого процесса, если вы знаете его PID.
Например, откроем консоль с bash и посмотрим PID нашего процесса
Во второй консоли запустим
Файл дескриптор с номером 255 можете смело игнорировать в рамках данной статьи, он был открыт для своих нужд уже самим bash, а не прилинкованной библиотекой.
Сейчас все 3 файл дескриптора связаны с устройством псевдотерминала /dev/pts, но мы все равно можем ими манипулировать, например запустим во второй консоли
И в первой консоли мы увидим
Redirect и Pipe
Вы можете легко переопределить эти 3 файл дескриптора в любом процессе, в том числе и в bash, например через трубу(pipe), соединяющую два процесса, смотрим
Вы можете сами запустить эту команду с strace -f и увидеть, что происходит внутри, но я вкратце расскажу.
Наш родительский процесс bash с PID 15771 парсит нашу команду и понимает сколько именно команд мы хотим запустить, в нашем случае их две: cat и sleep. Bash знает что ему нужно создать два дочерних процесса, и объединить их одной трубой. Итого bash потребуется 2 дочерних процесса и один pipe.
Перед созданием дочерних процессов bash запускает системный вызов pipe и получает новые файл дескрипторы на временный буфер pipe, но этот буфер никак пока не связывает наши два дочерних процесса.
Для родительского процесса это выглядит так будто pipe уже есть, а дочерних процессов еще нет:
Затем с помощью системного вызова clone bash создает два дочерних процесса, и наши три процесса будут выглядеть так:
Не забываем, что clone клонирует процесс вместе со всеми файл дескрипторами, поэтому в родительском процессе и в дочерних они будут одинаковые. Задача родительского процесса с PID 15771 следить за дочерними процессами, поэтому он просто ждет ответ от дочерних.
Следовательно pipe ему не нужен, и он закрывает файл дескрипторы с номерами 3 и 4.
В первом дочернем процессе bash с PID 9004, системным вызовом dup2, меняет наш STDOUT файл дескриптор с номером 1 на файл дескриптор указывающий на pipe, в нашем случае это номер 3. Таким образом все, что первый дочерний процесс с PID 9004 будет писать в STDOUT, будет автоматически попадать в буфер pipe.
Во втором дочернем процессе с PID 9005 bash меняет с помощью dup2 файл дескриптор STDIN с номером 0. Теперь все, что будет читать наш второй bash с PID 9005, будет читать из pipe.
После этого в дочерних процессах так же закрываются файл дескрипторы с номерами 3 и 4, так как они более не используются.
Файл дескриптор 255 я намеренно игнорирую, он использует для внутренних нужд самого bash и в дочерних процессах будет также закрыт.
Далее в первом дочернем процессе с PID 9004 bash запускает с помощью системного вызова exec исполняемый файл, который мы указали в командной строке, в нашем случае это /usr/bin/cat.
Во втором дочернем процессе с PID 9005 bash запускает второй исполняемый файл, который мы указали, в нашем случае это /usr/bin/sleep.
Системный вызов exec не закрывает файл дескрипторы, если они не были открыты с флагом O_CLOEXEC во время выполнения вызова open. В нашем случае после запуска исполняемых файлов все текущие файл дескрипторы сохранятся.
Проверяем в консоли:
Как видите уникальный номер нашего pipe у нас в обоих процессах совпадает. Таким образом у нас есть связь между двумя разными процессами с одним родителем.
Для тех, кто не знаком с системными вызовами, которые использует bash, крайне рекомендую запустить команды через strace и посмотреть, что происходит внутри, например, так:
Вернемся к нашей проблеме с нехваткой места на диске и попыткой сохранить данные без перезапуска процесса. Напишем небольшую программу, которая будет записывать на диск примерно 1 мегабайт в секунду. При этом если по какой-либо причине мы не смогли записать данные на диск, мы будем просто игнорировать это и пытаться записать данные вновь через секунду. В примере я использую Python, вы можете использовать любой другой язык программирования.
Запустим программу и посмотрим на файл дескрипторы
Как видим у нас есть наши 3 стандартные файл дескрипторы и еще один, который мы открыли. Проверим размер файла:
данные пишутся, пробуем поменять права на файл:
Видим, что данные все еще пишутся, хотя наш пользователь не имеет права писать в файл. Попробуем его удалить:
Куда пишутся данные? И пишутся ли вообще? Проверяем:
Да, наш файл дескриптор все еще существует, и мы можем работать с этим файл дескриптором как с нашим старым файлом, мы можем его читать, очищать и копировать.
Смотрим на размер файла:
Размер файла 19923457. Пробуем очистить файл:
Как видим размер файла только увеличивается и наш транкейт не сработал. Обратимся к документации по системному вызову open. Если при открытии файла мы используем флаг O_APPEND, то при каждой записи операционная система проверяет размер файла и пишет данные в самый конец файла, причем делает это атомарно. Это позволяет нескольким тредам или процессам писать в один и тот же файл. Но в нашем коде мы не используем этот флаг. Мы можем увидеть другой размер файла в lsof после транкейт только если откроем файл для дозаписи, а значит в нашем коде вместо
мы должны поставить
Проверяем с «w» флагом
Программируем уже запущенный процесс
Часто программисты при создании и тестировании программы используют дебагеры (например GDB) или различные уровни логирования в приложении. Linux предоставляет возможность фактически писать и менять уже запущенную программу, например менять значения переменных, устанавливать breakpoint и тд и тп.
Возвращаясь к оригинальному вопросу с нехваткой места на диске для записи файла, попробуем сэмулировать проблему.
Создадим файл для нашего раздела, который мы подмонтируем как отдельный диск:
Создадим файловую систему:
Подмонтируем файловую систему:
Создаем директорию с нашим владельцем:
Откроем файл только на запись в нашей программе:
Ждем несколько секунд
Итак, мы получили проблему, описанную в начале этой статьи. Свободного места 0, занятого 100%.
Мы помним, что по условиям задачи мы пытаемся записать очень важные данные, которые нельзя потерять. И при этом нам нужно починить сервис без перезапуска процесса.
Допустим, у нас все же есть место на диске, но в другом разделе, например в /home.
Попробуем «перепрограммировать на лету» наш код.
Смотрим PID нашего процесса, который съел все место на диске:
Подключаемся к процессу через gdb
Смотрим открытые файл дескрипторы:
Смотрим информацию о файл дескрипторе с номером 3, который нас интересует
Помня о том, какой системный вызов делает Python (смотрите выше, где мы запускали strace и находили вызов open), обрабатывая наш код для открытия файла, мы делаем то же самое самостоятельно от имени нашего процесса, но биты O_WRONLY|O_CREAT|O_TRUNC нам нужно заменить на числовое значение. Для этого открываем исходники ядра, например тут и смотрим какие флаги за что отвечают
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Объединяем все значения в одно, получаем 00001101
Запускаем наш вызов из gdb
Итак мы получили новый файл дескриптор с номером 4 и новый открытый файл на другом разделе, проверяем:
Мы помним пример с pipe — как bash меняет файл дескрипторы, и уже выучили системный вызов dup2.
Пробуем подменить один файл дескриптор другим
Закрываем файл дескриптор 4, так как нам он не нужен:
И выходим из gdb
Проверяем новый файл:
Как видим, данные пишутся в новый файл, проверяем старый:
Данные не потеряны, приложение работает, логи пишутся в новое место.
Немного усложним задачу
Представим, что данные нам важны, но места на диске у нас нет ни в одном из разделов и подключить диск мы не можем.
Что мы можем сделать, так это перенаправить куда-то наши данные, например в pipe, а данные из pipe в свою очередь перенаправить в сеть через какую-либо программу, например netcat.
Мы можем создать именованный pipe командой mkfifo. Она создаст псевдофайл на файловой системе, даже если на ней нет свободного места.
Перезапускаем приложение, и проверяем:
Места на диске нет, но мы успешно создаем там именованный pipe:
Теперь нам надо как-то завернуть все данные, что попадают в этот pipe на другой сервер через сеть, для этого подойдет все тот же netcat.
На сервере remote-server.example.com запускаем
На нашем проблемном сервере запускаем в отдельном терминале
Теперь все данные, которые попадут в pipe автоматически попадут на stdin в netcat, который их отправит в сеть на порт 7777.
Все что нам осталось сделать это начать писать наши данные в этот именованный pipe.
У нас уже есть запущенное приложение:
Из всех флагов нам нужен только O_WRONLY так как файл уже существует и очищать нам его не нужно
Проверяем удаленный сервер remote-server.example.com
Данные идут, проверяем проблемный сервер
Данные сохранились, проблема решена.
Пользуясь случаем, передаю привет коллегам из компании Degiro.
Слушайте подкасты Радио-Т.
В качестве домашнего задания предлагаю подумать, что будет в файл дескрипторах процесса cat и sleep если запустить такую команду:
Источник