4.8 태스크 디스크립터(task_struct 구조체)

 

프로세스의 속성 정보를 표현하는 가장 중요한 자료구조는?

→ 태스크 디스크립터를 나타내는 task_struct 구조체

 

▶ ps -ely : 프로세스 목록

 

 

리눅스 시스템에서 구동 중인 프로세스 목록은 프로세스를 관리하는 태스크 디스크립터의 연결 리스트(init_task.tasks)에 접근하여 등록된 프로세스를 출력한다.

 

 

1) 프로세스를 식별하는 필드

 

 

 

→ comm은 TASK_COMM_LEN 크기의 배열이며 프로세스 이름을 저장한다.

→ 프로세스 이름들은 태스크 디스크립터를 나타내는 task_struct 구조체의 comm 필드에 접근해서 출력한다.

 

ex) 디버깅용 코드

 

 

현재 실행 중인 프로세스의 태스크 디스크립터 구조체 task_struct 구조체에 접근하는 current 매크로 + 프로세스 이름을 저장하는 comm 필드 -> 다양한 디버깅용 코드 작성 가능

 

 

task_struct *p가 깨우려는 프로세스의 태스크 디스크립터

프로세스 이름이 kthreadd인 경우 dump_stack( ) 함수를 호출해 함수 호출 흐름을 커널 로그로 출력

 

▶ pid : 프로세스를 식별하는 정수형 값

- 프로세스 아이디 : pid_t pid; 

- 스레드 그룹 아이디 : pid_t tgid;

 

 

2) 프로세스 상태 저장

 

▶ 태스크 디스크립터에서 프로세스 상태를 관리하는 두 가지 필드

 

① state : 프로세스 실행 상태

- TASK_RUNNING : CPU 에서 실행 중이거나 런큐에서 대기 상태에 있음 
- TASK_INTERRUPTIBLE : 휴면 상태 
- TASK_UNINTERRUPTIBLE : 특정 조건에서 깨어나기 위해 휴면 상태로 진입한 상태 

 

② flags : 프로세스 세부 동작 상태와 속성 정보

      → PF_*로 시작하는 매크로 필드를 OR 연산한 결과를 저장

- PF_IDLE : 아이들 프로세스
- PF_EXITING: 프로세스가 종료 중인 상태 
- PF_EXITPIDONE : 프로세스가 종료를 마무리한 상태 
- PF_WQ_WORKER : 프로세스가 워커 스레드인 경우 
- PF_KTHREAD : 프로세스가 커널 스레드인 경우 

 

▶ exit_state : 프로세스 종료 상태를 저장

- EXIT_DEAD

- EXIT_ZOMBIE

- EXIT_TRACE

 

▶ exit_code : 프로세스의 종료 코드를 저장

 

 

3) 프로세스 간의 관계

 

▶ 프로세스 간의 관계를 나타내는 필드

 

① struct task_struct *real_parent : 자신을 생성한 부모 프로세스의 태스크 디스크립터 주소 저장

 

② struct task_struct *parent : 부모 프로세스의 태스크 디스크립터 주소 저장

 

※ *real_parent와 *parent의 차이점

→ 부모 프로세스가 종료되지 않고 실행 중이면 같음

→ 프로세스 계층 구조에서 지정한 부모 프로세스가 없을 경우 init 프로세스를 부모 프로세스로 변경하면 다름

 

 

위와 같이 부모 프로세스가 종료되면 do_exit( ) 함수에서 화살표 방향으로 함수 호출

forget_original_parent( )와 find_new_reaper( )에서 새로운 부모 프로세스 지정

 

③ struct list_head children : 부모 프로세스가 자식 프로세스를 생성할 때 children 연결 리스트에 자식 프로세스 등록

 

④ struct list_head sibling : 같은 부모 프로세스로 생성된 프로세스의 연결 리스트 주소 저장

 

※ children과 sibling 필드의 연결 방식

 

▶ ps axjf : 부모와 자식 프로세스 관계 확인

 

 

rcu_gp, rcu_par_gp, kworker/0, mm_percpu_wq, ksoftirqd/0 프로세스들의 부몸 프로세스는 kthreadd임을 알 수 있다.

 

※ 부모 프로세스인 kthreadd 입장에서의 태스크 디스크립터

 

 

- kthreadd 프로세스 태스크 디스크립터의 children 필드는 연결리스트

- 연결리스트 헤드에 자식 프로세스의 task_struct 구조체의 sibling 필드 주소를 저장

