"ps" 명령어 흉내내기

Programming/Unix/Linux 2007/05/08 17:30 장인정신
UNIX의 ps 명령을 흉내낸 예제입니다.

코드:

/*
Copyright (c) 2002 Information Equipment co.,LTD.
All Right Reserved.

Code by JaeHyuk Cho <minzkn@infoeq.co.kr>

This example !!!
   - Simple is best.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>

static int MZ_ps_LoadFile(char *s_FileName, void **s_Buffer)
{
int s_Return = (-1), s_Handle, s_Length;
*(s_Buffer) = (void *)0;
s_Handle = open(s_FileName, O_RDONLY);
if(s_Handle >= 0)
{
  s_Length = 32 << 10;   
  *(s_Buffer) = (void *)malloc(s_Length);
  if(*(s_Buffer))s_Return = read(s_Handle, *(s_Buffer), s_Length);
  else fprintf(stderr, "Not enough memory !!!\n");
  close(s_Handle);
}
else fprintf(stderr, "Can not open file \"%s\" !!!\n", s_FileName);
return(s_Return);
}

static void MZ_ps(void)
{
char *s_ProcDirectory = "/proc";
DIR           *s_Directory;
struct dirent *s_DirectoryEntry;
char           s_TempPath[32];
s_Directory = opendir(s_ProcDirectory);
if(s_Directory)
{
  fprintf(stdout, "%5s %5s %5s %5s %s\n", "PPID", "PID", "UID", "State", "Name");
  do
  {
   s_DirectoryEntry = readdir(s_Directory);
   if(s_DirectoryEntry)
   {
    void *s_Buffer, *s_Ptr;
    int s_Length;   
    char s_Name[16];
    int  s_State, s_PID, s_PPID, s_UID;
    if(isdigit(*((char *)(s_DirectoryEntry->d_name))) == 0)continue;
    s_Name[0] = s_Name[15] = '\0';
    s_State   = '?';
    s_PID     = (-1);
    s_PPID    = (-1);
    s_UID     = (-1);
    sprintf(&s_TempPath[0], "%s/%s/status", s_ProcDirectory, (char *)s_DirectoryEntry->d_name);     
    s_Length = MZ_ps_LoadFile(&s_TempPath[0], &s_Buffer);
    if(s_Length > 0 && s_Buffer)
    {
     s_Ptr = strstr(s_Buffer, "Name:");      
     if(s_Ptr)sscanf(s_Ptr, "Name:\t%15s", &s_Name[0]);
     s_Ptr = strstr(s_Buffer, "State:");
     if(s_Ptr)sscanf(s_Ptr, "State:\t%c", (char *)&s_State);
     s_Ptr = strstr(s_Buffer, "Pid:");
     if(s_Ptr)sscanf(s_Ptr, "Pid:\t%d", &s_PID);
     s_Ptr = strstr(s_Buffer, "PPid:");
     if(s_Ptr)sscanf(s_Ptr, "PPid:\t%d", &s_PPID);
     s_Ptr = strstr(s_Buffer, "Uid:");
     if(s_Ptr)sscanf(s_Ptr, "Uid:\t%d", &s_UID);
     free(s_Buffer); 
    }
    sprintf(&s_TempPath[0], "%s/%s/cmdline", s_ProcDirectory, (char *)s_DirectoryEntry->d_name);   
    s_Length = MZ_ps_LoadFile(&s_TempPath[0], &s_Buffer);
    if(s_Length > 0 && s_Buffer)
    {
     fprintf(stdout, "%5d %5d %5d   %c   %s\n", s_PPID, s_PID, s_UID, s_State, s_Name);      
     free(s_Buffer); 
    }
   }
  }while(s_DirectoryEntry);
  closedir(s_Directory);
}
else fprintf(stderr, "Can not open directory \"/proc\"\n");
}

int main(int s_Argc, char **s_Argv)
{
fprintf(stdout, "MZ_ps v1.0.0 - Copyright(c)InfoEQ co.,LTD - %s %s\n", __DATE__, __TIME__);
fprintf(stdout, "Code by JaeHyuk Cho - <minzkn@infoeq.co.kr>\n\n");
MZ_ps();
return(0);   
}

/* End of source */
2007/05/08 17:30 2007/05/08 17:30
받은 트랙백이 없고, 댓글 6개가 달렸습니다.

댓글+트랙백 RSS :: http://blog.minzkn.com/rss/response/73

댓글+트랙백 ATOM :: http://blog.minzkn.com/atom/response/73

트랙백 주소 :: http://blog.minzkn.com/trackback/73

트랙백 RSS :: http://blog.minzkn.com/rss/trackback/73

트랙백 ATOM :: http://blog.minzkn.com/atom/trackback/73

댓글을 달아 주세요

