64

I'm working on an application that monitors the processes' resources and gives a periodic report in Linux, but I faced a problem in extracting the open files count per process.

This takes quite a while if I take all of the files and group them according to their PID and count them.

How can I take the open files count for each process in Linux?

Alfe
  • 56,346
  • 20
  • 107
  • 159
Aladdin
  • 1,207
  • 2
  • 16
  • 26

5 Answers5

128

Have a look at the /proc/ file system:

ls /proc/$pid/fd/ | wc -l

To do this for all processes, use this:

cd /proc
for pid in [0-9]*
do
    echo "PID = $pid with $(ls /proc/$pid/fd/ | wc -l) file descriptors"
done

As a one-liner (filter by appending | grep -v "0 FDs"):

for pid in /proc/[0-9]*; do printf "PID %6d has %4d FDs\n" $(basename $pid) $(ls $pid/fd | wc -l); done

As a one-liner including the command name, sorted by file descriptor count in descending order (limit the results by appending | head -10):

for pid in /proc/[0-9]*; do p=$(basename $pid); printf "%4d FDs for PID %6d; command=%s\n" $(ls $pid/fd | wc -l) $p "$(ps -p $p -o comm=)"; done | sort -nr

Credit to @Boban for this addendum:

You can pipe the output of the script above into the following script to see the ten processes (and their names) which have the most file descriptors open:

  ...
done | sort -rn -k5 | head | while read -r _ _ pid _ fdcount _
do
  command=$(ps -o cmd -p "$pid" -hc)
  printf "pid = %5d with %4d fds: %s\n" "$pid" "$fdcount" "$command"
done

Here's another approach to list the top-ten processes with the most open fds, probably less readable, so I don't put it in front:

find /proc -maxdepth 1 -type d -name '[0-9]*' \
     -exec bash -c "ls {}/fd/ | wc -l | tr '\n' ' '" \; \
     -printf "fds (PID = %P), command: " \
     -exec bash -c "tr '\0' ' ' < {}/cmdline" \; \
     -exec echo \; | sort -rn | head
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • it gives me an error :"cannot access /proc/$pid/fd/: no such file or directoy " – Aladdin Feb 13 '14 at 11:11
  • What is your `$pid`? You know shell variables, don't you? – Alfe Feb 13 '14 at 11:12
  • Try with `ls /proc/$$/fd/ | wc -l` first. `$$` evaluates to your current shell; that should always exist of course. – Alfe Feb 13 '14 at 11:13
  • I would like to obtain the count of all running processes , not only one . – Aladdin Feb 13 '14 at 11:15
  • 1
    Of course, you will need to have root permissions to do that for many of the processes. Their file descriptors are kind of private, you know ;-) – Alfe Feb 13 '14 at 11:20
  • 2
    /proc/$pid/fd lists descriptor files, that is slightly different of "open files" as we can have memory map and other unusual file objects. – mcoolive Jul 31 '17 at 15:55
  • 1
    This extends the answer and turns pids to command names: `for pid in [0-9]*; do echo "PID = $pid with $(ls /proc/$pid/fd/ 2>/dev/null | wc -l) file descriptors"; done | sort -rn -k5 | head | while read -r line; do pid=`echo $line | awk '{print $3}'`; command=`ps -o cmd -p $pid -hc`; echo $line | sed -s "s/PID = \(.*\) with \(.*\)/Command $command (PID = \1) with \2/g"; done` – Boban P. Jan 12 '18 at 09:44
  • Thanks, @BobanP.! I adopted your extension and added it to my answer. – Alfe Jan 12 '18 at 10:22
  • also add `2>dev/null` to `ls /proc/$pid/fd/` to avoid printing errors on stderr for shortlived processes `ls: cannot access /proc/38391/fd/: No such file or directory` – Boban P. Jan 12 '18 at 11:16
  • 1
    Yeah, well. Instead of parsing the original output and then call `ps` again for each process to find out its command, it might make more sense to use `/proc/$pid/cmdline` in the first loop. While technically it is still possible for a process to disappear between the evaluating of `[0-9]*` and the scanning of its disc, this is less likely. – Alfe Jan 12 '18 at 11:46
  • @BobanP. I added another approach using `find`. – Alfe Jan 12 '18 at 12:00
  • 1
    Executing `command=$(ps -o cmd -p "$pid" -hc)` gave me `Warning: bad syntax, perhaps a bogus '-'`. It worked running as `command=$(ps -o cmd -p "$pid" hc)`. – Lucas Basquerotto Sep 20 '18 at 18:09
  • @LucasBasquerotto Without the dash it worked for me as well (Ubuntu 18.04, `ps` version 3.3.10) so maybe that's a bit more portable. In fact my manpage doesn't mention `-h` at all which also indicates that omitting the dash might be preferable. – Alfe Jan 07 '19 at 08:37
  • The second block of code works great. The third block is doing something wrong because I get wildly different numbers. – Ryan DuVal Dec 07 '19 at 23:13