- kworker/0:0H의 입장에서 mm_percpu_wq와 ksoftirpd/0 프로세스는 자신의 sibling 연결 리스트로 이어져 있음

 

 

4) 프로세스 연결 리스트

 

→ task_struct 구조체의 tasks 필드는 list_head 구조체로서 연결 리스트 타입

 

※ tasks 필드는 언제 init 프로세스의 태스크 디스크립터 필드 중 연결 리스트 타입이 tasks 필드에 등록될까?

 

▶ copy_process( ) 함수 : 프로세스를 생성할 때 호출

 

 

12번째 줄 실행 시 커널로부터 태스크 디스크립터 할당받음

13번째 줄 실ㄹ행 시 init_task.tasks 연결 리스트의 마지막 노드에 현재 프로세스의 task_struct 구조체의 tasks 주소 등록

 

▶ tasks 필드를 TRACE32로 확인해보기

 

 

6번째 줄에서 가리키는 주소의 의미는?

→ init_task.tasks 연결 리스트에 추가된 다음 프로세스의 task_struct 구조체의 tasks 필드 주소

 

 

즉, 0xA1618310 주소는 연결 리스트에 등록된 다음 프로세스 태스크 디스크립터의 next 필드 주소를 의미

→ 이 방식으로 커널에서 구동 중인 모든 프로세스의 태스크 디스크립터 주소를 알 수 있음

 

 

5. 프로세스 실행 시각 정보

 

▶ 태스크 디스크립터에서 프로세스의 실행 시각 정보를 알 수 있는 필드

 

① u64 utime : 유저 모드에서 프로세스가 실행한 시각

 

② u64 stime : 커널 모드에서 프로세스가 실행한 시각

 

③ struct sched_info sched_info.last_arrival : 프로세스 스케줄링 정보 저장

 

※ sched_info.last_arrival 필드는 언제 변경될까?

→ sched_info_arrive( ) 함수의 9번째 줄에서 업데이트됨

 

※ sched_info_arrive( ) 함수는 언제 호출될까?

context_switch( ) 함수 내에서 컨텍스트 스위칭을 수행하기 직전에 prepare_task_switch( ) 함수를 호출하는데, 이 함수를 따라가다 보면 호출됨

다음 순서로 함수 호출 후 실행

• context_switch( ) 
• prepare_task_switch( ) 
• sched_info_switch( ) 
• __sched_info_switch( ) 
• sched_info_arrive( ) 

→ 커널은 sched_info_arrive( ) 함수에서 프로세스의 실행 시간을 업데이트함

 

★ 크래시 유틸리티 프로그램 "ps -l"

→ 가장 마지막에 실행된 순서대로 프로세스 목록 출력

→ 크래시 유틸리티 프로그램 : 커널 크래시를 디버깅할 수 있는 유틸리티 프로그램

- URL: https://people.redhat.com/anderson/crash_whitepaper/ 

이는 리눅스 커널 개발자들이 자주 활용하는 프로그램이니 잘 알아두기!

 

 

4.9 스레드 정보 : thread_info 구조체

 

1) thread_info 구조체란?

 

→ 커널에서는 프로세스의 세부 실행 정보를 저장하거나 로딩하는 자료구조가 필요한데, 이를 관리함

 

▶ 프로세스의 핵심 실행 정보 저장

- 선점 스케줄링 실행 여부

- 시그널 전달 여부

- 인터럽트 컨텍스트와 Soft IRQ 컨텍스트 상태

- 휴면 상태로 진입학 직전 레지스터 세트를 로딩 및 백업

 

※ thread_info 구조체는 어디에 있을까?

→ 프로세스 스택의 최상단 주소에 위치, 프로세스마다 1개의 구조체가 있음

 

▶ 프로세스의 세부 실행 정보 저장

- 컨텍스트 정보

- 스케줄링 직전 실행했던 레지스터 세트

- 프로세스 세부 실행 정보

 

★ task_struct 구조체와 thread_info 구조체의 차이점

- task_struct : CPU 아키텍처에 독립적인 프로세스 관리용 속성을 저장

- thread_info : CPU 아키텍처에 종속적인 프로세스의 세부 속성을 저장

 

★ thread_info 구조체에서 관리하는 커널의 핵심 동작

- 현재 실행 중인 코드가 인터럽트 컨텍스트인지 여부

- 현재 프로세스가 선점 가능한 조건인지 점검

- 프로세스가 시그널을 받았는지 여부