댓글 RSS 주소 : http://blog.minzkn.com/rss/comment/73
댓글 ATOM 주소 : http://blog.minzkn.com/atom/comment/73
  1. 김원경 2009/11/03 13:04  댓글주소  수정/삭제  댓글쓰기

    지금 현재 리눅스를 배우고 있는 학생입니다.
    다름이아니라...
    이번에 ps명령어를 c로 구현해야하는데...
    어떻게 해야하는지 몰라서요...
    직접제작하신 ps명령어 흉내내는걸 봤습니다..
    그런데... 어떤말인지 잘몰라서요 ㅠㅠ
    설명이나 주석처리 가능하신지요...
    주석처리해놓으신파일이나...
    아니면 전체적으로 어떻게 해야하는지를 알려주셨으면 하고 이렇게 글을씁니다..

    • 장인정신 2009/11/03 23:23  댓글주소  수정/삭제

      리눅스에서는 각 프로세스마다 고유의 ID를 부여하는데 이것을 PID라고 합니다.
      그리고 모든 프로세스는 각각 고유의 정보를 담고 있는 디렉토리가 존재하게 되는데 "/proc/<PID>" 이 바로 그것입니다.

      "/proc"디렉토리에서 숫자로만 되어 있는 디렉토리만을 취하면 각각의 프로세스목록을 얻을수 있게 됩니다.

      리눅스의 ps 명령은 이러한 "/proc/<숫자>"로 이루어진 디렉토리 하위의 특정 정보파일을 구문파싱하여 사람이 보기 편한 형태로 보여주는것입니다.

      일종의 Directory scan 관련 함수과 String 의 구문해석관련 함수를 이미 알고 있다면 익히 어렵지 않은 소스입니다.

      opendir, readdir, closedir, open, read, close, strstr, malloc, free, isgigit

      사용되어진 함수중 주요 함수들만 열거하였습니다.
      김원경님께서는 일단 위 함수들을 man page에서 찾아보고 정리해보시면
      도움이 될거 같습니다.
      소스의 주석을 달정도의 난이도를 가지고 있는 소스가 아닙니다.
      그 이전에 이 소스를 어렵게 느끼시는것은 위 함수들에 대한
      공부를 소흘히 하셨기 때문이라 생각됩니다.


      opendir계열함수는 주어진 디렉토리의 하위 파일 또는 디렉토리를 검색하는데 사용되며 간단한 슈도코드는 다음과 같은식으로 사용합니다.

      DIR *d;
      struct dirent *e;

      d = opendir("/proc");
      for(;;) {
      e = readdir(d);
      if(e == NULL) break;

      /* printf("d_name=\"%s\"\n", d.d_name);
      }
      closedir(d);




      open계열 함수는 특정 파일의 내용을 읽고 쓰기위한 조작에 사용하며 간단한 슈도 코드는 다음과 같습니다.
      int fd;
      ssize_t check;
      size_t bsize;
      unsigned char buffer[ 4 << 10 ];
      fd = open("/proc/1/status", O_RDONLY /* | O_BINARY */);
      bsize = sizeof(buffer) - 1u;
      for(;;) {
      check = read(fd, (void *)(&buffer[0]), bsize);
      if(check == (-1)) {
      if(errno == EINTR) continue;

      perror("읽는데 에러가 났어요.");
      break;
      }
      if(check == 0) {
      printf("파일의 끝\n");
      }

      buffer[check] = '\0';
      printf("%s", (char *)(&buffer[0]));
      }
      close(fd);



      str계열 함수는 다양한 기능의 함수들이 많습니다. 그중에서 strstr함수는
      특정 문자열에서 문자열을 찾아 그 위치를 반환하는 함수입니다.

  2. 김원경 2009/11/09 21:27  댓글주소  수정/삭제  댓글쓰기

    답변감사합니다!^^

    ps에는 많은 옵션들을 가지고있는데요
    여러옵션을 실행하게되면
    tty나 time 이것도 나오게됩니다.
    이 2개는 proc 디렉토리에서 불러오게 되면 나옵니까...?
    만약 불러올려면 똑같이 opendir로 불러오면 되나요..?

    아니면 구현하는방법이 따로 있습니까??ㅎ

  3. 장인정신 2009/11/10 11:23  댓글주소  수정/삭제  댓글쓰기

    tty 는 /proc/<PID>/fd/[0..2]의 link 가 어디로 걸려있는지로 확인할수 있습니다.
    link 가 가르키는 것을 얻기 위해서는 readlink 함수를 사용할수 있습니다.
    time 은 /proc/<PID>/stat에 있고 이것을 적절히 사람이 식별가능한 상태로 변환하기 위해서 asctime 또는 ctime 함수를 사용할수 있겠죠.
    그리고 UID, GID 등을 실제 사용자 ID와 그룹ID로 변환하여 보여주기 위해서 getpwuid 함수계열을 사용할수 있을겁니다.

    관련하여 Linux Kernel Source에서 Documentation/filesystem/proc.txt 파일을 보시면 도움이 될거 같군요.

    그리고 참고할만한 소스는 busybox source 를 강력히 추천합니다.
    정말로 프로그래밍에 관심있으시다면 busybox source를 꼭 다운로드 받아서 훝어보세요. (http://www.busybox.net/)

  4. 장인정신 2009/11/10 11:32  댓글주소  수정/삭제  댓글쓰기

    윗 소스보다 좀더 발전된 형태의 소스는
    http://source.minzkn.com/viewvc/opensource/mzps/trunk/
    에서 볼수 있습니다.
    이 발전된 소스는 라이브러리 형태로 사용가능하게 만들어져 있으니 보기 편할겁니다.

  5. 김원경 2009/11/26 18:51  댓글주소  수정/삭제  댓글쓰기

    ps명령어 구현하는거에 대해 많은 연구도 해보고 했지만
    이번에 처음 리눅스를 배우고 함수를 접하는거라 많은 어려움이있네요...;;
    전체적인것은 대략적으로나마 알아내고 했지만
    s_Length = 32 << 10;
    이부분을 왜 하는지 알고싶습니다.
    그리고 strstr을 써서 문자열을 찾아 반환하는데
    그폴더안에 name이나 state등의 문자열들이 있어서 그 문자열을 찾아서 반환 하는건가요???
    tty나 time 구현하는것도 많은 도전을해보았습니다.
    tty는 getutent()를 이용하여 utmp정보를 얻어오는 와서
    터미널 이름을 얻어올수도 있다고 알아냈습니다.
    하지만 파일을 돌려보면 세그먼트오류가나와서 실패하였습니다.
    tty를 구현할 수 있도록 간단한 코드를 알려주시면 안될까요??
    구현되어있는것도 괜찮습니다.;