Process Manipulation#

프로세스란 실행되고 있는 프로그램을 말한다(자세한 내용은 다른 문서를 참고한다). 이런 프로세스는 여러가지로 쓰임새가 많다. 대표적인 예로, 데몬을 들 수 있다. 데몬은 사용자 모르게 내부적으로 수행하며 각종 작업을 처리한다. Windows에서 좋은 예는 explore, svchost, winlogin 등을 들 수 있다. 특히 svchost는 사용자가 실행하기를 원하는 각종 서비스를 수행하는 데몬이다. Linux는 inetd등과 같은 데몬이 있다. 이와 같이 데몬은 여러모로 유용하게 쓰이는데, 이러한 프로세스 다루기의 정점은 Semapore, Lock 등을 통한 Critical Section 관리와 프로세스간 Communication이다.

이 문서에서는 간단하게 데몬을 생성하는 법을 다룬다.

 

Daemon#

먼저 데몬에 대해서 간략하게 설명하면, 데몬이란 독립적으로 실행되는 프로세스이다. 이런 프로세스가 되기 위해서는 다음과 같은 과정을 거친다.

  1. 부모 프로세스를 실행한다.
  2. 자식 프로세스가 생성된다.
  3. 자식 프로세스를 독립화 하고 작업을 할당한다.
  4. 부모 프로세스는 자식 프로세스가 종료하기를 기다리지 않고 종료한다.
  5. 자식 프로세스는 데몬화 하여 동작한다.

이러한 과정을 거쳐 데몬이 동작하게 된다. 이런 데몬 프로세스를 작성하는 방법은 운영체제마다 틀리며,각 운영체제에 적합한 방법을 사용해야 한다. 본 문서는 Linux 환경에서 데몬을 생성하는 방법에 대해서 다룬다.먼저 부모 프로세스를 생성하기 위해서는 fork()를 사용하며, 독립화 하기위해서 setsid()를 사용한다. 1번 과정에서는 fork()를 실행하여 부모 프로세스를 만들고, setsid()를 사용하여 앞으로 생성될 자식 프로세스를 독립화한다. 이후, 자식 프로세스를 하나 생성하고 작업을 할당한 후에 프로그램과 부모 프로세스는 종료한다.

 

Cron Table#

사실 cron table을 사용하면 root 계정에서 특정 user 계정으로 일정 주기별로 프로그램 실행이 가능하다. 이는 Daemon을 굳이 작성하지 않아도 된다는 이야기이다.즉, 일정 주기를 가지고 작업이 실행된다면 굳이 데몬을 작성하지 않고 데몬과 유사하게 프로그램을 수행할 수 있다. 하지만 이 경우 단점은 cron을 root만 설정할 수 있기 때문에 일반 사용자가 접근하기 힘들다. 따라서 이 부분은 배제 하였다.

 

Create Daemon#

Daemon절에서 언급한 내용을 Python으로 구현하면 아래와 같다. Sample 클래스는 Daemon 클래스 테스트 용도로 작성되었으며 실제 구현 코드는 Daemon 클래스이다. Daemon 클래스를 사용하기 위해서는 아래 __main__ 프로시저에서 사용한 것과 같이 therad를 하나 생성하여 인자로 넘겨주면 된다

  1. #!/usr/bin/python
    # -*- coding: utf-8

    import os, time, sys, threading

    class NullDevice(object):
        def write(self, s): pass

    class SampleJob(object):
        def Main(self):
            logfile = open('sample.log', 'a')
            id = 0
            while True:
                logfile.write('log : %d\n' % id)
                logfile.flush()
                time.sleep(10)
                id = id + 1

    class Daemonize(object):
        def __init__(self):
            pass
        def initDaemon(self):
            # stdout, stderr, stdin closing
            sys.stdout = NullDevice()
            sys.stderr = NullDevice()
            sys.stdin.close()

        def createDaemon(self, job):
            self.initDaemon()
            try:
                pid = os.fork()
                if pid > 0:
                    sys.exit(0)
            except OSError, e:
                print >> sys.stderr, "fork #1 failed : %d (%s)" % \
                    (e.errno, e.strerror)
                sys.exit(1)

            os.setsid()
            os.umask(0)

            try:
                pid = os.fork()
                if pid > 0:
                    print "Daemon PID %d" % pid
                    sys.exit(0)
            except OSError, e:
                print >> sys.stderr, "fork #2 failed : %d (%s)" % \
                    (e.errno, e.strerror)
                sys.exit(1)

            job.start()

    if __name__ == '__main__':
        smjob = SampleJob()
        dm = Daemonize()
        th = threading.Thread(target=smjob.Main(), args=())     dm.createDaemon(th)
        sys.exit(0)

 