- 컨텍스트 스케줄링 전후로 실행했던 레지스터 세트를 저장하거나 로딩

 

 

 

 

 

 

 

 

'2021 SUMMER STUDY > LINUX KERNEL' 카테고리의 다른 글

Week04_프로세스2  (0) 2021.08.15
Week03_프로세스  (0) 2021.08.09
Week02_커널 디버깅과 코드 학습  (0) 2021.07.25
Week01_리눅스 소개와 전망  (0) 2021.07.18

4.5 커널 스레드

 

1) 커널 스레드란?

 

→ 커널 프로세스는 커널 공간에서만 실행되는 프로세스로 대부분 커널 스레드 형태로 동작한다.

 

- 데몬 vs 커널 스레드

공통점 : 백그라운드 작업으로 실행되면서 시스템 메모리나 전원을 제어하는 동작 수행

차이점 : 커널 스레드는 유저 영역과 시스템 콜을 받지 않고 동작

 

★ 커널 스레드의 특징

- 커널 공간에서만 실행되며 유저 공간과 상호작용하지 않는다.

- 실행, 휴면 등 모든 동작을 커널에서 직접 제어 관리한다.

- 대부분 시스템이 부팅할 때 생성되고 종료할 때까지 백그라운드로 실행된다.

 

 

2) 커널 스레드의 종류

 

▶ ps axjf

 

커널 스레드 목록

 

① kthreadd 프로세스

→ 모든 커널의 부모 프로세스, 커널 스레드를 생성하는 역할 수행

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'2021 SUMMER STUDY > LINUX KERNEL' 카테고리의 다른 글

Week05_프로세스3  (0) 2021.08.21
Week03_프로세스  (0) 2021.08.09
Week02_커널 디버깅과 코드 학습  (0) 2021.07.25
Week01_리눅스 소개와 전망  (0) 2021.07.18

4.1 프로세스 소개

 

1) 프로세스란?

 

→ 리눅스 시스템 메모리에서 실행 중인 프로그램 (태스크와 유사한 의미)

※ 멀티프로세싱 : 다수의 프로세스를 실시간으로 사용

※ 멀티태스킹 : 같은 시간에 여러 프로그램 실행

 

- 프로세스를 관리하는 자료구조이자 객체를 태스크 디스크립터라고 부르고, task_struct 구조체로 표현됨

→ task_struct 구조체에는 메모리 리소스, 프로세스 이름, 실행 시각, 프로세스 아이디, 프로세스 스택의 최상단 주소와 같은 속성 정보가 저장됨

 

프로세스의 실행 흐름을 표현하는 중요한 공간은 스택 공간이며, 이 프로세스 스택의 최상단 주소에 thread_info 구조체가 있다.

 

- 리눅스 커널에서 프로세스를 표현할 수 있는 자료구조

① task_struct 구조체 : 태스크 디스크립터

② thread_info 구조체 : 프로세스 스레드 정보

→ 프로세스를 잘 알려면 프로세스 속성 정보와 실행 흐름을 저장하는 구조체를 잘 알아야 함

 

2) 태스크란?

 

→ 실행(Execution)의 의미, 실행 및 작업 단위

 

- 리눅스 커널에서 태스크는 프로세스와 같은 개념으로 쓰는 용어이다.

- 소스코드나 프로세스에 대한 설명을 읽을 때 태스크라는 단어를 보면 프로세스와 같은 개념으로 이해하자.

 

3) 스레드란?

 

→ 유저 레벨에서 생성된 가벼운 프로세스

 

- 일반 프로세스에 비해 컨텍스트 스위칭을 수행할 때 시간이 적게 걸림

   (자신이 속한 프로세스 내의 다른 스레드와 파일 디스크립터, 파일 및 시그널 정보에 대한 주소 공간을 공유하기 때문)

- 스레드 그룹 안의 다른 스레드와 주소 공간 공유

- 커널 입장에서는 스레드를 다른 프로세스와 동등하게 관리

 

4.2 프로세스 확인하기

 

1) ps 명령어로 프로세스 목록 확인

 

★ ps : 실행 중인 프로세스를 출력하는 명령어

→ 리눅스 커널 내부의 init_task 전역변수를 통해 전체 프로세스 목록 출력

 

라즈베리파이에 "ps -ely" 명령어 입력

 

라즈베리파이에 "ps -ejH" 명령어 입력

 

- "-ejH" 옵션 : 부모 자식 프로세스 관계를 토대로 출력

