28.2. /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
         

쉘 스크립트는 /proc에 있는 파일에서 데이터를 뽑아낼 수도 있습니다. [1]

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

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

if [ $CPU = Pentium ]
then
  어떤 명령어 실행
  ...
else
  다른 명령어 실행
  ...
fi

/proc 디렉토리에는 별나게도 숫자로 된 디렉토리들이 들어 있습니다. 이 서브 디렉토리들은 현재 실행중인 프로세스 ID를 나타냅니다. 각 서브 디렉토리에는 해당 프로세스에 대한 유용한 정보들을 담고 있는 파일이 들어 있는데, statstatus 파일은 프로세스에 대한 통계 정보를 담고 있고, cmdline 파일은 해당 프로세스가 실행됐을 때의 명령어 줄 인자를 담고 있고, exe 파일은 실행중인 프로세스의 완전한 경로명을 심볼릭 링크하고 있습니다. 각 서브 디렉토리에는 이런식의 다른 파일들도 더 있지만 쉘 스크립팅의 관점에서는 이 정도면 충분할 겁니다.

예 28-1. 특정 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 "사용법: `basename $0` PID-number" >&2  # 에러 메세지 >표준에러.
  exit $E_WRONGARGS
fi  

pidno=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 )
# "ps" 목록에서 pid(첫번째 필드)를 확인.
# 그 다음에는 이 스크립트가 실행시킨 프로세스가 아닌 실제 프로세스인지 확인.
# 마지막의 "grep $1"이 이런 가능성을 제거해 줍니다.
if [ -z "$pidno" ]  # 결과가 아무것도 없다면 길이가 0인 문자열이 됩니다.
then                # 주어진 pid로 실행중인 프로세스가 없음.
  echo "실행중인 프로세스가 없습니다."
  exit $E_NOSUCHPROCESS
fi  

# 대신 이렇게 해도 됩니다:
#   if ! ps $1 > /dev/null 2>&1
#   then                # 주어진 pid로 실행중인 프로세스 없음.
#     echo "실행중인 프로세스가 없습니다."
#     exit $E_NOSUCHPROCESS
#    fi

# 이 전체 과정은 "pidof" 명령어로 간단하게 할 수 있습니다.


if [ ! -r "/proc/$1/$PROCFILE" ]  # 읽기 퍼미션 확인.
then
  echo "$1 프로세스는 실행중이지만,"
  echo "/proc/$1/$PROCFILE 을 읽을 수 없습니다."
  exit $E_NOPERMISSION  # 일반 사용자는 /proc 의 몇몇 파일에 접근할 수 없습니다.
fi  

# 바로 앞의 두 테스트들을 이렇게 할 수도 있습니다:
#    if ! kill -0 $1 > /dev/null 2>&1 # '0' 은 시그널이 아니고 해당 프로세스에 
									  # 시그널을 보낼 수 있는지를 확인해서 
									  # 실행 여부를 알 수 있습니다.
#    then echo "PID 가 존재하지 않거나 소유자가 아닙니다." >&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 "$1 번 프로세스는 $exe_file 으로 실행됐습니다."
else
  echo "실행중인 프로세스가 없습니다."
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

예 28-2. 온라인 연결 상태

#!/bin/bash

PROCNAME=pppd        # ppp 데몬
PROCFILENAME=status  # 찾을 곳
NOTCONNECTED=65
INTERVAL=2           # 매 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" ]   # pid 가 없다면 그 프로세스는 실행중이 아님.
then
  echo "연결중이 아닙니다."
  exit $NOTCONNECTED
else
  echo "연결중입니다."; echo
fi

while [ true ]       # 무한 루프, 이 부분은 좀 더 개선될 수 있습니다.
do

  if [ ! -e "/proc/$pidno/$PROCFILENAME" ]
  # 프로세스가 실행중이면, "status" 파일도 존재합니다.
  then
    echo "연결이 끊어졌습니다."
    exit $NOTCONNECTED
  fi

netstat -s | grep "packets received"  # 몇 가지 접속 통계.
netstat -s | grep "packets delivered"


  sleep $INTERVAL
  echo; echo

done

exit 0

# 이 스크립트는 Control-C 로만 끝낼 수 있습니다.

#    독자들을 위한 연습문제:
#    "q"를 눌렀을 때 종료하도록 개선시켜 보세요.
#    사용자가 좀 더 쓰기 좋게(user-friendly) 만들어 보세요.

주의

보통, /proc 밑에 있는 파일에 쓰기를 하면, 파일 시스템을 손상 시킬 수도 있고 시스템을 망가트릴 수도 있기 때문에 위험합니다.

주석

[1]

procinfo, free, vmstat, lsdev, uptime 같은 명령어들이 이렇게 동작합니다.