-
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: 대표적인 값과 그게 무슨 상태인지도 이야기
- 리눅스 시스템에서 상태가 인터럽터블 상태가 대부분임. 러닝이나 언인터럽터블이 많으면 뭔가 시스템이 문제가 있을수도 있음.
- https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L85 부터 매크로로 정의된 특정 의미를 가지는 unsigned_int 값이 들어감.
- flags
- https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L1731 부터 매크로로 정의됨.
- TODO: 대표적인 값과 그게 무슨 상태인지도 이야기
- https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L1731 부터 매크로로 정의됨.
- 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: 등 핵심적인거 추가
- 소스코드: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/linux/sched.h#L746
-
ps를 치면 구동중인 프로세스가 나오는데,
init_task라는 대스크 디스크립터의 연결리스트인tasks필드(init_task.tasks)에 접근하여 확인. -
커널 분석 시 태스크 디스크립터를 중심으로 보면 좋다. 커널은 프로세스를 중심으로 중요한 데이터를 저장하고 불러오기 떄문.
-
thread_info- 설명
- 특정 아키텍처에 종속적인 데이터를 관리. 아키텍처마다 다른 구현을 함.
- 선점 스케줄링 실행 여부
- 시그널 전달 여부
- 인터럽트 컨텍스트와 Soft IRQ 컨텍트스 상태
- 그 외 데이터는 -
thread_struct로 대체된 것도 있음. - e.g. 휴면 상태 진입 직전 레지스터 세트 로딩/백업
- 역할
- 현재 실행 중인 코드가 인터럽트 컨텍스트인가?(flag 값): 이 값을 확인해서 인터럽트의 선점을 막거나 허용함으로써 안정적인 인터럽트 처리. (인터럽트 처리 중 인터럽트 발생 시)
- 현재 프로세스가 선점 가능한 조건인가(flag 값)?: 이 값을 확인해서 스케줄러의 작업 스케줄링을 효율적으로 함.
- 프로세스가 시그널을 받았는가?
- (대체됨) 레지스터 세트를 저장/로딩
- 설명
-
{linux_root)/arch/{아키텍처_이름}/include/asm/thread_info.h에 위치함. -
arm64
thread_info분석- 소스코드: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm64/include/asm/thread_info.h
- flags
- 핵심적인 값과 그 의미.
- 프로세스는 자신의 flags 값을 수시로 체크하면서, 아래 상황을 파악함.
- 시그널이 자신에게 전달되었나?
- 선점 스케줄링될 조건인가?
- 시스템 콜 트레이싱 조건인가?
- preempt_count: 선점과 관련된 값.
- cpu: cpu 번호
- scs_base: shadow call stack의 시작 주소
- scs_sp: shadow call stack의 스택 포인터
- 등
-
재스케줄링: 현재 태스크를 종료하고 새로운 테스트로 실행 가능한 상태로 전환하는 과정
-
컨텍스트 스위칭 과정 보기
- 소스코드: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/kernel/sched/core.c#L5321
switch_to()를 호출 ->__switch_to()를 호출 - https://github.com/raspberrypi/linux/blob/rpi-6.6.y/include/asm-generic/switch_to.h__switch_to()는 아키텍처에 따라 구현이 다름. ->cpu_switch_to()를 호출 - https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm64/kernel/process.c#L521cpu_switch_to()는 어셈블리로 작성됨 - https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm64/kernel/entry.S#L826THREAD_CPU_CONTEXT(레지스터 값)를 저장하고 스위치할 모델로 변경하는 모습
THREAD_CPU_CONTEXT는 매크로, 실제로는offsetof(struct task_struct, thread.cpu_context)형식 - https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm64/kernel/asm-offsets.c#L48task_struct는thread_struct를 가짐.thread_struct가 가지는cpu_context를 보면 레지스터 값을 그대로 가짐. - https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm64/include/asm/processor.h#L131