- 위 목록에서 보이는 프로세스를 커널 스레드, 커널 프로세스라고 하며, 커널 공간에서만 실행된다.

 

※ PID(Process Identifier) : 프로세스에 부여하는 고유의 정수형 ID

→ 리눅스 커널에서 pid_t라는 타입으로 <sys.types.h> 헤더 파일에 저장되어 있음

→ 리눅스 커널에서는 프로세스가 생성될 때 PID를 프로세스에게 알려줌

 

커널은 PID를 증가시키면서 프로세스에게 부여함

 

 

'2021 SUMMER STUDY > LINUX KERNEL' 카테고리의 다른 글

Week05_프로세스3  (0) 2021.08.21
Week04_프로세스2  (0) 2021.08.15
Week02_커널 디버깅과 코드 학습  (0) 2021.07.25
Week01_리눅스 소개와 전망  (0) 2021.07.18

3.1 디버깅이란?

 

버그를 수정하고 있다는 의미

리눅스 커널과 드라이버가 정상 동작할 때 자료구조와 함수 호출 흐름까지 파악하는 과정

 

1) 문제 해결 능력의 지름길

 

커널 디버깅은 문제 해결 능력 그 자체이다.

임베디드 리눅스 활용 분야

- 부팅 도중 커널 크래시 발생

- 인터럽트 핸들러를 설정했는데 호출되지 않음

- 시스템 응답 속도가 매우 느려짐

- 파일 복사가 안 됨

 

문제를 해결하기 위해서는 문제 발생의 원인을 분석해야 하고,

문제 발생의 원인은 문제 발생 시 확보한 커널 로그와 메모리 덤프를 정확히 분석해야 한다.

 

임베디드 BSP 개발에서 커널 로그와 메모리 덤프를 정확히 분석하는 스킬을 커널 디버깅이라고 한다.

 

※ 리눅스 커널을 구성하는 서브시스템이 정상적으로 동작할 때 파악할 내용

- 함수가 실행될 때 변경되는 자료구조

- 함수가 실행되는 빈도와 실행 시각

- 실행 중인 코드를 어떤 프로세스가 실행하는지 확인

프로그램이 정상적으로 동작할 때의 함수 호출 흐름과 자료구조를 알고 있어야 오류나 버그 발생 시 무엇이 문제인지 식별 가능하기 때문

 

★ 커널 로그 분석

 

https://www.unix.com/programming/148285-what-unbalanced-irq.html

 

① 오류 메시지를 커널의 어느 코드에서 출력했는지 확인

 

 

__enable_irq( ) 함수에서 오류 조건을 검출한 것으로 보이고, 어느 코드에서 콜 스택을 출력했는지 살펴봐야 함

 

https://github.com/raspberrypi/linux/blob/rpi-4.19.y/kernel/irq/manage.c

 

irq_desc 구조체의 depth 필드는 인터럽트를 활성화하면 0, 비활성화하면 1을 설정

6번째 줄 코드는 인터럽트를 2번 활성화했을 때 실행되며 이미 인터럽트를 활성화했으면 depth 필드가 0인데 다시 활성화했기 때문에 콜 스택을 출력한다.

※ warn( ) 매크로 함수가 호출되면 커널 로그로 콜 스택을 출력한다.

 

② 소스코드에서 에러 메시지를 출력한 이유 살펴보기

 

 

IRQ 4 : 4번 인터럽트를 두 번 활성화했기 때문에 오류 발생

위 에러 메시지는 enable_irq( ) 함수에서 출력하는데 함수 내부에 있는 논리적 오류 때문이 아닌 

enable_irq( ) 함수를 호출한 드라이버 코드에 있는 오류 때문일 가능성이 높다.

 

 

커널 에러 메시지를 보면  svsknfdrvr 드라이버 모듈이 보이고,

만약 enable_irq( ) 함수를 호출한 코드가 svsknfdrvr 드라이버 코드에 있다면 관련 코드를 분석한 후 논리적인 오류가 있는지 점검해야 함

 

③ 필요에 따라 디버깅 코드를 작성해 다시 문제가 발생했을 때 추가 커널 로그 확보 시도

 

패치 코드를 입력해 문제 현상을 재현하여 커널 로그를 받아보면 누가 4번 인터럽트를 2번 활성화했는지 알려줄 것이다.

 

패치 코드 ( 디버깅 패치 )

 

 __disable_irq ( ) 함수와 __ enable_irq( ) 함수의 2번째 인자는 인터럽트 번호를 나타내는 irq이다.

