Skip to content

Instantly share code, notes, and snippets.

@YangSiJun528
Last active December 31, 2024 01:44
Show Gist options
  • Select an option

  • Save YangSiJun528/ee1f93975083ffd41f580c19e2eb1bbd to your computer and use it in GitHub Desktop.

Select an option

Save YangSiJun528/ee1f93975083ffd41f580c19e2eb1bbd to your computer and use it in GitHub Desktop.
리눅스 태스크 설명.md

섹션 13 - 12. 태스크의 모든 정보는 여기에 있다!

  • C에서 구조체는 메모리 상에 일정한 형식으로 배열된 바이트의 집합임.

    • 같은 메모리 데이터를 서로 다른 구조체 정의로 강제로 캐스팅하거나 접근할 경우, 결과가 비정상적이거나 예상과 다를 수 있음.
    • (메모: 다른 언어의 경우 에러가 나거나 뭔가 처리를 해주긴 하는데 (그래도 이상하게 동작할 수는 있음), C는 언어 상의 이러한 보호가 거의 없음. 직접 해야함.)
  • 태스크 디스크립터

    • 태스크의 디스크립터로 task_struct 구조체를 사용함.
    • 디스크립터: 어떤 자원이나 객체에 대한 정보를 포함하는 데이터 구조
  • task_struct 구조

    • 소스코드: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L746
      • 매크로나 주석이 있어서 긴것도 있지만, 구조체 정의에 700줄이 넘어간다.
    • 핵심 변수와 역할 정리
      • 프로세스의 이름과 그걸 변경하는 함수
      • TODO: pid
      • TODO: tgid
      • __state
        • https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L85 부터 매크로로 정의된 특정 의미를 가지는 unsigned_int 값이 들어감.
          • TODO: 대표적인 값과 그게 무슨 상태인지도 이야기
          • 리눅스 시스템에서 상태가 인터럽터블 상태가 대부분임. 러닝이나 언인터럽터블이 많으면 뭔가 시스템이 문제가 있을수도 있음.
      • flags
      • eixt_state
        • TODO: 대표적인 값과 그게 무슨 상태인지도 이야기
      • real_parent
      • parent
        • 자신을 생성한 프로세스가 죽은 경우, do_exit -> exit_notify -> forget_original_parent -> find_new_reaper 과정을 통해 새로운 부모 프로세스를 지정함.
      • children
        • 타입이 list_head인데, 보면 prev, next 가지는 양뱡향 링크드리스트임.
      • TODO: sibing
      • TODO: tasks
    • TODO: 프로세스의 linked list의 구성 예 설명
      • TODO: sibing, - children, tasks 가 어떤 식으로 연결되는건지.
    • TODO: utime:
      • u64는 일반적으로 나노초를 나타내는데 사용함.
    • TODO: stime:
    • TODO: sched_info
    • TODO: thread_info
    • TODO: 등 핵심적인거 추가
  • ps를 치면 구동중인 프로세스가 나오는데, init_task 라는 대스크 디스크립터의 연결리스트인 tasks 필드(init_task.tasks)에 접근하여 확인.

  • 커널 분석 시 태스크 디스크립터를 중심으로 보면 좋다. 커널은 프로세스를 중심으로 중요한 데이터를 저장하고 불러오기 떄문.


섹션 13 - 12. 태스크의 모든 정보는 여기에 있다!


1. 구조체와 메모리

  • 구조체의 본질
    • C 언어에서 구조체는 메모리상에서 일정 형식으로 배열된 바이트 집합.
    • 같은 메모리 데이터를 다른 구조체 정의로 강제 캐스팅하거나 접근 시, 결과가 비정상적이거나 예상과 다를 수 있음.
      • 참고: 다른 언어는 이러한 상황에서 에러를 발생시키거나 보호 매커니즘이 있을 수 있지만, C에서는 직접 처리해야 함.