Resource Manipulation

이 절은 자원관리의 중요성을 설명하기 위한 장이다. 여기서 자원관리라 함은, CPU 자원을 뜻한다. 일반적으로 데몬을 작성할 때 유의해야 하는 사항은 바로 자원관리이다. Daemon이 CPU 자원과 메모리 자원을 90% 이상 소모한다면 그것이 어떻게 Daemon이라 하겠는가? 따라서 CPU를 최대한 작게 사용하면서 자신이 수행해야 하는 작업은 자주 수행하는 것이 좋다.

Daemon이 이런 방식으로 작동하기 위해서는 다음과 같은 원칙을 지켜주어야 한다.

  • Loop를 수행할 때는 반드시 time.sleep()를 사용하여 Step 마다 CPU를 양도할 것
  • 프로그램에서 사용하는 모든 모듈에 위 조항을 적용할 것.

 

설령 이렇게 한다 하더라도 크게 자원을 소모할 수 있다. 이 때는 스스로 관찰하여 최대한 자원소모를 줄이는 방법 밖에 없다. 자원 소모를 줄이는 방법은 대부분 프로그래머의 지식에 의존한다. 프로그래머의 지식이란, 크게 컴퓨터 구조에 관한 지식과 언어 자체에 대한 지식에 대한 것이 있다. 컴퓨터 구조에 관한 지식이란, 컴퓨터 구조에 대해서 잘 알고 거기에 최적화 된 코드를 작성하는 것을 말한다. 이를테면, CPU가 가지는 명령어에 대해서 잘 알고, 그 명령어가 최대한 작게 수행될 수 있는 형태를 말한다. 쉬운 예를 들자면 예전 CPU는 곱하기 연산을 덧셈을 수번 반복하여 수행하였다. 이 경우 CPU는 반복된 작업을 수행하게 된다. 하지만 쉬프트 연산을 사용할 경우 작업 횟수를 크게 줄일 수 있다. 이와 같이 컴퓨터 구조에 대한 지식을 가지고, 거기에 적합한 최적화 코드를 생성하는 것이다. 두 번째로 언어에 대한 지식이 있다. 현 시점에서 수없이 다양한 언어가 존재하며 이 언어는 다양한 형태로 제작되어 졌다. 때문에 언어에 대해서 정통하다면 CPU에서와 마찬가지로 연산 횟수를 크게 줄일 수 있다.

이와 같이 Resoruce를 줄이는 방법은 다양한 형태가 있으며 언어에 대한 지식에 크게 의존하며, 추가적으로 컴퓨터 구조에 관한 지식에 의존한다. 왜냐하면 컴퓨터 구조에 관한 지식보다는 언어에 대한 지식이 더 습득하기 쉬우며(여기에는 개인 차이가 있다) 최적화 하기가 더 용이하다.

 

Python Popen