위 코드에서는 이 인터럽트 번호가 4일 때 콜 스택을 호출한다.

위 패치 코드를 빌드해서 시스템에 반영한 후 문제를 다시 재현하면 4번 인터럽트를 활성화 혹은 비활 
성화할 때 콜스택을 커널 로그로 출력하여 어떤 코드에서 연속으로 활성화했는지 알아낼 수 있다.

 

 

2) 디버깅과 코드 학습 능력

 

디버깅을 하면서 리눅스 커널 코드를 함께 분석하면 다음과 같은 정보를 얻을 수 있다.

- 분석 대상 코드가 동작하는 콜 스택 
- 함수가 실행될 때 변경되는 자료구조 
- 함수가 실행되는 빈도와 실행 시각 
- 분석 대상 코드를 실행하는 프로세스

 

★ 커널 디버깅 과정 예시

 

※ 명령어 "cat /proc/interrupts" : 인터럽트의 세부 속성

 

https://github.com/raspberrypi/linux/blob/rpi-4.19.y/kernel/irq/proc.c

 

"/* 이 부분에 번째 패치 코드를 입력하세요 */"라는 주석으로 표시된 부분에 다음 코드를 입력

 

 

irqaction 구조체 타입인 action 포인터형 변수가 NULL 이 아닐 때 action 인자와 함께 rpi_get_interrupt_info( ) 함수를 호출하는 코드

 

 

rpi_get_interrupt_info( ) 함수는 ftrace 로 다음 정보를 출력 하는 기능

- 프로세스 이름

- 인터럽트 번호

- 인터럽트 이름

- 인터럽트 핸들러 함수 이름

 

위와 같은 패치 코드를 입력한 이유는??

 

 

위와 같이 인터럽트의 속성 정보는 action 필드에 저장된다.

 

 

irqaction 구조체 오른쪽 부분에 표시된 주석이 인터럽트의 속성 정보이고,

이 정보를 라즈비안에서 확인하기 이해 패치 코드를 입력한 것이다.

 

ftrace를 설정하는 명령어로 작성한 셸 스크립트 코드

 

echo rpi_get_interrupt_info > /sys/kernel/debug/tracing/set_ftrace_filter 

패치 코드에서 작성한 rpi_get_interrupt_info( )라는 함수명을 /sys/kernel/debug/tracing/set_ftrace_filter 파일에 지정하는 명령어

→  rpi_get_interrupt_info( ) 함수를 누가 호출했는지 알 수 있음

 

./irq_trace_ftrace.sh : 셸 스크립트 실행

 

 

cat /proc/interrupts : rpi_get_interrupt_info( ) 함수가 호출되도록 커널을 동작시키는 역할

 

 

ftrace 로그를 라즈비안 시스템에서 안전하게 추출하기 위한 셸 스크립트 코드

 

get_ftrace.sh

 

./get_ftrace.sh : ftrace 로그가 현재 디렉터리에 복사되어 ftrace 로그를 확인할 수 있음

 

 

함수의 흐름은 11번째 줄에서 3번째 줄 방향으로 이어짐

01 : pid가 884인 cat 프로세스가 rpi_get_interrupt_info( ) 함수 호출

05 : seq_read( ) 함수에서 show_interrupts( ) 함수 호출

10 : sys_read( ) 함수가 호출된 후 유저 공간에서 read 시스템 콜 실행

 

egrep -nr irq_handler * : 현재 디렉토리에 있는 파일에서 irq_handler라는 문자열을 검색한 다음과 같은 결과 출력

- 인터럽트 번호

- 인터럽트 이름

- 인터럽트 핸들러 이름

 

ex)

 

- 인터럽트 번호 : 86

- 인터럽트 이름 : mmc1

- 인터럽트 핸들러 이름 : bcm2835 mmc_irq

 

 

3.2 printk( ) 함수

 

→ printk( ) 함수를 호출하면 커널 로그를 볼 수 있다.

 

https://github.com/raspberrypi/linux/blob/rpi-4.19.y/arch/arm/kernel/process.c

 

위와 같이 레지스터 정보를 출력해준다.

 

▶ printk로 함수 심벌 정보를 보는 방법

 

- printk로 포인터를 출력하고 싶을 때는 %p를 쓰면 된다.

- %pS를 쓰면 함수 주소를 심벌로 출력한다. → 함수 포인터를 디버깅할 때 자주 쓰는 기법

 

- 리눅스 커널 코드에 printk 코드 입력해보기

 

