shell教程

27.2. /proc

/proc目录实际上是一个伪文件系统.在/proc目录里的文件是当前Ű

/proc目录实际上是一个伪文件系统 . 在 /proc 目录里的文件是当前运行系统和内核进程及它们的相关信息和统计.

 bash$ cat /proc/devices
 Character devices:
   1 mem
   2 pty
   3 ttyp
   4 ttyS
   5 cua
   7 vcs
  10 misc
  14 sound
  29 fb
  36 netlink
 128 ptm
 136 pts
 162 raw
 254 pcmcia

 Block devices:
   1 ramdisk
   2 fd
   3 ide0
   9 md
 
 
 
 bash$ cat /proc/interrupts
            CPU0       
   0:      84505          XT-PIC  timer
   1:       3375          XT-PIC  keyboard
   2:          0          XT-PIC  cascade
   5:          1          XT-PIC  soundblaster
   8:          1          XT-PIC  rtc
  12:       4231          XT-PIC  PS/2 Mouse
  14:     109373          XT-PIC  ide0
 NMI:          0 
 ERR:          0
 
 
 bash$ cat /proc/partitions
 major minor  #blocks  name     rio rmerge rsect ruse wio wmerge wsect wuse running use aveq

    3     0    3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030
    3     1      52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
    3     2          1 hda2 0 0 0 0 0 0 0 0 0 0 0
    3     4     165280 hda4 10 0 20 210 0 0 0 0 0 210 210
    ...
 
 
 
 bash$ cat /proc/loadavg
 0.13 0.42 0.27 2/44 1119
 
 
 
 bash$ cat /proc/apm
 1.16 1.2 0x03 0x01 0xff 0x80 -1% -1 ?
          

Shell 脚本可以从/proc目录中的一些文件里提取数据. [1]

FS=iso                       # ISO 文件系统是否被内核支持?

grep $FS /proc/filesystems   # iso9660

kernel_version=$( awk '{ print $3 }' /proc/version )

CPU=$( awk '/model name/ {print $4}' < /proc/cpuinfo )

if [ $CPU = Pentium ]
then
  run_some_commands
  ...
else
  run_different_commands
  ...
fi

devfile="/proc/bus/usb/devices"
USB1="Spd=12"
USB2="Spd=480"


bus_speed=$(grep Spd $devfile | awk '{print $9}')

if [ "$bus_speed" = "$USB1" ]
then
  echo "USB 1.1 port found."
  # 这儿开始操作USB 1.1相关的动作.
fi

/proc目录下有许多不相同的数字命名的子目录. 这些子目录的数字名字都映射对应的当前正在运行的进程的进程号(process ID) . 这些子目录里面有许多文件用于保存对应进程的信息。文件 statstatus 保存着进程运行时的各项统计, the cmdline文件保存该进程的被调用时的命令行参数, 而and the exe 文件是该运行进程完整路径名的符号链接. 还有其他一些文件,但从脚本的观点来看它们都非常的有意思。


例子 27-2. 搜索与一个PID相关的进程

#!/bin/bash
# pid-identifier.sh: 给出指定PID的进程的程序全路径.

ARGNO=1  # 此脚本期望的参数个数.
E_WRONGARGS=65
E_BADPID=66
E_NOSUCHPROCESS=67
E_NOPERMISSION=68
PROCFILE=exe

if [ $# -ne $ARGNO ]
then
  echo "Usage: `basename $0` PID-number" >&2  # 帮助信息重定向到标准出错.
  exit $E_WRONGARGS
fi

pidno=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 )
# 搜索命令"ps"输出的第一列.
# 然后再次确认是真正我们要寻找的进程,而不是这个脚本调用而产生的进程.
# 后一个"grep $1"会滤掉这个可能产生的进程.
#
#    pidno=$( ps ax | awk '{ print $1 }' | grep $1 )
#    也可以, 由 Teemu Huovila指出.