2. 태스크 디스크립터

  • 태스크 디스크립터 정의

    • 태스크 디스크립터는 태스크에 대한 정보를 담고 있는 데이터 구조.
    • 리눅스 커널에서는 task_struct 구조체가 태스크 디스크립터로 사용됨.
  • task_struct의 주요 정보

    • 소스 코드: sched.h
      • 구조체 정의에만 약 700줄 이상 소요됨.
    • 핵심 필드
      • 프로세스 이름 및 변경 함수
      • pid
        • 프로세스 고유 식별자.
      • tgid
        • 프로세스 그룹 ID.
      • __state
        • 프로세스 상태를 나타내는 unsigned_int 값.
        • 참조 코드
        • 주요 상태:
          • TASK_RUNNING: 실행 가능 상태.
          • TASK_INTERRUPTIBLE: 인터럽트 가능한 대기 상태.
          • TASK_UNINTERRUPTIBLE: 인터럽트 불가 대기 상태.
      • flags
        • 참조 코드
        • 주요 플래그:
          • PF_EXITING: 프로세스 종료 중.
          • PF_FORKNOEXEC: 자식 생성 후 실행되지 않은 상태.
      • exit_state
        • 프로세스 종료 상태를 나타냄.
        • 주요 값:
          • EXIT_ZOMBIE: 좀비 상태.
          • EXIT_DEAD: 종료된 상태.
      • real_parentparent
        • 현재 프로세스를 생성한 부모 프로세스를 가리킴.
        • 부모 프로세스 종료 시, 새로운 부모를 지정하는 과정:
          • do_exitexit_notifyforget_original_parentfind_new_reaper.
      • children
        • 프로세스의 자식 리스트.
        • 이중 연결 리스트(list_head)로 관리되며, prevnext 필드를 가짐.
      • sibling
        • 형제 프로세스를 가리키는 리스트.
      • tasks
        • 프로세스가 연결된 리스트. 커널의 모든 태스크는 이 리스트로 연결됨.
    • 프로세스 연결 구조
      • 태스크 디스크립터의 sibling, children, tasks 필드는 리스트로 연결되어, 프로세스 간의 관계를 형성.
      • ps 명령이 이걸 사용함.
    • 시간 관련 필드
      • utime
        • 유저 모드에서 소모한 시간. 일반적으로 나노초 단위.
      • stime
        • 커널 모드에서 소모한 시간.
    • 기타 필드
      • sched_info
        • 스케줄링 관련 통계 정보.
      • thread_info
        • 아키텍처 종속적 정보 포함.

3. thread_info의 역할

  • 설명
    • 특정 아키텍처 종속 데이터 관리.
    • 선점 스케줄링 가능 여부, 시그널 전달 상태, 인터럽트 컨텍스트 등을 확인.
    • 과거에는 많은 데이터를 thread_info에서 다뤘지만, 일부는 thread_struct로 이동.
  • 주요 필드
    • flags
      • 인터럽트 처리, 선점 스케줄링, 시스템 콜 트레이싱 여부를 나타냄.
    • preempt_count
      • 선점 관련 카운터.
    • cpu
      • 프로세스가 실행 중인 CPU 번호.
    • scs_base, scs_sp
      • Shadow Call Stack의 시작 주소와 스택 포인터.

4. 재스케줄링과 컨텍스트 스위칭

  • 재스케줄링
    • 현재 태스크를 종료하고, 다른 태스크를 실행 가능한 상태로 전환.
  • 컨텍스트 스위칭 과정
    • 소스 코드: core.c
    • 레지스터 값을 저장 및 복원하는 매크로: THREAD_CPU_CONTEXT.
      • offsetof(struct task_struct, thread.cpu_context) 형태.
      • cpu_context는 레지스터 값을 그대로 포함.

5. ps 명령과 태스크 디스크립터

  • ps 명령을 실행하면 구동 중인 모든 프로세스를 나열.
  • init_task 디스크립터의 연결 리스트인 tasks 필드를 통해 프로세스를 탐색.