https://github.com/raspberrypi/linux/blob/rpi-4.19.y/kernel/workqueue.c

 

08 : 프로세스 이름 출력

09~10

- __ func __ : 현재 실행 중인 함수의 이름 
- __ LINE__: 현재 실행 중인 코드 라인 
- _builtin_return_address(0): 현재 실행 중인 함수를 호출한 함수의 주소 

 

 

빌드 후 설치하면 위와 같은 커널 로그를 볼 수 있고, 로그에 담긴 디버깅 정보는 다음과 같다.

- printk 로그를 실행한 포로세스의 이름은 kworker/2 : 3이다. 
- printk가 추가된 곳은 insert_wq_barrier( ) 함수의 2354번째 줄이다. 
- insert_wq_barrier( ) 함수는 workqueue_cpu_down_callback( ) 함수가 호출했다. 

 

★ printk를 쓸 때 주의해야 할 점

→ printk의 호출 빈도를 반드시 체크해야 한다.

리눅스 커널에서 1초에 수백 번 이상 호출되는 함수에 printk를 사용하면 시스템이 락업되거나 커널 패닉으로 오동작할 수 있음

∵ printk가 커널 입장에서 많은 비용이 드는 함수이기 때문

락업 : 리눅스 디바이스에서 마우스를 움직이거나 키보드를 입력해도 아무런 반응이 없는 상황

 

 

3.3 dump_stack( ) 함수

 

→ 커널 로그를 통해 커널 동작을 보여주는 기능

 

▶ dump_stack( ) 함수 사용법

 

- "linux/kernel.h" 헤더 파일 추가하기

- asmlinkage __visible void dump_stack (void); 인자와 반환값 모두 void

 

▶ dump_stack( ) 함수로 커널 로그에서 콜 스택 확인하기 

 

 

_do_fork( ) 함수 : 함수 콜 스택을 확인하기 위한 코드

 

부팅 후 출력되는 커널 로그

 

01 : 콜 스택을 실행한 프로세스 정보

02~07 : 콜 스택

06 : sys_clone( ) 함수에서 _do_fork( ) 함수를 호출한다고 알려줌

 

_do_fork ( ) 항수에 dump_stack ( ) 함수를 추가했을 때의 콜 스택

 

함수 호출은 07번째 줄에서 03번째 줄 방향으로 이뤄짐

 

★ dump_stack( ) 함수를 쓸 때 주의해야 할 점

→ dump_stack( ) 함수를 호출하여 콜 스택을 보려는 코드가 자주 호출되는지 점검해야 함

dump_stack( )은 현재 실행 중인 프로세스 스택 주소를 읽어 스택에 푸시된 프레임 포인터 레지스터를 읽고,

ARM 아키텍처의 함수 호출 규약에 따라 프레임 포인터 레지스터를 읽어 함수 호출 내역을 추적하는 동작 반복

 

 

3.4 ftrace

→ 리눅스 커널에서 제공하는 강력한 디버그 기능

 

- 함수 호출 흐름을 소스코드를 수정하지 않고도 볼 수 있음

- 커널의 세부 실행 정보 출력

- 1초에 수십 번 호출해도 성능에 부담 없음

- 커널 로그 함께 볼 수 있음

 

※ ftrace의 특징

① 인터럽트, 스케줄링 커널 타이머 등의 커널 동작을 상세히 추적한다.
② 함수 필터를 지정하면 지정한 함수를 호출한 함수와 전체 콜 스택까지 출력하며 코드를 수정할 필요가 없다.
③ 함수를 어느 프로세스가 실행하는지 알 수 있다.
④ 함수가 실행된 시각 정보를 알 수 있다. 
⑤ ftrace 로그를 활성화해도 시스템 동작에 부하를 거의 주지 않는다.

 

▶ ftrace 설정

 

 

/sys/kernel/debug/tracing에서 ftrace 설정 파일을 확인할 수 있다.

 

- sleep 1 : 1초 동안 딜레이를 주는 동작

- tracing_on : 트레이서 활성화

- tracer 설정

① nop : 기본 트레이서, ftrace 이벤트만 출력
② function : 함수 트레이서, set_ftrace filter로 지정한 함수를 누가 호출하는지 출력
③ function_graph : 함수 실행 시간과 세부 호출 정보를 그래프 포맷으로 출력

- set_ftrace_filter : 필터 설정

→ 리눅스 커널에 존재하는 모든 함수를 필터로 지정할 수는 없고, available_filter_functions 파일에 포함된 함수만 지정할 수 있음

 