if [ -z "$pidno" ]  # 如果过滤完后结果是一个空字符串,
then                # 没有对应的PID进程在运行.
  echo "No such process running."
  exit $E_NOSUCHPROCESS
fi

# 也可以用:
#   if ! ps $1 > /dev/null 2>&1
#   then                # 没有对应的PID进程在运行.
#     echo "No such process running."
#     exit $E_NOSUCHPROCESS
#    fi

# 为了简化整个进程,使用"pidof".


if [ ! -r "/proc/$1/$PROCFILE" ]  # 检查读权限.
then
  echo "Process $1 running, but..."
  echo "Can't get read permission on /proc/$1/$PROCFILE."
  exit $E_NOPERMISSION  # 普通用户不能存取/proc目录的某些文件.
fi

# 最后两个测试可以用下面的代替:
#    if ! kill -0 $1 > /dev/null 2>&1 # '0'不是一个信号,
                                      # 但这样可以测试是否可以
                                      # 向该进程发送信号.
#    then echo "PID doesn't exist or you're not its owner" >&2
#    exit $E_BADPID
#    fi



exe_file=$( ls -l /proc/$1 | grep "exe" | awk '{ print $11 }' )
# 或       exe_file=$( ls -l /proc/$1/exe | awk '{print $11}' )
#
# /proc/pid-number/exe 是进程程序全路径的符号链接。
#

if [ -e "$exe_file" ]  # 如果 /proc/pid-number/exe 存在 ...
then                 # 则相应的进程存在.
  echo "Process #$1 invoked by $exe_file."
else
  echo "No such process running."
fi


# 这个被详细讲解的脚本几乎可以用下面的命令代替:
# ps ax | grep $1 | awk '{ print $5 }'
# 然而, 这样并不会工作...
# 因为'ps'输出的第5列是进程的argv[0](即命令行第一个参数,调用时程序用的程序路径本身),
# 但不是可执行文件.
#
# 然而, 下面的两个都可以工作.
#       find /proc/$1/exe -printf '%l\n'
#       lsof -aFn -p $1 -d txt | sed -ne 's/^n//p'

# 由Stephane Chazelas附加注释.

exit 0


例子 27-3. 网络连接状态

#!/bin/bash

PROCNAME=pppd        # ppp 守护进程
PROCFILENAME=status  # 在这儿寻找信息.
NOTCONNECTED=65
INTERVAL=2           # 两秒刷新一次.

pidno=$( ps ax | grep -v "ps ax" | grep -v grep | grep $PROCNAME | awk '{ print $1 }' )
# 搜索ppp守护进程'pppd'的进程号.
# 一定要过滤掉由搜索进程产生的该行进程.
#
#  正如Oleg Philon指出的那样,
#+ 使用"pidof"命令会相当的简单.
#  pidno=$( pidof $PROCNAME )
#
#  颇有良心的建议:
#+ 当命令序列变得复杂的时候,去寻找更简洁的办法。 .


if [ -z "$pidno" ]   # 如果没有找到此进程号,则进程没有运行.
then
  echo "Not connected."
  exit $NOTCONNECTED
else
  echo "Connected."; echo
fi

while [ true ]       # 死循环,这儿可以有所改进.
do

  if [ ! -e "/proc/$pidno/$PROCFILENAME" ]
  # 进程运行时,对应的"status"文件会存在.
  then
    echo "Disconnected."
    exit $NOTCONNECTED
  fi

netstat -s | grep "packets received"  # 取得一些连接统计.
netstat -s | grep "packets delivered"


  sleep $INTERVAL
  echo; echo

done

exit 0

# 当要停止它时,可以用Control-C终止.

#    练习:
#    ---------
#    改进这个脚本,使它能按"q"键退出.
#    给脚本更友好的界面.

一般来说, 写/proc目录里的文件是危险 ,因为这样会破坏这个文件系统或摧毁机器.

[1]

一些系统命令, 例如 procinfo, free, vmstat, lsdev, 和uptime 也能做类似 的事情.

© 内存溢出 OutOfMemory.CN