views:

348

answers:

3

Currently the way /usr/sbin/alsa in Debian knows the processes using the sound card looks like:

echo $( \
 lsof +D /dev -F rt \
 | awk '/^p/ {pid=$1} /^t/ {type=$1} /^r0x(74|e)..$/ && type == "tCHR" {print pid}' \
 | cut -c 2- \
 | uniq \
)

Which is rather ugly and depends on lsof. I am looking for a POSIX solution without lsof, perhaps using /proc.

 time for i in /proc/*/fd/*; do readlink $i | grep -q /dev/snd/pcm && echo $i | awk -F '/' '{print $3}'; done | uniq

Unfortunately seems to take twice as long as the lsof based snippet above. Can you make it faster, in order to make it a viable replacement?

Update I rewrote the above as:

#!/bin/sh
for i in /proc/[0-9]*/fd/*
do
  if readlink $i | grep -q /dev/snd/pcm
  then
    IFS=/; set -- $i; unset IFS; echo $3
  fi
done

But it seems to have the same performance as my earlier snippet. I suspect grep is the culprit.

Update: I have opened a Debian bug on the topic.

+2  A: 

There is an answer for this question on the ALSA FAQ. On my system, using fuser is way faster than using lsof.

fuser -v /dev/snd/*
jfsantos
`fuser` is fast, but it depends on [psmisc](http://packages.qa.debian.org/p/psmisc.html). I'm trying to shed dependencies, not replace with another. :-)
hendry
+1  A: 

You don't say what kind of timescales you're looking for but for your alternative suggestion

for i in /proc/[0-9]*/fd/*;

might work and give you a bit of speed up, as might using cut rather than awk.

sparklewhiskers
+1  A: 

You start a lot of processes here. Instead you can try doing in a similar way to the lsof script you posted... but replacing lsof by a shell for loop:

If you want to avoid launching lots of grep processes, start only one:

#!/bin/sh
for i in /proc/[0-9]*/fd/*
do
    echo ${i%/fd/*} $(readlink $i)
done | grep -q /dev/snd/pcm

This takes now 4.5s on my desktop, compared to 7.5s when there's one grep process for each opened file.

But... your grep is not necessary here, I think. If you care so much, you can try:

#!/bin/sh
for i in /proc/[0-9]*/fd/*
do
    var="$(readlink $i)"
    if test x"$var" != x"${var#/dev/snd/pcm}"
    then
        echo $i
    fi
done

This is even faster for me (test is almost always a shell builtin), but I guess this is more because of bad testing methods. Try yourself.

liori
That code is 3x faster on my tests. Great thank you! I've wrongly avoided parameter expansion.
hendry