어떻게 디 버 깅 을 통 해 nginx 를 배 웁 니까?
실제 응용 에서 리 눅 스 함수 fork 를 통 해 새로운 하위 프로 세 스 를 만 드 는 응용 프로그램 이 있 습 니 다.nginx 를 예 로 들 면 nginx 가 클 라 이언 트 에 대한 연결 은 다 중 프로 세 스 모델 을 사용 합 니 다. nginx 가 클 라 이언 트 연결 을 받 아들 인 후에 이 연결 의 정보 왕래 를 처리 하 는 새로운 프로 세 스 를 만 듭 니 다.새로 생 긴 프로 세 스 는 원래 의 프로 세 스 와 서로 부자 관계 이다.그렇다면 어떻게 gdb 로 이러한 부자 프로 세 스 를 디 버 깅 합 니까?일반적으로 두 가지 방법 이 있다.
방법 1
gdb 로 부모 프로 세 스 를 먼저 디 버 깅 하고 하위 프로 세 스 가 fork 에서 나 온 후에 gdb attach 를 사용 하여 하위 프로 세 스 에 연결 합 니 다.물론 디 버 깅 을 위해 셸 창 을 다시 열 어야 합 니 다. gdb attach 의 용법 은 앞에서 소개 되 었 습 니 다.
우 리 는 nginx 서 비 스 를 디 버 깅 하 는 것 을 예 로 들 었 다.
nginx 홈 페이지 에서http://nginx.org/en/download.html 최신 nginx 소스 코드 를 다운로드 한 다음 에 컴 파일 하여 설치 합 니 다 (필자 가 이 책 을 쓸 때 nginx 의 최신 안정 버 전 은 1.18.0).
## nginx
[root@iZbp14iz399acush5e8ok7Z zhangyl]# wget http://nginx.org/download/nginx-1.18.0.tar.gz
--2020-07-05 17:22:10-- http://nginx.org/download/nginx-1.18.0.tar.gz
Resolving nginx.org (nginx.org)... 95.211.80.227, 62.210.92.35, 2001:1af8:4060:a004:21::e3
Connecting to nginx.org (nginx.org)|95.211.80.227|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1039530 (1015K) [application/octet-stream]
Saving to: ‘nginx-1.18.0.tar.gz’
nginx-1.18.0.tar.gz 100%[===================================================================================================>] 1015K 666KB/s in 1.5s
2020-07-05 17:22:13 (666 KB/s) - ‘nginx-1.18.0.tar.gz’ saved [1039530/1039530]
## nginx
[root@iZbp14iz399acush5e8ok7Z zhangyl]# tar zxvf nginx-1.18.0.tar.gz
## nginx
[root@iZbp14iz399acush5e8ok7Z zhangyl]# cd nginx-1.18.0
[root@iZbp14iz399acush5e8ok7Z nginx-1.18.0]# ./configure --prefix=/usr/local/nginx
[root@iZbp14iz399acush5e8ok7Z nginx-1.18.0]make CFLAGS="-g -O0"
## , nginx /usr/local/nginx/
[root@iZbp14iz399acush5e8ok7Z nginx-1.18.0]make install
메모: make 명령 을 사용 하여 컴 파일 할 때 저 희 는 생 성 된 nginx 가 디 버 깅 기호 정 보 를 가지 고 컴 파일 러 를 최적화 시 키 기 위해 '- g - O 0' 옵션 을 설정 하 였 습 니 다.
시작 nginx:
[root@iZbp14iz399acush5e8ok7Z sbin]# cd /usr/local/nginx/sbin
[root@iZbp14iz399acush5e8ok7Z sbin]# ./nginx -c /usr/local/nginx/conf/nginx.conf
[root@iZbp14iz399acush5e8ok7Z sbin]# lsof -i -Pn | grep nginx
nginx 5246 root 9u IPv4 22252908 0t0 TCP *:80 (LISTEN)
nginx 5247 nobody 9u IPv4 22252908 0t0 TCP *:80 (LISTEN)
위 에서 보 듯 이 nginx 는 기본적으로 두 개의 프로 세 스 를 시작 합 니 다. 제 기계 에서 루트 사용자 로 실행 되 는 nginx 프로 세 스 는 부모 프로 세 스 이 고 프로 세 스 번 호 는 5246 이 며 nobody 사용자 로 실행 되 는 프로 세 스 는 하위 프로 세 스 이 며 프로 세 스 번 호 는 5247 입 니 다.현재 창 에서 gdb 를 nginx 메 인 프로 세 스에 추가 하 는 명령
gdb attach 5246
을 사용 합 니 다.[root@iZbp14iz399acush5e8ok7Z sbin]# gdb attach 5246
... ...
0x00007fd42a103c5d in sigsuspend () from /lib64/libc.so.6
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-72.el8_1.1.x86_64 libxcrypt-4.1.1-4.el8.x86_64 pcre-8.42-4.el8.x86_64 sssd-client-2.2.0-19.el8.x86_64 zlib-1.2.11-10.el8.x86_64
(gdb)
이 때 우 리 는 nginx 부모 프로 세 스 를 디 버 깅 할 수 있 습 니 다. 예 를 들 어 bt 명령 으로 현재 호출 스 택 을 볼 수 있 습 니 다.
(gdb) bt
#0 0x00007fd42a103c5d in sigsuspend () from /lib64/libc.so.6
#1 0x000000000044ae32 in ngx_master_process_cycle (cycle=0x1703720) at src/os/unix/ngx_process_cycle.c:164
#2 0x000000000040bc05 in main (argc=3, argv=0x7ffe49109d68) at src/core/nginx.c:382
(gdb) f 1
#1 0x000000000044ae32 in ngx_master_process_cycle (cycle=0x1703720) at src/os/unix/ngx_process_cycle.c:164
164 sigsuspend(&set);
(gdb) l
159 }
160 }
161
162 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
163
164 sigsuspend(&set);
165
166 ngx_time_update();
167
168 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
(gdb)
f 1 명령 을 사용 하여 현재 호출 스 택 으로 전환 합 니 다 \ # 1. nginx 부모 프로 세 스 의 메 인 스 레 드 가
src/core/nginx.c:382
에 걸 려 있 는 것 을 발견 할 수 있 습 니 다.이 때 는 c 명령 을 사용 하여 프로그램 을 계속 실행 시 킬 수도 있 고 정지점 을 추가 하거나 다른 디 버 깅 작업 을 할 수도 있 습 니 다.
셸 창 을 하나 더 열 고 gdb 를 nginx 서브 프로 세 스에 추가 합 니 다.
[root@iZbp14iz399acush5e8ok7Z sbin]# gdb attach 5247
... ...
0x00007fd42a1c842b in epoll_wait () from /lib64/libc.so.6
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-72.el8_1.1.x86_64 libblkid-2.32.1-17.el8.x86_64 libcap-2.26-1.el8.x86_64 libgcc-8.3.1-4.5.el8.x86_64 libmount-2.32.1-17.el8.x86_64 libselinux-2.9-2.1.el8.x86_64 libuuid-2.32.1-17.el8.x86_64 libxcrypt-4.1.1-4.el8.x86_64 pcre-8.42-4.el8.x86_64 pcre2-10.32-1.el8.x86_64 sssd-client-2.2.0-19.el8.x86_64 systemd-libs-239-18.el8_1.2.x86_64 zlib-1.2.11-10.el8.x86_64
(gdb)
우 리 는 bt 명령 을 사용 하여 프로 세 스 의 주 스 레 드 현재 호출 스 택 을 봅 니 다.
(gdb) bt
#0 0x00007fd42a1c842b in epoll_wait () from /lib64/libc.so.6
#1 0x000000000044e546 in ngx_epoll_process_events (cycle=0x1703720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:800
#2 0x000000000043f317 in ngx_process_events_and_timers (cycle=0x1703720) at src/event/ngx_event.c:247
#3 0x000000000044c38f in ngx_worker_process_cycle (cycle=0x1703720, data=0x0) at src/os/unix/ngx_process_cycle.c:750
#4 0x000000000044926f in ngx_spawn_process (cycle=0x1703720, proc=0x44c2e1 , data=0x0, name=0x4cfd70 "worker process", respawn=-3)
at src/os/unix/ngx_process.c:199
#5 0x000000000044b5a4 in ngx_start_worker_processes (cycle=0x1703720, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
#6 0x000000000044acf4 in ngx_master_process_cycle (cycle=0x1703720) at src/os/unix/ngx_process_cycle.c:131
#7 0x000000000040bc05 in main (argc=3, argv=0x7ffe49109d68) at src/core/nginx.c:382
(gdb) f 1
#1 0x000000000044e546 in ngx_epoll_process_events (cycle=0x1703720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:800
800 events = epoll_wait(ep, event_list, (int) nevents, timer);
(gdb)
하위 프로 세 스 가 걸 려 있 는 epoll 을 발견 할 수 있 습 니 다.wait 함수 부.우 리 는 epollwait 함수 가 돌아 온 후
gdb attach 5247
정지점 을 추가 한 다음 c 명령 을 사용 하여 nginx 서브 프로 세 스 를 계속 실행 합 니 다.800 events = epoll_wait(ep, event_list, (int) nevents, timer);
(gdb) list
795 /* NGX_TIMER_INFINITE == INFTIM */
796
797 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
798 "epoll timer: %M", timer);
799
800 events = epoll_wait(ep, event_list, (int) nevents, timer);
801
802 err = (events == -1) ? ngx_errno : 0;
803
804 if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
(gdb) b 804
Breakpoint 1 at 0x44e560: file src/event/modules/ngx_epoll_module.c, line 804.
(gdb) c
Continuing.
이 어 저 희 는 브 라 우 저 에서 nginx 의 사 이 트 를 방문 합 니 다. 제 ip 주 소 는 제 클 라 우 드 호스트 주소 입 니 다. 독자 가 실제 디 버 깅 할 때 자신의 nginx 서버 가 있 는 주소 로 바 꾸 었 습 니 다. 이 컴퓨터 라면 127.0.0.0.1 입 니 다. 기본 포트 는 80 이기 때문에 포트 번 호 를 지정 하지 않 아 도 됩 니 다.
http:// ip :80
http:// ip
이 때 우 리 는 nginx 서브 프로 세 스 의 디 버 깅 인터페이스 로 돌아 가 정지점 이 실 행 된 것 을 발견 합 니 다.
Breakpoint 1, ngx_epoll_process_events (cycle=0x1703720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:804
804 if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
(gdb)
bt 명령 을 사용 하면 현재 호출 스 택 을 얻 을 수 있 습 니 다:
(gdb) bt
#0 ngx_epoll_process_events (cycle=0x1703720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:804
#1 0x000000000043f317 in ngx_process_events_and_timers (cycle=0x1703720) at src/event/ngx_event.c:247
#2 0x000000000044c38f in ngx_worker_process_cycle (cycle=0x1703720, data=0x0) at src/os/unix/ngx_process_cycle.c:750
#3 0x000000000044926f in ngx_spawn_process (cycle=0x1703720, proc=0x44c2e1 , data=0x0, name=0x4cfd70 "worker process", respawn=-3)
at src/os/unix/ngx_process.c:199
#4 0x000000000044b5a4 in ngx_start_worker_processes (cycle=0x1703720, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
#5 0x000000000044acf4 in ngx_master_process_cycle (cycle=0x1703720) at src/os/unix/ngx_process_cycle.c:131
#6 0x000000000040bc05 in main (argc=3, argv=0x7ffe49109d68) at src/core/nginx.c:382
(gdb)
info threads 명령 을 사용 하면 하위 프로 세 스 의 모든 스 레 드 정 보 를 볼 수 있 습 니 다. nginx 하위 프로 세 스 는 하나의 메 인 스 레 드 만 있 습 니 다.
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7fd42b17c740 (LWP 5247) "nginx" ngx_epoll_process_events (cycle=0x1703720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:804
(gdb)
nginx 부모 프로 세 스 는 클 라 이언 트 요청 을 처리 하지 않 습 니 다. 클 라 이언 트 요청 을 처리 하 는 논 리 는 하위 프로 세 스 에 있 습 니 다. 단일 키 프로 세 스 클 라 이언 트 요청 수량 이 일정 수량 에 이 르 렀 을 때 부모 프로 세 스 는 새로운 하위 프로 세 스 를 다시 fork 하여 새로운 클 라 이언 트 요청 을 처리 합 니 다. 즉, 하위 프로 세 스 수량 이 여러 개 있 을 수 있 습 니 다. 여러 개의 셸 창 을 열 수 있 습 니 다.gdb attach 를 사용 하여 각 하위 프로 세 스 에 디 버 깅 합 니 다.
요약 하면 우 리 는 이런 방법 으로 각종 단점 디 버 깅 nginx 의 기능 을 추가 할 수 있 고 천천히 우 리 는 nginx 의 각 내부 논 리 를 익 힐 수 있다.
그러나 방법 중 하 나 는 프로그램 이 시작 되 었 다 는 단점 이 있 습 니 다. 우 리 는 gdb 를 사용 하여 프로그램의 행동 을 관찰 할 수 밖 에 없습니다. 만약 에 우리 가 프로그램 이 시작 에서 실행 되 는 사이 의 실행 절 차 를 디 버 깅 하려 면 방법 이 적용 되 지 않 을 수 있 습 니 다.일부 독자 들 은 내 가 gdb 로 프로 세 스 에 추가 한 후에 정지점 을 추가 한 다음 에 run 명령 으로 프로 세 스 를 다시 시작 하면 디 버 깅 프로그램 이 시작 에서 실행 되 는 사이 의 실행 절 차 를 디 버 깅 할 수 있 지 않 겠 느 냐 고 말 할 수 있다.문 제 는 이러한 방법 이 통용 되 는 것 이 아니 라 다 중 프로 세 스 서비스 모델 에 대해 일부 부자 프로 세 스 는 어느 정도 의존 관계 가 있 기 때문에 운영 과정 에서 다시 시작 하기 가 불편 하 다 는 것 이다.이 럴 때 는 방법 2 로 디 버 깅 할 수 있다.
방법 2
gdb 디 버 거 는 follow - fork 라 는 옵션 을 제공 합 니 다. set follow - fork mode 를 통 해 프로 세 스 fork 가 새로운 하위 프로 세 스 를 만 들 때 gdb 는 부모 프로 세 스 (값 은 parent) 를 계속 디 버 깅 하 는 지, 하위 프로 세 스 (값 은 child) 를 계속 디 버 깅 하 는 지, 기본 값 은 부모 프로 세 스 (값 은 parent) 입 니 다.
# fork gdb attach
set follow-fork child
# fork gdb attach ,
set follow-fork parent
쇼 follow - fork 모드 를 사용 하여 현재 값 을 볼 수 있 습 니 다:
(gdb) show follow-fork mode
Debugger response to a program call of fork or vfork is "child".
저 희 는 nginx 를 디 버 깅 하 는 것 을 예 로 들 어 nginx 에서 실행 가능 한 파일 이 있 는 디 렉 터 리 에 먼저 들 어가 방법 1 중의 nginx 서 비 스 를 중단 합 니 다.
[root@iZbp14iz399acush5e8ok7Z sbin]# cd /usr/local/nginx/sbin/
[root@iZbp14iz399acush5e8ok7Z sbin]# ./nginx -s stop
nginx 소스 코드 에 이러한 논리 가 존재 합 니 다. 이 논 리 는 프로그램 main 함수 에서 호출 됩 니 다.
//src/os/unix/ngx_daemon.c:13
ngx_int_t
ngx_daemon(ngx_log_t *log)
{
int fd;
switch (fork()) {
case -1:
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
return NGX_ERROR;
//fork case
case 0:
break;
// fork PID, 0, case
//
default:
exit(0);
}
//... ...
}
상기 코드 에서 설명 한 바 와 같이 메 인 프로 세 스 가 종료 되 지 않도록 nginx 설정 파일 에 한 줄 을 추가 합 니 다.
daemon off;
이렇게 nginx 는 ngx 를 호출 하지 않 습 니 다.daemon 함수 입 니 다.
다음 에 저 희 는
src/event/modules/ngx_epoll_module.c:800
을 실행 한 다음 에 설정 파 라 메 터 를 통 해 디 버 깅 을 기다 리 는 nginx 프로 세 스 에 설정 파일 nginx. conf 를 전달 합 니 다.Quit anyway? (y or n) y
[root@iZbp14iz399acush5e8ok7Z sbin]# gdb nginx
... ...
Reading symbols from nginx...done.
(gdb) set args -c /usr/local/nginx/conf/nginx.conf
(gdb)
다음 run 명령 을 입력 하여 nginx 를 실행 하려 고 시도 합 니 다.
(gdb) run
Starting program: /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
[Thread debugging using libthread_db enabled]
... ...
[Detaching after fork from child process 7509]
앞에서 말 한 바 와 같이 gdb 가 fork 명령 을 만 났 을 때 기본적으로 부모 프로 세 스 에 첨부 됩 니 다. 따라서 상기 출력 에는 "Detaching after fork from child process 7509" 라 는 힌트 가 있 습 니 다.", 우 리 는 Ctrl + C 를 누 르 면 프로그램 을 중단 한 다음 bt 명령 을 입력 하여 현재 호출 된 스 택 을 봅 니 다. 출력 된 스 택 정 보 는 우리 가 방법 1 에서 본 부모 프로 세 스 의 호출 스 택 과 마찬가지 로 gdb 가 프로그램 fork 이후 에 부모 프로 세 스 를 확실히 첨부 했 음 을 설명 합 니 다."
^C
Program received signal SIGINT, Interrupt.
0x00007ffff6f73c5d in sigsuspend () from /lib64/libc.so.6
(gdb) bt
#0 0x00007ffff6f73c5d in sigsuspend () from /lib64/libc.so.6
#1 0x000000000044ae32 in ngx_master_process_cycle (cycle=0x71f720) at src/os/unix/ngx_process_cycle.c:164
#2 0x000000000040bc05 in main (argc=3, argv=0x7fffffffe4e8) at src/core/nginx.c:382
(gdb)
fork 이후 gdb 에서 attach 하위 프로 세 스 를 가 려 면 프로그램 이 실행 되 기 전에 gdb 에 설정
src/event/modules/ngx_epoll_module.c:804
한 다음 run 명령 을 사용 하여 프로그램 을 다시 실행 할 수 있 습 니 다.(gdb) set follow-fork child
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[Attaching after Thread 0x7ffff7fe7740 (LWP 7664) fork to child process 7667]
[New inferior 2 (process 7667)]
[Detaching after fork from parent process 7664]
[Inferior 1 (process 7664) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
^C
Thread 2.1 "nginx" received signal SIGINT, Interrupt.
[Switching to Thread 0x7ffff7fe7740 (LWP 7667)]
0x00007ffff703842b in epoll_wait () from /lib64/libc.so.6
(gdb) bt
#0 0x00007ffff703842b in epoll_wait () from /lib64/libc.so.6
#1 0x000000000044e546 in ngx_epoll_process_events (cycle=0x71f720, timer=18446744073709551615, flags=1) at src/event/modules/ngx_epoll_module.c:800
#2 0x000000000043f317 in ngx_process_events_and_timers (cycle=0x71f720) at src/event/ngx_event.c:247
#3 0x000000000044c38f in ngx_worker_process_cycle (cycle=0x71f720, data=0x0) at src/os/unix/ngx_process_cycle.c:750
#4 0x000000000044926f in ngx_spawn_process (cycle=0x71f720, proc=0x44c2e1 , data=0x0, name=0x4cfd70 "worker process", respawn=-3)
at src/os/unix/ngx_process.c:199
#5 0x000000000044b5a4 in ngx_start_worker_processes (cycle=0x71f720, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
#6 0x000000000044acf4 in ngx_master_process_cycle (cycle=0x71f720) at src/os/unix/ngx_process_cycle.c:131
#7 0x000000000040bc05 in main (argc=3, argv=0x7fffffffe4e8) at src/core/nginx.c:382
(gdb)
이 어 Ctrl + C 를 누 르 면 프로그램 을 중단 시 킨 다음 bt 명령 을 사용 하여 현재 스 레 드 호출 스 택 을 확인 합 니 다. 이것 은 gdb 가 하위 프로 세 스 에 확실히 연결 되 었 음 을 설명 합 니 다.
우 리 는 방법 2 디 버 깅 프로그램 fork 이전 과 그 후의 모든 논 리 를 이용 하여 비교적 통용 되 는 다 중 프로 세 스 디 버 깅 방법 으로 독자 에 게 파악 하도록 건의 할 수 있다.
만약 당신 이 gdb 에 익숙 하지 않다 면, 여 기 는 gdb 강좌 입 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.