본문 바로가기
개발일지/기타

Node.js의 내부 작동 방식을 이해하기

by Peter.JH 2024. 8. 17.
728x90
반응형

 

Node.js의 내부 작동 방식을 이해하기 위해, Node.js의 핵심 구성 요소인 V8 엔진, 이벤트 루프, 그리고 명령줄 옵션 파서에 대해 설명하겠습니다. 이를 위해 Node.js의 소스 코드 일부를 분석하며, 각 구성 요소가 어떻게 구현되어 있는지 살펴보겠습니다.

 

 

1. V8 엔진과 메모리 관리

V8 엔진이란?

V8은 구글이 개발한 오픈 소스 자바스크립트 엔진으로, 자바스크립트 코드를 기계어로 컴파일하여 실행합니다. Node.js는 이 V8 엔진을 사용하여 서버 측에서 자바스크립트를 실행합니다. Node.js는 C++로 작성된 코드를 통해 V8 엔진과 상호작용합니다.

 

 

메모리 관리: NodeArrayBufferAllocator ( node_internals.h )

NodeArrayBufferAllocator 클래스는 Node.js와 V8 엔진 간의 메모리 할당을 관리하는 중요한 부분입니다. 이 클래스는 ArrayBufferAllocator를 상속받아 V8에서 사용할 메모리 블록을 할당하고, 재할당하며, 해제하는 역할을 합니다. 

class NodeArrayBufferAllocator : public ArrayBufferAllocator {
 public:
  inline uint32_t* zero_fill_field() { return &zero_fill_field_; }

  void* Allocate(size_t size) override;  // Defined in src/node.cc
  void* AllocateUninitialized(size_t size) override;
  void Free(void* data, size_t size) override;
  void* Reallocate(void* data, size_t old_size, size_t size) override;
  virtual void RegisterPointer(void* data, size_t size) {
    total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
  }
  virtual void UnregisterPointer(void* data, size_t size) {
    total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
  }

  NodeArrayBufferAllocator* GetImpl() final { return this; }
  inline uint64_t total_mem_usage() const {
    return total_mem_usage_.load(std::memory_order_relaxed);
  }

 private:
  uint32_t zero_fill_field_ = 1;  // Boolean but exposed as uint32 to JS land.
  std::atomic<size_t> total_mem_usage_ {0};

  // Delegate to V8's allocator for compatibility with the V8 memory cage.
  std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_{
      v8::ArrayBuffer::Allocator::NewDefaultAllocator()};
};

 

 

  • Allocate: 지정된 크기의 메모리를 할당합니다. 이 메서드는 AllocateUninitialized와 달리 메모리를 0으로 초기화합니다.
  • Free: 할당된 메모리를 해제합니다.
  • Reallocate: 기존 메모리 블록의 크기를 조정하여 메모리를 재할당합니다.
  • RegisterPointer / UnregisterPointer: 할당된 메모리의 총 사용량을 추적합니다. 이를 통해 Node.js는 전체 메모리 사용량을 관리할 수 있습니다.

 

 

이 클래스는 V8 엔진에서 사용하는 메모리 할당기를 나타냅니다. Allocate, Free, Reallocate 등의 메서드는 V8 엔진에서 메모리를 할당하고 해제하는 과정을 정의합니다. Node.js는 이를 통해 V8에서 사용하는 메모리를 효율적으로 관리합니다.

 

 

 

2. 이벤트 루프와 비동기 콜백

이벤트 루프란?

Node.js의 이벤트 루프는 자바스크립트 코드의 비동기 작업을 처리하는 핵심 메커니즘입니다. 비동기 작업은 이벤트 루프에 의해 큐에 등록되며, 메인 스레드는 큐에 있는 작업을 순차적으로 처리합니다.

 

 

InternalCallbackScope 클래스

InternalCallbackScope 클래스는 비동기 작업이 완료된 후 자바스크립트 콜백을 실행하는 스코프를 관리합니다. 이 클래스는 비동기 작업 중에 발생할 수 있는 여러 상황을 제어하며, 특정 작업이 완료된 후 적절한 후크(hook)와 태스크 큐가 호출되도록 보장합니다. 

class InternalCallbackScope {
 public:
  enum Flags {
    kNoFlags = 0,
    // Indicates whether 'before' and 'after' hooks should be skipped.
    kSkipAsyncHooks = 1,
    // Indicates whether nextTick and microtask queues should be skipped.
    // This should only be used when there is no call into JS in this scope.
    // (The HTTP parser also uses it for some weird backwards
    // compatibility issues, but it shouldn't.)
    kSkipTaskQueues = 2
  };
  InternalCallbackScope(Environment* env,
                        v8::Local<v8::Object> object,
                        const async_context& asyncContext,
                        int flags = kNoFlags);
  // Utility that can be used by AsyncWrap classes.
  explicit InternalCallbackScope(AsyncWrap* async_wrap, int flags = 0);
  ~InternalCallbackScope();
  void Close();

  inline bool Failed() const { return failed_; }
  inline void MarkAsFailed() { failed_ = true; }

 private:
  Environment* env_;
  async_context async_context_;
  v8::Local<v8::Object> object_;
  bool skip_hooks_;
  bool skip_task_queues_;
  bool failed_ = false;
  bool pushed_ids_ = false;
  bool closed_ = false;
};

 

 

  • InternalCallbackScope: 이 생성자는 콜백이 실행되는 환경(Environment)과 관련된 객체 및 비동기 컨텍스트를 설정합니다. flags 매개변수를 통해 후크와 태스크 큐의 실행을 제어할 수 있습니다.
  • Close: 이 메서드는 스코프를 종료하고, 관련 자원을 정리합니다. 일반적으로 비동기 작업이 끝난 후 호출됩니다.
  • Failed / MarkAsFailed: 작업이 실패했는지 여부를 확인하고, 실패한 경우 이를 기록합니다

 

 

이 클래스는 비동기 작업이 완료된 후 자바스크립트 콜백을 실행하는 스코프를 관리합니다. Node.js는 이 스코프를 사용하여 비동기 작업과 관련된 후크(hook)와 태스크 큐를 관리하며, 비동기 작업이 올바르게 처리되도록 보장합니다.

 

 

 

3. 명령줄 옵션 처리

2024.08.16 - [개발일지] - Node.js의 내부 구조를 이해하기

이전 글에서 나온 OptionsParser를 통해 V8 엔진이나 Node.js 런타임에 전달합니다. 이를 통해 Node.js는 다양한 설정을 기반으로 실행될 수 있습니다.

 

 

 

 

- V8 엔진은 Node.js는 NodeArrayBufferAllocator 클래스를 통해 V8 엔진과 상호작용하며, 메모리 관리 작업을 수행합니다. 이 클래스는 메모리 할당, 해제 및 재할당을 관리하며, V8 엔진과의 호환성을 유지합니다.

 

- 이벤트 루프는 InternalCallbackScope 클래스는 Node.js의 비동기 작업과 콜백을 관리합니다. 이 클래스는 비동기 작업이 안전하게 처리되도록 보장하며, 태스크 큐와 후크의 실행을 제어합니다.

 

이러한 구성 요소들이 상호작용하여 Node.js가 동작하게 되며, 이를 통해 고성능의 비동기 I/O 기반 서버 환경을 제공합니다.

 

728x90
반응형