▶ 추가 설정 파일

- buffer_size_kb : ftrace 로그의 버퍼 크기, 킬로바이트 단위, ftrace 로그를 더 많이 저장하고 싶을 때 설정

- available_filter_functions : 트레이싱할 수 있는 함수 목록을 저장한 파일

- events : ftrace에서 제공하는 이벤트의 종류를 알 수 있는 디렉터리

     - sched : 프로세스가 스케줄링되는 동작과 스케줄링 프로파일링을 트레이싱하는 이벤트를 볼 수 있음
     • sched_switch: 컨텍스토 스위칭 동작 
     • sched_wakeup: 프로세스를 우는 동작 
     - irq : 인터럽트와 Soft IRQ를 트레이싱하는 이벤트
     • irq_handler_entry: 인터럽트가 발생한 시각과 인터럽트 번호 및 이름을 출력 
     • irq_handler_exit: 인터럽트 핸들링이 완료 
     • softirq_raise: Soft IRQ 서비스 실행 요청 

     • softirq_entry: Soft lRQ 서비스 실행 시작 

     • softirq_exit: Soft IRQ 서비스 실행 완료 

 

▶ ftrace 로그 추출

 

 

./get_ftrace.sh로 셸 스크립트를 실행하면 같은 폴더에 ftrace 로그가 담긴 ftrace log.c 라는 파일이 생성됨

 

★ ftrace는 커널 코드 분석의 안내자이다.

→ ftrace 이벤트의 이름으로 커널 내부의 어떤 소스코드에서 이벤트를 출력하는지 알 수 있기 때문

 

각 이벤트를 출력하는 함수 목록

 

3.5 임베디드 디버거의 전설 TRACE32

 

▶ 주소로 코드 정보 파악

 

 

▶ 전역 변수 확인

 

 

▶ 구조체를 주소로 캐스팅

 

 

▶ 어셈블리 코드 보기

 

 

 

'2021 SUMMER STUDY > LINUX KERNEL' 카테고리의 다른 글

Week05_프로세스3  (0) 2021.08.21
Week04_프로세스2  (0) 2021.08.15
Week03_프로세스  (0) 2021.08.09
Week01_리눅스 소개와 전망  (0) 2021.07.18

1.5 임베디드 리눅스 개발 단체

 

1) 리눅스 커널 커뮤니티

→ 리눅스 커널 개발의 심장으로 논리적 오류나 문제점을 개선하는 패치를 논의하고 관리함

 

① 버그 수정 패치

② 코드 리팩터링

③ 신규 알고리즘

④ 문서화

 

리눅스 커널 패치 배포 시 발송되는 메일

 

리눅스 커널 버전과 코드 내역은 리눅스 커널 https://www kern l.org/) 사이트에서 확인 가능

 

리눅스 커널 소스 저장소

 

- longterm : 안정화된 리눅스 커널 버전

→ 리눅스 커널 커뮤니티에서 관리하는 안정화된 리눅스 커널 버전을 LTS(Long Term Support)라고 부름

 

 

2) CPU 벤더

→ CPU를 설계하는 회사를 뜻함

 

- 대표 업체 : ARM(ARMv7/ARMv8), 인텔(x86), IBM(PowerPC)

- 리눅스 커널 개발에 참여 

- 리눅스 커널 핵심 기능 : 시스템 콜, 익셉션, 컨텍스트 스위칭

 

CPU 아키텍처와 리눅스 드라이버 계층도

 

커널의 핵심 동작은 서로 다른 CPU 어셈블리 코드로 구현되어 있고, 컨텍스트 스위칭의 세부 동작은 CPU 별로 구현 방식이 다름

 

라즈비안과 같이 ARMv7 기반 리눅스 커널을 쓰려면?

→ ARMv7에 맞는 빌드 스크립트로 커널을 빌드하면 된다.

 

즉, 리눅스 커널은 다양한 CPU 아키텍처를지원하는 소스 트리를 갖추고 있으며 사용하고자 하는 CPU 아키텍처에 맞춰 빌드하면 이에 맞는 커널 이미지를 생성한다.

 

 

3) SoC 벤더

→  SoC를 개발하는 업체인 브로드컴, 삼성전자(시스템 LSI), 퀄컴, 인텔, 미디어택 엔비디아 같은 회사를 뜻함

 

※ SoC(System-on-Chip) :  하나의 컴퓨터 또는 다른 전자 시스템들의 모든 구성 요소를 통합한 집적회로

 