6. 커널 분석 시 관점

  • 태스크 디스크립터는 커널 분석의 핵심.
  • 커널은 프로세스를 중심으로 중요한 데이터를 저장하고, 필요 시 불러오는 구조.
  • 태스크 디스크립터와 task_struct
  1. 태스크 디스크립터의 정의

    • C에서 구조체는 메모리 상에 일정한 형식으로 배열된 바이트의 집합임
    • 태스크의 디스크립터로 task_struct 구조체를 사용함
    • 디스크립터: 어떤 자원이나 객체에 대한 정보를 포함하는 데이터 구조
  2. task_struct 구조

    • 소스코드: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L746
    • 구조체 정의에 700줄이 넘는 방대한 크기
    • 핵심 변수와 역할:
      • 프로세스의 이름과 그걸 변경하는 함수
      • pid: 프로세스 식별자
      • tgid: 스레드 그룹 ID
      • __state: 프로세스 상태
        • 대표적인 값: TASK_RUNNING, TASK_INTERRUPTIBLE, TASK_UNINTERRUPTIBLE 등
        • 리눅스 시스템에서 상태가 인터럽터블 상태가 대부분임. 러닝이나 언인터럽터블이 많으면 시스템에 문제가 있을 수 있음
      • flags: 프로세스 플래그
        • 대표적인 값: PF_KTHREAD (커널 스레드), PF_EXITING (종료 중), PF_FORKNOEXEC (fork 후 exec 전) 등
      • exit_state: 프로세스 종료 상태
        • 대표적인 값: EXIT_ZOMBIE, EXIT_DEAD 등
      • real_parent: 실제 부모 프로세스
      • parent: 현재 부모 프로세스
      • children: 자식 프로세스 리스트
      • sibling: 형제 프로세스 리스트
      • tasks: 전체 프로세스 리스트
    • 프로세스의 linked list 구성:
      • sibling, children, tasks가 양방향 링크드 리스트로 연결됨
    • utime: 사용자 모드에서 실행된 시간 (나노초)
    • stime: 시스템(커널) 모드에서 실행된 시간
    • sched_info: 스케줄링 관련 정보
    • thread_info: 아키텍처 종속적인 스레드 정보
  3. 프로세스 리스트 접근

    • ps 명령어로 구동 중인 프로세스 확인 가능
    • init_task 태스크 디스크립터의 tasks 필드(init_task.tasks)에 접근하여 프로세스 리스트 확인
  4. 커널 분석 시 태스크 디스크립터 중요성

    • 커널은 프로세스를 중심으로 중요한 데이터를 저장하고 불러오기 때문에 태스크 디스크립터를 중심으로 분석하면 효과적
  • thread_info 구조체
  1. thread_info 개요

    • 특정 아키텍처에 종속적인 데이터를 관리
    • 아키텍처마다 다른 구현을 함
  2. thread_info의 주요 역할

    • 선점 스케줄링 실행 여부 확인
    • 시그널 전달 여부 확인
    • 인터럽트 컨텍스트와 Soft IRQ 컨텍스트 상태 관리
    • 현재 실행 중인 코드가 인터럽트 컨텍스트인지 확인 (flag 값)
    • 현재 프로세스가 선점 가능한 조건인지 확인 (flag 값)
    • 프로세스가 시그널을 받았는지 확인
  3. thread_info 위치

    • {linux_root)/arch/{아키텍처_이름}/include/asm/thread_info.h에 위치
  4. arm64 thread_info 분석

  5. flags 필드의 중요성

    • 프로세스는 자신의 flags 값을 수시로 체크하여 다음 상황을 파악:
      • 시그널이 자신에게 전달되었는지
      • 선점 스케줄링될 조건인지
      • 시스템 콜 트레이싱 조건인지
  • 컨텍스트 스위칭 과정
  1. 재스케줄링 정의

    • 현재 태스크를 종료하고 새로운 태스크로 실행 가능한 상태로 전환하는 과정
  2. 컨텍스트 스위칭 과정

    • switch_to() 호출 -> __switch_to() 호출
    • __switch_to()는 아키텍처에 따라 구현이 다름 -> cpu_switch_to() 호출
    • cpu_switch_to()는 어셈블리로 작성됨
    • THREAD_CPU_CONTEXT(레지스터 값)를 저장하고 스위치할 모델로 변경
    • THREAD_CPU_CONTEXT는 매크로, 실제로는 offsetof(struct task_struct, thread.cpu_context) 형식
    • task_structthread_struct를 가짐
    • thread_struct가 가지는 cpu_context는 레지스터 값을 그대로 가짐
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment