[Linux, OS] GDB | Signal, Timer | Thread

pos++·2023년 11월 22일
0

Linux

목록 보기
11/16
post-thumbnail

2023.11.14 TIL
Code, Image source: The Linux Programming Interface, Michael Kerrisk

GDB

Toolchains

  • GNU toolchain = GCC + binutils + C library + GDB
  • GCC
    • GNU Compiler Collection (C, C++)
  • Binutils
    • Assembler, linker, utilities
  • GDB
    • GNU Debugger

GDB Command List 보기

$ gdb
(gdb) help

단축 command

c == continue
s == stepi

GDB 실행

$ gdb [program name]
(gdb) run

$ gdb -p [pid]  # pid를 알고있는 경우
# Example
$ gdb copy
(gdb) run seek_io seek_io_new

step

  • step
    • 한 line 수행
    • 만약 함수를 만나면 함수 안으로 진입
  • next
    • 한 line 수행
    • 만약 함수를 만나도 다름 line 진행
  • stepi, nexti
    • Assembly language 단위로 실행

수행

  • continue, c
    • 다음 breakpoint를 만날때까지 수행
  • finish
    • 현재 함수가 return할때까지 수행

breakpoint

  • break [location], b
    • 특정 위치에서 멈춤
    • location
      • Memory addresses (ex: 0x7c00)
      • main.c:50
  • delete, disable, enable
    • breakpoint 수정

Watchpoint

  • watch [expression]
    • code를 멈춰세우고 값의 변화 확인가능

출력

  • x argv[0]
    • Memory값 출력
    • x/x : hexadecimal
    • x/i : assembly
  • print evaluates
  • p *((struct datatype *) 0x20000
  • display argc
    • 값을 계속해서 출력
  • info registers
    • CPU Register 출력
  • info frame
    • 현재 Stack frame 출력
  • list [location]
    • Source code 출력
  • backtrace, bt
    • Function call stack 출력
  • info threads
    • Thread list 출력
  • info breakpoints
    • breakpoint list 출력

fork debugging

  • set follow-fork-mode child
    • child process debugging
  • show follow-fork-mode
    • (default) parent process debugging

GDB Frontends

  • TUI
    • gdb fork_whos_on_first -tui
  • CGDB
    • sudo apt install -y cgdb
    • cgdb fork_whos_on_first
  • DDD
    • ddd --debugger
  • VSCODE
    • $ code .
    • tasks.json 신경x
    • launch.json

Process

Process

  • 실행 중인 program
  • 하나의 program으로 여러 process 생성 가능
  • elf (실행 파일) → shell이 RAM에 올려서 실행

  • Isolation
  • Multiplexing
  • Interaction or share
  • getpid() → pid 얻는 system call

Virtual Memory

우리가 보는 Memory

  • Paging system
    • 직접 접근 X
    • Page table을 통해 접근

Stack, Stack Frame

  • Function parameter, local variable
  • Call 연결 정보

argc, argv

Environment Variable

  • UNIX 전동적인 key, value
  • export SHELL=/bin/bash
  • printenv
  • setenv
  • getenv
  • 주로 booting시 설정값 활용하기 위해 사용

Process 관련 system call

  • fork()
    • Process 생성
    • tlpi-dist/procexec/t_fork.c debugging해보기
      • switch()에서 -exec set follow-fork-mode child
      • case0default의 시작에서 b
      • parent로도 해보기
  • exit()
    • Process 종료
  • wait()
    • Child process 대기
    • 대기하지 않는다면 child process의 자원을 초기화할 수 없음 → zombie process❗
    • child의 pid 또는 -1(error) return
    • tlpi-dist/procexec/multi_wait.c
  • waitpid()
    • 특정 process 기다리기
    • error handling
      • WIFEXITED(status)
      • WIFSIGNALED(status)
      • WIFSTOPPED(status)
    • tlpi-dist/procexec/print_wait_status.c
  • execve()
    • Program 실행 (execution file Memory load)
    • tlpi-dist/procexec/t_execve.c

exec()execve()의 차이점 - by ChatGPT

The exec() and execve() functions are both part of the family of functions in Unix-like operating systems that are used to replace the current process image with a new one. However, they have some differences in terms of how they handle the command and arguments.

  1. exec() Function:

    • The exec() function is a family of functions, such as execl(), execle(), execlp(), execv(), and execvp(). Each variant of exec() takes different arguments and provides different ways of specifying the command and arguments.
    • The exec() functions take the command and its arguments as separate parameters in the function call.
    • The environment variables are inherited from the calling process.

    Example (execl()):

    #include <unistd.h>int main() {
        execl("/bin/ls", "ls", "-l", (char *)NULL);
        // If execl() returns, an error occurred
        return 1;
    }
  2. execve() Function:

    • The execve() function is more flexible and allows you to explicitly pass the command, an array of arguments, and the environment variables as separate parameters.
    • It requires specifying the full path to the executable, the array of arguments, and the array of environment variables.
    • This function gives you more control over the execution environment.

    Example (execve()):

    #include <unistd.h>int main() {
        char *args[] = {"ls", "-l", NULL};
        char *envp[] = {NULL};
        execve("/bin/ls", args, envp);
        // If execve() returns, an error occurred
        return 1;
    }

In summary, the primary difference lies in the way you pass the command and its arguments. The execve() function provides more control by allowing you to explicitly pass the arrays of arguments and environment variables, while the exec() functions take these as separate parameters in the function call. The choice between them depends on the specific requirements of your program.


fork와 File descriptor

  • File descriptor → Open file table을 가리킴

과제

filebrowser 설치

curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash filebrowser -r /path/to/your/files

  • ⚠️ 문제
    bash: filebrowser: No such file or directory
    curl: (23) Failure writing output to destination
  • 해결 curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash -s filebrowser -r /path/to/your/files
  • id, pw = admin, admin

google chrome 설치

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb

sudo apt install ./google-chrome-stable_current_amd64.deb

아니 이것도 arm64에서 안된다고? 세상이 나를 억까한다

대체재가 이거라고 함. chrome이 이거 기반이라고 함.

sudo apt install chromium-browser

execl("/usr/bin/chromium-browser", "chromium-browser", "http://localhost:8282", NULL);


Signal

Process에게 event가 발생했음을 알림

  • Process간 정보 전달용 알림
  • OS가 알려주는 software interrupt

Signal handler

Interrupt service routine과 동급…

OS → Process

control+CSIGINT생성, Signal interrupt

  • tlpi-dist/signals/ouch.c ouch…

Process 죽이기

  • kill -9 [pid] → process 무조건 죽임
  • pkill [process name] → process 무조건 죽임

Signal type

  • SIGSEGV 받아서 직접 signal handler 등록 가능 → seg fault 원인을 로그로 저장 → 중요❗

자기자신한테 보낼 때

  • raise

Signal 대기

  • pause

Signal의 특징

  • Signal은 queue에 들어가지 않는다
    • 누적되어도 여러번 호출되지 않음
    • Signal handler로 분기 → 그동안 signal 10개가 발생했다고 해도 1만 호출
    • 그래서 Signal handler는 짧게 짜야 한다…
  • Global variable은 조심히 사용해야
    • 재진입 가능한 함수 사용
    • 재진입 불가 예제 tlpi-dist/signals/nonreentrant.c

SIGCHLD

  • Child의 signal을 Parent가 받을 수 있다
  • 언제 사용?
    • 예외 처리
    • System의 연속성을 위해서 child process를 다시 살려야 하는 경우
  • tlpi-dist/procexec/multi_SIGCHLD.c

Zombie Process

  • Parent가 wait()을 호출하지 않을 경우
    • Child의 stack, memory 등이 남아있다
    • Child가 zombie로 남는다
  • tlpi-dist/procexec/make_zombie.c

Real-world Signal


Timer

시간 간격 Timer

  • 전통적인 UNIX API
    • setitimer(), alarm()
  • which
    • ITIMER_REAL
    • ITIMER_VIRTUAL
    • ITIMER_PROF

setitimer

  • timer 등록
  • argument로 interval을 전달
  • 💔 문제점
    • Timer 만료를 전달받는 유일한 방법이 signal
    • Signal 수행 중 timer 만료가 여러번이면
      • 무시 → timer overrun 발생…

getitimer

  • argument로 pointer로 주소 전달

  • tlpi-dist/timers/real_timer.c

POSIX Timers

UNIX Timer의 문제점 개선

sleep

  • 저해상도 수면
    • sleep()
  • 고해상도 수면
    • nanosleep()
  • tlpi-dist/timers/ptmr_sigev_signal.c

Process review

  • Isolation을 위해 만들어짐
  • 추상화된 Virtual machine, 하지만 실제 virtual machine 아님 (더 확장된 vm: Linux container)
  • 마치 자신이 CPU와 Memory를 모두 소유한 것처럼 생각
  • API: fork, exec, exit, wait, kill, getpid

CPU Isolation

  • How?
    • HW 주기적으로 clock interrupt 제공
  • Why?
    • 긴 연산을 하는 process를 막기 위해
    • 무한루프 코드 방지
  • Context Switch
    • Kernel은 반드시 현재의 process state(registers)를 저장/복구

Memory Isolation

  • Address Space
    • code, variables, heap, stack
  • Kernal/다른 process가 접근하는 것 방지
  • How?
    • Paging Hardware
    • MMU
    • 독립적 주소 공간 page tables 관리

Thread

  • CPU core에서 실제로 실행되는 코드
  • 1개의 process는 적어도 1개 이상(main thread + ɑ)의 thread를 가짐
  • 실행되지 않은 thread
    • stack에 저장됨
    • register (→ PC)
  • Thread간에는 Memory를 공유
    • Process address space를 공유
  • 실행하거나 실행하지 않거나
  • 실행중
    • CPU 사용 (register 이용 - stack pointer를 이용해 stack memory 사용)
  • 안실행중
    • Blocked/waiting/suspended …
  • Thread가 대기 상태에서 실행되면 저장된 register들 복구됨

Linux vs RTOS

  • Linux
    • Process 기반
    • Process간 memory 침범 X
  • RTOS (Real-Time OS)
    • Thread로만 동작
    • 모든 Memory 공유

Process Memory Map - with Thread

4 thread memory map
→ Thread마다 stack이 따로 잡힌다

Memory map 보기

  • sudo cat /proc/[pid]/maps

Thread 생성

  • int pthread_create(pthread_t *thrd, const [thread attribute] *attr, void *(*start)(void *), void *arg);
    • pid 변수의 주소 pointer로 전달
    • thread attribute 설정 (schedule 정책, 우선순위 등)
    • handler - function pointer로 돌릴 thread이름 넣어주기
    • thread 생성하고 전달할 arg값 void pointer로 설정
  • pthread_t → pid 자료형

Thread 종료

  • void pthread_exit(void *retval);

종료된 Thread Join

  • Process의 wait()과 같은..
  • int pthread_join(pthread_t thrd, void **setval);

Detaching a Thread

  • Thread가 memory, stack 공간 차지하고 있음
  • Process 내부에서 thread가 죽을때 알아서 정리하도록 한다.
  • pthread_join을 호출하지 않아도 생성된 thread 알아서 정리
  • int pthread_detach(pthread_t thrd);

Debugging Multithread

  • GDB
    • (gdb) info thread
    • (gdb) thread [#] → 다른 thread로 바꿈, backtrace(bt) 도 확인하면서…
  • vscode

Thread Cancel

  • tlpi-dist/threads/thread_cancel.c
    #include <pthread.h>
    
    int pthread_cancel(pthread_t thread);
profile
밀린 TIL 업로드 조금씩 정리중...

0개의 댓글