10

Try this:

ps aux | sed 1d | awk '{print "fd_count=$(lsof -p " $2 " | wc -l) && echo " $2 " $fd_count"}' | xargs -I {} bash -c {}
Cokorda Raka
  • 4,375
  • 6
  • 36
  • 54
  • For Fedora, it gives: lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs Output information may be incomplete. lsof: no pwd entry for UID 65535 – Alexred May 10 '23 at 06:33
7

I used this to find top filehandler-consuming processes for a given user (username) where dont have lsof or root access:

for pid in `ps -o pid -u username` ; do echo "$(ls /proc/$pid/fd/ 2>/dev/null | wc -l ) for PID: $pid" ; done  | sort -n | tail
JG_
  • 121
  • 1
  • 5
5

How can I take the open files count for each process in Linux?

procpath query -f stat,fd

if you're running it from root (e.g. prefixing the command with sudo -E env PATH=$PATH), otherwise it'll only return file descriptor counts per process whose /proc/{pid}/fd you may list. This will give you a big JSON document/tree whose nodes look something like:

{
  "fd": {
    "anon": 3,
    "blk": 0,
    "chr": 1,
    "dir": 0,
    "fifo": 0,
    "lnk": 0,
    "reg": 0,
    "sock": 3
  },
  "stat": {
    "pid": 25649,
    "ppid": 25626,
    ...
  },
  ...
}

The content of fd dictionary is counts per file descriptor type. The most interesting ones are probably these (see procfile.Fd description or man fstat for more details):

  • reg – count of open (regular) files
  • sock – count of open sockets

I'm the author of Procpath, which is a tool that provides a nicer interface to procfs for process analysis. You can record a process tree's procfs stats (in a SQLite database) and plot any of them later. For instance this is how my Firefox's process tree (root PID 2468) looks like with regards to open file descriptor count (sum of all types):

procpath --logging-level ERROR record -f stat,fd -i 1 -d ff_fd.sqlite \ 
  '$..children[?(@.stat.pid == 2468)]'
# Ctrl+C
procpath plot -q fd -d ff_fd.sqlite -f ff_df.svg

Firefox open file descriptors

If I'm interested in only a particular type of open file descriptors (say, sockets) I can plot it like this:

procpath plot --custom-value-expr fd_sock -d ff_fd.sqlite -f ff_df.svg
saaj
  • 23,253
  • 3
  • 104
  • 105
1

This works for me:

ps -opid= -ax | xargs -L 1 -I{} -- sudo bash -c 'echo -n "{} ";lsof -p {} 2>/dev/null | wc -l' | sort -n -k2

It prints numopenfiles per pid sorted by numopenfiles.

It will ask for sudo password once.

Note that the sum of the above numbers might be bigger than the total number of open files from all processes.
As I read here: forked processes can share file handles

Marinos An
  • 9,481
  • 6
  • 63
  • 96