V8 - Build system

GN

V8은 GN의 도움을 받아 빌드된다. GNmeta build system이다. 다른 빌드 시스템을 위한 빌드파일을 생성한다. 따라서 빌드 자체는 어떤 빌드 시스템과 컴파일러를 사용하는지에 의해 결정된다.

GN에 대한 더 자세한 정보 ➨ Chromium’s documentation 또는 GN’s own docs

V8을 빌드하는 과정은 다음과 같다.

  1. generating build files (gn)
  2. compiling
  3. running test

gm은 이 세 단계를 실행해주는 helper script이다.

Building V8 using gm

gm은 빌드 파일들을 생성하고 선택적으로 테스트를 실행해주는 편리한 all-in-one script이다. 위치는 tools/dev/gm.py이다. alias를 등록해두면 위치를 기억하지 않아도 실행할 수 있다.

$ alias gm='/path/to/v8/tools/dev/gm.py'

alias를 등록해두었다면 아래와 같이 명령어를 입력하면 된다.

gm.py [<arch>].[<mode>[-<suffix>]].[<target>] [testname...] [--flag]

모든 argument들은 생략 가능하며 대부분의 조합에 대해 작동한다.

$ gm.py ia32.debug x64.release x64.release-my-custom-opts d8
$ gm.py android_arm.release.check --progress=verbose
$ gm.py x64 mjsunit/foo cctest/test-bar/*

arch

mode

target

testname

subprocess 모듈

def _CallWithOutput(cmd):
  print("# %s" % cmd)
  # The following trickery is required so that the 'cmd' thinks it's running
  # in a real terminal, while this script gets to intercept its output.
  parent, child = pty.openpty()
  p = subprocess.Popen(cmd, shell=True, stdin=child, stdout=child, stderr=child)
  os.close(child)
  output = []
  try:
    while True:
      try:
        data = os.read(parent, 512).decode('utf-8')
      except OSError as e:
        if e.errno != errno.EIO: raise
        break # EIO means EOF on some systems
      else:
        if not data: # EOF
          break
        print(data, end="")
        sys.stdout.flush()
        output.append(data)
  finally:
    os.close(parent)
    p.wait()
  return p.returncode, "".join(output)

아래의 편법(trickery)은 ‘cmd’가 실행 결과는 이 스크립트에 전달하면서 실제 터미널에서 실행되는 것처럼 느끼게 한다.

call('cmd', shell=True)

  • shell = True - 전달된 명령(cmd)이 shell을 통해 실행된다. shell을 통해 명령어를 실행하기 때문에 system에 의존하게 되지만 shell의 pipe, 파일 이름에 대한 여러 기능(와일드카드 등), 환경 변수 등을 사용할 수 있다. 하지만 이런 기능들과 유사한 API(glob fnmatch os.walk() os.path.expandvars() os.path.expandusershutil)를 파이썬에서 제공하기 때문에 shell=False이 권장된다. shell=True or False

pty

pseudo-terminal 개념을 다루기 위한 연산이 정의된 모듈이다. controlling terminal을 사용하여 코드를 통해(programmatically) 프로세스를 생성하고 해당 프로세스에 입력을 넣고 출력을 받을 수 있다.
Pseudo-terminal handling은 platform에 의존한다. 지금까지 Linux, FreeBSD, macOS에서 검증되었다.

  • pty.fork() child의 controlling terminal을 psudo-terminal에 연결한다. 반환값은 (pid, fd)이다. child는 pid 0를 받고 fd에는 접근할 수 없다. parent는 child의 pid와 child에 연결된 controlling terminal에 대한 fd를 받는다. controlling terminal을 통해 child의 stdin/out에 접근할 수 있게 된다.

  • pty.openpty() pseudo-terminal 쌍을 open한다. 가능하다면 os.openpty()를 사용하는 것이 좋다. 한 쌍의 file descriptor (master, slave)를 반환받는다.

  • pty.spawn(argv[, master_read[, stdin_read]]) 프로세스 하나를 생성(spawn)하고 생성된 프로세스의 controlling terminal을 현재 프로세스의 standard io에 연결한다. pty 뒤에 존재하는 새로 생성된 프로세스는 언젠가 종료되어야 한다. spawned 프로세스가 종료되어야 이 함수가 종료(return)되기 때문이다. Python 3.4부터 spawn() 함수는 os.waitpid()와 같은 값(생성된 프로세스의 종료상태)을 반환한다.

fork vs spawn

fork - 생성된 child는 부모의 모든 리소스를 복제한다. spawn - child를 생성한 뒤 명령어를 실행한다. 최소한의 리소스만 복제한다.

psuedo-terminal

프로세스 입장에서는 터미널로 보이지만, 터미널은 아니다. 프로세스 사이의 통신에 사용된다.
터미널은 stdin/out/err를 합친 입출력의 단위이다.

pseudo-terminal

Popen

pty 모듈의 underlying process 생성과 관리는 모두 Popen 객체를 통해 처리된다. 이 객체를 사용하면 convinience 함수들이 다루지 않는 좀 더 특수한 상황들을 핸들링 할 수 있다.

class subprocess.Popen(args, bufsize=- 1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=- 1, encoding=None, errors=None, text=None, pipesize=- 1, process_group=None)

새로운 프로세스에서 child 프로그램을 실행한다. 자세한 사용 방법은 공식 문서 참고

  • stdin stdout stderr - 실행되는 프로그램(child)의 standard in/out/err 에 대한 파일 handle이다. file object 또는 유효한 file descriptor(양의 정수)를 넘겨주면 된다.

TTY & PTY

Terminal이란 시스템으로 입력값을 전달하고 출력값을 제공받는 추상적인 개념이다.
Terminal에는 여러 종류가 있다.

  • Hardware terminal - teletypewriters, hard-copy, video display unit 등등
  • Software terminal - 운영체제의 주요 인터페이스인 virtual TeleTYpe(TTY)
  • Software pseudo-terminal - TTY를 모방(emulate)하는 PseudoTeletYpe (PTY)
  • Software terminal emulator - software pseudo-terminal과 유사하지만 GLI를 통해 강화된 인터페이스

What is TTY?

TTY(TeleTYpe Writer)는 멀리 떨어진 상대(end point)에게 전달할 데이터를 입력하게 해주는 장치이다. 20세기 중반에 사용되었다.
현대의 OS는 이 개념을 이어 받아 사용하고 있다. Linux는 OS와의 상호작용(interaction)을 매개하는 virtual TTY를 device file로 표현한다. TTY의 개수에는 제한이 있다.

What is PTY?

pseudo-TTY의 준말이다. 어떤 종류의 두 endpoints에 대해 TTY와 같이 동작한다. 하나의 TTY에 대해 여러 개의 PTY가 존재할 수 있다.
PTY에는 양 끝점이 있는데 각각 이름을 가진다.

  • slave - /dev/pts/에 존재하는 파일들(/dev/pts/#)에 의해 표현(represent)된다.
  • master - PTY를 요청한 프로세스에 대해 file descriptor 형태로 존재한다.

하나의 TTY에 여러 개의 PTY를 대응시키기 위해 사용되는 것이 pseudo-terminal multiplexor 장치(/dev/ptmx/에 존재하는 파일)이다. 이것을 사용하기 위한 절차는 다음과 같다.

  1. process가 /dev/ptmx를 오픈한다.
  2. OS가 master ptm의 file descriptor를 반환한다.
  3. OS는 master에 대응되는 /dev/pts/#라는 이름의 slave pseudo-device를 생성한다.
  4. 이후에는 slave의 input은 master에게 전달되고, master의 input은 slave에게 전달된다.

PTY는 pipe와 유사하게 양방향 communication을 가능하게 해준다. pipe와 다른 점은 PTY는 터미널을 필요로 하는 모든 프로세스에 터미널을 제공한다. PTY는 프로세스 사이의 통신, 프로세스와 OS 사이의 통신에 이용된다.

PTY diagram

reference

Comments