Поиск идентификатора процесса с учетом сокета и индекса в Python 3

/proc/net/tcp дает мне локальный адрес, порт и номер inode для сокета (например, 0.0.0.0:5432 и 9289).

Я хотел бы найти PID для конкретного процесса, учитывая приведенную выше информацию.

Можно открыть каждую пронумерованную папку в /proc, а затем проверить символические ссылки на соответствие номеров сокетов/инодов с помощью команды оболочки, например «$ sudo ls -l /proc/*/fd/ 2>/dev/null | grep socket». Однако это кажется более затратным в вычислительном отношении, чем необходимо, поскольку ‹5% процессов в любой данной системе имеют открытые сокеты TCP.

Каков наиболее эффективный способ найти PID, который открыл данный сокет? Я бы предпочел использовать стандартные библиотеки, и в настоящее время я разрабатываю Python 3.2.3.

Изменить: образцы кода удалены из вопроса, поскольку теперь они включены в ответ ниже.


person 5ba8cabe450348c7fbe2    schedule 02.02.2013    source источник
comment
Что касается стандартных библиотек, я хотел бы избежать использования subprocess.call или os.exec и выполнять всю работу внутри Python.   -  person 5ba8cabe450348c7fbe2    schedule 03.02.2013
comment
Кажется, нет большой альтернативы чтению procfs, использованию lsof или использованию netstat. И я думаю, что все они делают то же самое (читает procfs). К сожалению, больше нечего делать. Многие похожие вопросы SO имеют один и тот же ответ: 1. прочитать /proc/net/tcp 2. прочитать /proc/‹pid›/fd/.   -  person emil    schedule 03.02.2013
comment
Я добавил некоторый рабочий код, используя os.readlink и os.listdir - никаких вызовов подпроцесса. Проблема, которую я вижу, заключается в том, что я делаю гораздо больше вызовов os.readlink, чем необходимо - около 600 вызовов, чтобы найти PID примерно для четырех открытых сокетов.   -  person 5ba8cabe450348c7fbe2    schedule 03.02.2013
comment
Можете ли вы сузить область поиска, например, с помощью конкретных имен пользователей? Если вам интересно, вы можете запустить strace lsof -n -i. Я не понимаю, почему запуск подпроцесса - это так плохо? Это путь Unix. :)   -  person emil    schedule 03.02.2013
comment
В основном я пишу чистую замену Python 3 для netstat (или, по крайней мере, что-то подобное), поэтому мне больше интересно узнать, как это сделать без подпроцесса, а затем сделать это максимально эффективно. Я пишу с нуля, чтобы узнать как можно больше о Python 3 и procfs!   -  person 5ba8cabe450348c7fbe2    schedule 03.02.2013


Ответы (2)


Следующий код достигает исходной цели:

def find_pid(inode):

    # get a list of all files and directories in /proc
    procFiles = os.listdir("/proc/")

    # remove the pid of the current python process
    procFiles.remove(str(os.getpid()))

    # set up a list object to store valid pids
    pids = []

    for f in procFiles:
        try:
            # convert the filename to an integer and back, saving the result to a list
            integer = int(f)
            pids.append(str(integer))
        except ValueError:
            # if the filename doesn't convert to an integer, it's not a pid, and we don't care about it
            pass

    for pid in pids:
        # check the fd directory for socket information
        fds = os.listdir("/proc/%s/fd/" % pid)
        for fd in fds:
            # save the pid for sockets matching our inode
            if ('socket:[%d]' % inode) == os.readlink("/proc/%s/fd/%s" % (pid, fd)):
                return pid
person 5ba8cabe450348c7fbe2    schedule 03.02.2013
comment
Возможно, следует сделать: if ('socket:[%d]' % inode) == os.readlink("/proc/%s/fd/%s" % (pid, fd)): вместо этого, чтобы вы не сопоставляли индексные дескрипторы, которые являются просто подстрокой. Но в остальном я думаю, что это выглядит хорошо. Не знаю, есть ли более эффективный способ. - person emil; 04.02.2013
comment
Спасибо за предложение - я отредактировал код выше. Я также не могу придумать способ сделать это намного быстрее, поэтому я пойду дальше и отмечу его как решенный. Спасибо! - person 5ba8cabe450348c7fbe2; 05.02.2013

Я не знаю, как это сделать на питоне, но вы можете использовать lsof(1):

lsof -i | awk -v sock=158384387 '$6 == sock{print $2}'

158384387 — индексный дескриптор сокета. А затем вызовите его из python, используя subprocess.Popen.

Вам придется использовать sudo(8), если вы хотите видеть сокеты, открытые другими пользователями.

person emil    schedule 02.02.2013