Python에서 프로세스를 수행하는 방법은 다양한 방법이 있다. popen 함수와 system 함수를 이용하는 방법이 대표적이다. popen 함수는 입출력을 제어 여부에 따라서 popen2, popen3과 같은 함수로 다시 구분된다. 사실 이러한 구분 방법은 Python의 역사를 대변한다고 볼 수 있다. Python은 초기에는 OO 지향적 언어가 아니었으므로 함수이름을 이용하여 구분하였다. 하지만 이런 방법은 프로그래머로 인하여 함수 이름을 암기하는 고통을 증대 시킨다고 볼 수 있다. 따라서 최근 Python은 함수 이름 암기에 대한 부담을 덜고, 인자(Parameter)를 조절하여 역활을 구분하는 형태의 OO 지향적 모습을 많이 볼 수 있다. 이는 Ruby와 같은 후발주자에 대한 견제로 볼 수 있다. 한 개발자는 Python과 Ruby가 둘 다 뛰어난 언어임에는 틀림이 없으나, Python이 실행하는 다양한 형태의 함수와 그 다양한 함수를 사용하기 위해서 이름을 암기해야 하는 고통을 예로 들며 Ruby를 선호한다고 이야기 하였다. 이와 같이 함수 이름에 대한 암기 부담은 크게 작용하며 이를 줄이기 위한 노력은 Overloading와 같은 형태로 꾸준히 진화하였다.

Popen은 이러한 최근 Python에 대한 모습을 대변한다. Popen 클래스 하나를 이용하여 subprocess를 쉽게 수행하고 통신(Messaging)할 수 있다. Python Library Reference에서는 이와 같이 Popen을 사용하여 기존의 popen 함수와 system 함수를 대체하는 방법에 대해서 따로 서술한 문서가 있으며 본 절에서는 이 문서에서 서술하는 방법과 새롭게 변화된 프로세스 다루기에 대해서 설명한다.

 

기존 방법 Python 2.3 이하 (os.system)

          sts = os.system("mycmd" + " myarg")
          

새로운 방법 Python 2.4 이상 (Popen class)

          p = Popen("mycmd" + " myarg", shell=True)
          sts = os.waitpid(p.pid, 0)
          
          try:
              retcode = call("mycmd" + " myarg", shell=True)
              if retcode < 0:
                  print >>sys.stderr, "Child was terminated by signal", -retcode
              else:
                  print >>sys.stderr, "Child returned", retcode
          except OSError, e:
              print >>sys.stderr, "Execution failed:", e
          
           
          

코드 자체는 기존 코드가 조금 더 깔끔하다. 하지만 Popen 클래스를 이용하면 기존에 사용하였던 os.system(), popen(), popen2()와 같은 모든 함수를 Popen 클래스를 사용하여 대체 할 수 있다. 아래 예제는 popen 함수에 대한 예제이다.

 

기존방법 Python 2.3 이하 (os.popen)

          pipe = os.popen(cmd, mode='r', bufsize)
          

새로운 방법 Python 2.4 이상 (Popen Class)

          pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
          

이와 같이 Popen 클래스를 사용하면 기존 프로세스 관련 함수를 모두 대체할 수 있다. Popen 클래스는 subprecess 모듈에 포함되어 있으며 2.4 부터 지원한다.

Reference#

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012

http://www.thescripts.com/forum/thread605748.html

http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/2/setsidman/2/setsid

http://bbs.python.or.kr/viewtopic.php?t=22300&amp;highlight=%EB%8D%B0%EB%AA%ACAA%AC

 

Cron Table Reference#

http://blog.naver.com/lukers?Redirect=Log&amp;logNo=70020680512



http://www.py2exe.org 에서 프로그램을 다운받는다. 

설명서대로 한다. 아주 쉽다. py2exe를 설치한 후 python/lib/site-package/py2exe/example 폴더에 보면 예제가 더 많다. 이 파일의 실행은 다음과 같이 한다.

python setup.py py2exe

빌드 중에 unicode 에러가 나오면 다음과 같이 한다.

  • C:\Python24\Lib\site.py 를 연다.
  • encoding = "ascii"를 찾는다 (기본 설정임)
  • 여기에 #를 달아 주석처리하고, 다음과 같이 넣고 저장한 후 다시 실행해본다. encoding = "euc-kr" 이래도 안되면 "utf-8" 그래도 안되면 "uhc"
  • 컴파일이 된다. ^^


+ Recent posts