ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [컴퓨터 밑바닥의 비밀] 2.2 스레드 간 공유되는 프로세스 리소스
    Study/컴퓨터 밑바닥의 비밀 2025. 3. 1. 21:35

    2.2.1 스레드 전용 리소스

    • 상태 변화 관점에서 보면 스레드는 '함수 실행'
    • 함수 실행에는 항상 하나의 시작점이 존재하고, 이 시작점이 진입 함수임
    • CPU는 하나의 진입 함수의 명령어를 실행하여 실행 흐름인 스레드를 생성

     

    Q. 함수 실행에는 어떤 종류의 정보가 존재할까?

    • 함수의 실행 시간 정보는 스택 영역을 구성하는 스택 프레임에 저장

    • 스택 프레임에는 함수의 반환값, 다른 함수를 호출할 때 전달되는 매개변수, 함수 내에서 사용되는 지역 변수, 레지스터 정보가 저장
    • 스레드는 자신만 사용할 수 있는 스택 영역을 가짐

     

    Q. 스레드 전용 리소스는 어떤 것일까?

    • 스레드에 속한 스택 영역, PC 레지스터(프로그램 카운터), 스택 포인터, 함수 실행 시 사용되는 레지스터 정보가 모두 스레드 전용
      • PC 레지스터 : 다음에 실행될 명령어 주소 저장
      • 스택 포인터 : 스레드 스택 영역에서 스택 상단(stack pop) 위치 저장
      • CPU가 기계 명령어를 실행할 때 사용하는 내부 레지스터 정보
    • 이 모든 정보를 통틀어 스레드 상황 정보(thread context)라고 함
    • 스레드는 프로세스 주소 공간에서 스택 영역을 제외한 나머지 영역을 모두 공유

     

    2.2.2 코드 영역: 모든 함수를 스레드에 배치하여 실행할 수 있다.

    • 프로세스 주소 공간의 코드 영역에는 프로그래머가 작성한 코드를 컴파일한 후 생성된 실행 가능한 기계 명령어가 저장됨
      • 기계 명령어는 실행 파일에 저장되어 있고, 프로그램이 시작될 때 프로세스 주소 공간에 적재됨

    • 코드 영역은 스레드 간 공유되므로 어떤 함수든지 모두 스레드에 적재하여 실행할 수 있음
      • 특정 함수를 특정 스레드에서만 실행되도록 하는 것을 불가능
    • 코드 영역은 읽기 전용(read-only)이기 때문에 프로그램이 실행되는 동안 어떤 스레드도 코드 영역 내용을 변경할 수 없음
      • 프로그램의 올바른 실행을 위해 당연한 것
    • 따라서 프로세스 내 모든 스레드가 코드 영역을 공유하지만, 코드 영역에 관해서는 스레드 안전 문제(thread safety issue)가 발생하지 않음

     

    2.2.3 데이터 영역: 모든 스레드가 데이터 영역의 변수에 접근할 수 있음

    • 데이터 영역은 전역 변수가 저장되는 곳
    • 프로그램이 실행되는 동안 데이터 영역 내에 전역 변수의 인스턴스는 하나만 있기 때문에 모든 스레드는 이 전역 변수에 접근할 수 있음
      • 스레드가 전역 변수 값 변경 가능

     

    2.2.4 힙 영역: 포인터가 핵심이다

    • C/C++ 언어에서 malloc 함수와 new 예약어로 요청하는 메모리가 이 영역에 할당됨
    • 모든 스레드는 (해당 메모리 주소를 가리키는) 포인터를 얻을 수 있다면 포인터가 가리키는 데이터에 접근할 수 있음
    • 따라서 힙 영역은 스레드 간 공유 리소스

     

    2.2.5 스택 영역: 공유 공간 내 전용 데이터

    • 서로 다른 프로세스의 주소 공간은 서로 격리되어 있고, 가상 메모리 시스템은 매우 특별한 경우를 제외하고 다른 프로세스의 주소 공간에 속한 데이터에 직접 접근하지 못하도록 보장
    • 서로 다른 스레드의 스택 영역 간에는 이런 보호를 위한 장치가 존재하지 않음
      • 즉, 실제 구현 측면에서 스택 영역은 엄밀하게 격리된 스레드 전용 공간은 아님
      • 하나의 스레드가 다른 스레드의 스택 프레임에서 포인터를 가져올 수 있다면, 해당 스레드는 다른 스레드의 스택 영역을 직접 읽고 쓸 수 있음

     

    예시)

    void foo(int* p)
    {
        *p = 2;
    }
    
    int main()
    {
        int a = 1;
        thread t(foo, &a);
        
        t.join();
        
        return 0;
    }
    • main thread의 지역 변수인 a의 주소가 매개변수 형태로 새로 생성되는 스레드에 전달됨
    • 새 스레드의 진입 함수인 foo가 다른 스레드에서 실행되면서 매개변수를 통해 지역 변수 a의 포인터인 p를 얻게 됨

     

    2.2.6 동적 링크 라이브러리와 파일

    • 동적 링크 라이브러리
      • 동적 링크는 실행 파일에 종속된 라이브러리의 코드와 데이터가 포함되어 있지 않기 때문에 프로그램을 시작할 때 or 실행 중일 때 종속된 라이브러리의 코드와 데이터를 찾아 프로세스 주소 공간에 넣는 링크 과정이 완료되어야 함
      • 참고) 링크
        • 컴파일 후 최종적으로 실행 파일을 생성하는 단계
        • 유형 2가지
          • 정적 링크
            • 종속된 모든 라이브러리가 실행 파일에 포함되는 것
            • 프로그램의 실행 파일에 모든 코드와 데이터가 포함되어 있어 프로그램 시작할 때 추가적인 작업 필요없음
          • 동적 링크
            • 실행 파일에 종속된 라이브러리의 코드와 데이터가 포함되어 있지 않음
    • 파일
      • 프로그램이 동작 중 특정 파일을 열면 프로세스 주소 공간에 열린 파일 정보도 저장됨
      • 프로세스가 연 파일 정보는 모든 스레드에서 사용할 수 있어 스레드 간 공유 리소스에 속함

     

    Q. 동적 링크되는 라이브러리의 코드와 데이터는 프로세스 주소 공간 중 어디에 놓여야 할까?

    • 힙 영역과 스택 영역 중간에 있는 여유 공간에 배치
    • 주소 공간에서 해당 부분은 모든 스레드가 공유하므로, 프로세스 내 모든 스레드가 동적 라이브러리 코드의 데이터를 사용할 수 있음

     

    2.2.7 스레드 전용 저장소

    • 스레드 전용 저장소(thread local storage)에 저장되는 변수는 다음과 같은 2가지 의미가 있음
      1. 이 영역에 저장된 변수는 모든 스레드에서 접근할 수 있다.
      2. 모든 스레드가 동일한 변수에 접근하는 것처럼 보이지만, 실제 변수의 인스턴스는 각각의 스레드에 속한다. 따라서 하나의 스레드에서 변수 값을 변경해도 다른 스레드에는 반영되지 않는다.

     

    예시)

    __thread int a = 1; // 스레드 전용 저장소
    • 전역 변수 a 앞에 __thread 수식어를 붙여 컴파일러가 전역 변수 a를 스레드 전용 저장소에 넣도록 지시하는 것

    • 스레드 전용 저장소를 사용하면 각 스레드에서 독점적으로 변수를 사용할 수 있음
    • 즉, 이 변수들은 모든 스레드에서 접근할 수 있지만 해당 변수는 초기화한 후 각각의 스레드가 복사본을 가지게 됨
      • 하나의 스레드에서 변수 값을 변경해도 다른 스레드에 영향 X

    댓글

Designed by Tistory.