- SoC 벤더에서 개발하는 제품명

• 브로드컴: BCM(bcm2837. 라즈베리 파이에 탑재) 
• 삼성전자(시스템 LS|): 엑시노스(Exynos) 
• 퀄컴:스냅드래곤 
• 인텔아톰무어필드 
• 마디어텍헬리오 
• 엔비디아: 테그라 

 

soc 벤더에서 개발하는 리눅스 드라이버 계층도

 

→ 리눅스 커널을 사용하여 SoC 하드웨어를 제어하는 디바이스 드라이버 작성

 

 

4) 보드 벤더 및 OEM

→ 보드 벤더는 라즈베리 파이 재단과 같은 업체이고 OEM은 삼성전자, LG 전자와 같이 상용 제품을 개발하는업체를 뜻함

 

보드 벤더 및 OEM 이 개발하는 리눅스 드라이버 계층도

 

→ SoC 벤더에서 작성한 드라이버에서 버그를 확인하면, 

보드 벤더 및 OEM 업체는 버그를 리포트하고 개선 패치를 받아 수정하는 경우가 많음

 

 

1.6 임베디드 리눅스 개발을 잘 하려면 무엇을 알아야 할까?

 

★ 임베디드 리눅스 개발자가 알아야 할 지식

 

- 디바이스 드라이버

- 리눅스 커널

- CPU 아키텍처

- SoC

※ 알아두면 좋음

- 유저 공간 HAL(Hardware Abstraction Layer) 코드 구현 
- 빌드 스크립트구현 
- 테스트용 디바이스 드라이버 구현 
- Git과 형상 관리

 

 

1) 디바이스 드라이버

- 인터럽트 핸들러 함수와 인터럽트를 처리하는 방식 
- 디바이스 파일로 open/read/write 연산에 대한 함수를 등록하는 방법 
- 디바이스 트리를 읽어 디바이스 속성을 저장하는 방식 

 

2) 리눅스 커널

- 디바이스 드라이버는 리눅스 커널에서 제공하는 함수로 구성되어 있음

- 디바이스 드라이버를 개발하는 과정은 인증 테스트 부서를 통해 드라이버 안정화 테스트 과정을 거치며,

   이 과정에서 다양한 버그나 문제 증상이 리포트됨

 

3) CPU 아키텍처

- 리눅스 커널의 핵심 개념은 어셈블리 코드로 구현됨

ex) 컨텍스트 스위칭, 익셉션 벡터, 시스템 콜, 시그널 핸들러, 메모리 관리(MMU)

 

4) 빌드 스크립트와 Git

- 다른 업체가 개발한 드라이버나 응용 프로그램을 현재 사용 중인 소스트리에 추가할 때 사용

 

 

1.7 라즈베리 파이와 리눅스 커널

 

1) 라즈베리 파이 실습 보드

 

라즈베리 파이 3 모델 B의 기본 하드웨어 스펙

• Soc: Broadcom BCM2837 Soc 
• CPU: 1.2GHz ARM Cortex- A53 MP4 
• GPU: Broadcom VideoCore IV MP2 400 MHz 
• 메모리: 1GB LPDDR2 
• SD 카드: Micro SD, push-pull type 

 

 

2) 리눅스 커널 버전

 

- 라즈비안 리눅스 커널 코드 아래에서 확인 가능

https://github.com/raspberrypi/linux/tree/rpi-4.19.y 

 

- 리눅스 커널 커뮤니티에서 관리하는 리눅스 커널 코드를 보려면 다음 URL 참고

https://elixir.bootlin.com/linux/v4.19.30/source

 

 

3) 라즈비안 버전

 

- 책에서 다룬 커널 패치는 다음 버전의 라즈비안 이미지에서 테스트함

2019-07-10-raspbian-buster-full 

 

- 다음과 같은 리눅스 커널 버전에서 활용 가능

4.14/4.9/4.4/3.18 

 

 

4) ARM 아키텍처

 

라즈베리 파이 3 모델 B는 ARMv7 아키텍처를 기반으로 동작하므로

이 책에서는 이를 기준으로 ARM 아키텍처와 관련된 동작을 설명한다.

'2021 SUMMER STUDY > LINUX KERNEL' 카테고리의 다른 글

Week05_프로세스3  (0) 2021.08.21
Week04_프로세스2  (0) 2021.08.15
Week03_프로세스  (0) 2021.08.09
Week02_커널 디버깅과 코드 학습  (0) 2021.07.25

+ Recent posts