Please enable Javascript to view the contents

k8s 容器热替换/重启主进程 - gdb exec 法

 ·  ☕ 6 分钟

k8s 容器热替换/重启主进程 - gdb exec 法

Fabia - Skoda

The first car in my life - Fabia - Skoda

目标

k8s 环境下,在不停止或重启 container 的情况下,重启应用进程(pid:1),甚至重新加载运行新版本的应用。本文以 gdb 作为工具,调用应用容器自带的 libc 的 close & exec 函数,去实现这个目标。

背景

K8s 显然已经由兴起转向成熟。大潮过后,是时候思考一下,当初吹过的牛有哪些是真的,哪些是还未对现的。不可否认,k8s 变革了运维的工作方式,这基本是进步的。但对于开发,特别是障碍问题定位、程序调试方法,显然难度是增加了。

在应用的障碍问题定位、程序调试时,我们时常希望能在相同的环境下重复重启应用,去观察我们对配置的修改,或者程序的更新是否真正解决了问题。要实现这个目标,通常需要:

  1. 修改应用代码,跑 CI pipeline 重新打包 docker image,上传。—— 费时费力 😱
  2. 想办法重启容器。—— 环境破坏了,问题可能重现不了 😱

如果容器启动脚本设计时就支持重启,当然没问题,但大部分情况下,均是不直接支持的,很多时候,应用主进程就直接是 container 的主进程 pid:1 了。

我研究过三个方法去替换主进程:

  1. gdb 调用 libc 的 execl 。 这是本文要说的方法。
  2. gdb 调用 syscall execve 。 这个方法比较复杂,但也更通用,还在研究。
  3. kill -STOP 挂起主进程的父进程
  4. gdb 主进程的父进程,让它收不到 SIGCHLD

警告

由于本文使用了 gdb attach 和非常规方法关闭文件描述符(close(fd))和替换进程执行文件(exec),潜在比较大的未知风险,请不要在生产环境中使用。我也未充分验证这个方法的可靠性,和副作用。包括 close 和 exec 是否能干净清理前任的问题。所以,使用有风险。

思路

  1. gdb attach 进程
  2. 调用 close fd ,特别是 socket 相关的,特别是 listen tcp port 的。
  3. 调用 exec 执行相同或不同的可执行文件

步骤

搭建实验目标环境

环境说明:

  • node: 192.168.122.55
    • Ubuntu 22.04.2 LTS
    • kernel: 5.4.0-152-generic
    • hostname: worknode5
    • gdb 9.2
  • docker image repo: 192.168.122.1:5000

我使用 tomcat 作为例子:

1
2
3
docker pull tomcat:9.0.76-jre17
docker tag tomcat:9.0.76-jre17 localhost:5000/tomcat:9.0.76-jre17
docker push localhost:5000/tomcat:9.0.76-jre17

运行 pod:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
kubectl delete StatefulSet tomcat-worknode5

kubectl apply -f - <<"EOF"

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: tomcat-worknode5
  labels:
    app: tomcat-worknode5
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tomcat-worknode5
  template:
    metadata:
      labels:
          app.kubernetes.io/name: tomcat-worknode5
          app: tomcat-worknode5
      annotations:
        sidecar.istio.io/inject: "false"
    spec:
      restartPolicy: Always
      imagePullSecrets:
      - name: docker-registry-key
      containers:
      - name: main-app
        image: 192.168.122.1:5000/tomcat:9.0.76-jre17
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
          protocol: TCP
          name: http 
          
      affinity: #make sure runing at worknode5(192.168.122.55)
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: "kubernetes.io/hostname"
                operator: In
                values:
                - "worknode5" 

EOF

查看 tomcat 进程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
ssh labile@192.168.122.55

# get the pid of tomcat
export POD="tomcat-worknode5-0"
PIDS=$(pgrep java)
while IFS= read -r TPID; do
    HN=$(sudo nsenter -u -t $TPID hostname)
    if [[ "$HN" = "$POD" ]]; then # space between = is important
        sudo nsenter -u -t $TPID hostname
        export POD_PID=$TPID
    fi
done <<< "$PIDS"
echo $POD_PID
export PID=$POD_PID

# 进程的状态:
➜  ~ ps -f $PID | cat
UID          PID    PPID  C STIME TTY      STAT   TIME CMD
root       38123   37929  2 07:06 ?        Ssl    0:04 /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start

# 进程树结构:
➜  ~ ps -ef --forest | grep -B2 $PID
root       37929       1  0 07:06 ?        00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id 9a43e5d86f29e64eb67ccf2ef19d442e4a06af69def716e181fa52694ec9f43b -address /run/containerd/containerd.sock
65535      37963   37929  0 07:06 ?        00:00:00  \_ /pause
root       38123   37929  1 07:06 ?        00:00:05  \_ /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apac....local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start

# 可见 socket fd
➜  ~ sudo ls -l /proc/$PID/fd
total 0
lrwx------ 1 root root 64 Jun 18 07:06 0 -> /dev/null
l-wx------ 1 root root 64 Jun 18 07:06 1 -> 'pipe:[181879]'
l-wx------ 1 root root 64 Jun 18 07:07 10 -> /usr/local/tomcat/logs/host-manager.2023-06-18.log
...
lr-x------ 1 root root 64 Jun 18 07:07 19 -> /usr/local/tomcat/lib/tomcat-i18n-es.jar
l-wx------ 1 root root 64 Jun 18 07:06 2 -> 'pipe:[181880]'
lr-x------ 1 root root 64 Jun 18 07:07 20 -> /usr/local/tomcat/lib/tomcat-i18n-cs.jar
...
lrwx------ 1 root root 64 Jun 18 07:07 43 -> 'socket:[182049]'
lrwx------ 1 root root 64 Jun 18 07:07 44 -> 'socket:[182552]'
l-wx------ 1 root root 64 Jun 18 07:07 45 -> /usr/local/tomcat/logs/localhost_access_log.2023-06-18.txt
lrwx------ 1 root root 64 Jun 18 07:07 46 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 Jun 18 07:07 47 -> 'anon_inode:[eventfd]'
lrwx------ 1 root root 64 Jun 18 07:07 49 -> 'socket:[182059]'
...
l-wx------ 1 root root 64 Jun 18 07:07 9 -> /usr/local/tomcat/logs/manager.2023-06-18.log


➜  ~ sudo strings /proc/$PID/environ 
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=tomcat-worknode5-0
LANGUAGE=en_US:en
JAVA_HOME=/opt/java/openjdk
GPG_KEYS=48F8E69F6390C9F25CFEDCD268248959359E722B A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243
HTTPBIN_PORT_80_TCP_PORT=80
PWD=/usr/local/tomcat
TOMCAT_SHA512=028163cbe15367f0ab60e086b0ebc8d774e62d126d82ae9152f863d4680e280b11c9503e3b51ee7089ca9bea1bfa5b535b244a727a3021e5fa72dd7e9569af9a
TOMCAT_MAJOR=9
HOME=/root
LANG=en_US.UTF-8
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
HTTPBIN_SERVICE_PORT=80
TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib
CATALINA_HOME=/usr/local/tomcat
SHLVL=0
KUBERNETES_PORT_443_TCP_PROTO=tcp
JDK_JAVA_OPTIONS= --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
FORTIO_SERVER_SERVICE_PORT_HTTP=8080
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib
KUBERNETES_SERVICE_HOST=10.96.0.1
LC_ALL=en_US.UTF-8
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/tomcat/bin:/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TOMCAT_VERSION=9.0.76
JAVA_VERSION=jdk-17.0.7+7


➜  ~ sudo nsenter -n -t $PID ss -tpna | grep $PID
# 可见 fd 与 listen port 的对应关系
LISTEN   0        1           [::ffff:127.0.0.1]:8005     *:*   users:(("java",pid=38123,fd=49))   
LISTEN   0        100                          *:8080     *:*   users:(("java",pid=38123,fd=43)) 

上面已经写得比较明细了,其它补充如下:

  • 进程的状态:ps 的 STAT 显示 Ssl。说明见 man ps
               D    uninterruptible sleep (usually IO)
               I    Idle kernel thread
               R    running or runnable (on run queue)
               S    interruptible sleep (waiting for an event to
                    complete)
               T    stopped by job control signal
               t    stopped by debugger during the tracing
               W    paging (not valid since the 2.6.xx kernel)
               X    dead (should never be seen)
               Z    defunct ("zombie") process, terminated but not
                    reaped by its parent

       For BSD formats and when the stat keyword is used, additional
       characters may be displayed:

               <    high-priority (not nice to other users)
               N    low-priority (nice to other users)
               L    has pages locked into memory (for real-time and
                    custom IO)
               s    is a session leader
               l    is multi-threaded (using CLONE_THREAD, like NPTL
                    pthreads do)
               +    is in the foreground process group
  • socket listen:
➜  ~ sudo nsenter -n -t $PID ss -tpna | grep $PID
LISTEN   0        1           [::ffff:127.0.0.1]:8005     *:*   users:(("java",pid=38123,fd=49))   
LISTEN   0        100                          *:8080     *:*   users:(("java",pid=38123,fd=43)) 

gdb attach

1
2
3
4
5
6
7
8
9
➜  ~ sudo gdb -p $PID
...
# 下面可以看出,java 进程使用了 libc
0x00007ffff7dea197 in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6

➜  ~ ps -f $PID
# STAT 变为 `tsl`
UID          PID    PPID  C STIME TTY      STAT   TIME CMD
root       38123   37929  0 07:06 ?        tsl    0:06 /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Dj

close fd

1
2
3
4
5
6
7
8
# fd 0 到 2 作为 stdin/stdout/stderr 不关闭,其它,从 3 开始关闭。
# 上面的 ls -l /proc/$PID/fd 看到,最大 fd 号是 49
(gdb) set $max=49
(gdb) set $current=3
(gdb) while ($current <= $max)
 > p (int) close($current++)
 >end
 # 在我的环境,需要多次执行上面的循环,才真正关闭了所有 fd,不知道为何。
1
2
3
4
5
6
➜  ~ sudo ls -l /proc/$PID/fd
total 0
# socket fd 已经被关闭了
lrwx------ 1 root root 64 Jun 18 07:06 0 -> /dev/null
l-wx------ 1 root root 64 Jun 18 07:06 1 -> 'pipe:[181879]'
l-wx------ 1 root root 64 Jun 18 07:06 2 -> 'pipe:[181880]'

调用 libc 的 execl,替换进程

函数说明:man exec

1
2
3
4
# 调用的参数来源于之前 ps -f $PID 的输出。注意:函数调用的第一个参数是可执行文件(ELF) 的路径,第二个参数是进程名,不是我们一般命令行的参数,最后一个参数需要补 0 。
# 为验证方便,我加了一个参数:-DgdbRestarted=true

p (int) execl("/opt/java/openjdk/bin/java", "java", "-DgdbRestarted=true", "-Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties", "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager", "-Djdk.tls.ephemeralDHKeySize=2048", "-Djava.protocol.handler.pkgs=org.apache.catalina.webresources", "-Dorg.apache.catalina.security.SecurityListener.UMASK=0027", "-Dignore.endorsed.dirs=", "-classpath", "/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar", "-Dcatalina.base=/usr/local/tomcat", "-Dcatalina.home=/usr/local/tomcat", "-Djava.io.tmpdir=/usr/local/tomcat/temp", "org.apache.catalina.startup.Bootstrap", "start",0)

检查

1
2
3
➜  ~ ps -f $PID | cat
UID          PID    PPID  C STIME TTY      STAT   TIME CMD
root       38123   37929  0 07:06 ?        ts     0:07 java -DgdbRestarted=true -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start

gdb detach

(gdb) detach

替换进程后的检查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
➜  ~ ps -f $PID | cat
UID          PID    PPID  C STIME TTY      STAT   TIME CMD
root       38123   37929  0 07:06 ?        Ssl    0:10 java -DgdbRestarted=true -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start

➜  ~ sudo ls -l /proc/$PID/fd  
total 0
lrwx------ 1 root root 64 Jun 18 07:46 0 -> /dev/null
l-wx------ 1 root root 64 Jun 18 07:46 1 -> 'pipe:[181879]'
l-wx------ 1 root root 64 Jun 18 07:50 10 -> /usr/local/tomcat/logs/host-manager.2023-06-18.log
lr-x------ 1 root root 64 Jun 18 07:50 11 -> /usr/local/tomcat/lib/jaspic-api.jar
...
lr-x------ 1 root root 64 Jun 18 07:50 17 -> /usr/local/tomcat/lib/el-api.jar
lr-x------ 1 root root 64 Jun 18 07:50 18 -> /usr/local/tomcat/lib/ecj-4.20.jar
lr-x------ 1 root root 64 Jun 18 07:50 19 -> /usr/local/tomcat/lib/tomcat-i18n-es.jar
l-wx------ 1 root root 64 Jun 18 07:46 2 -> 'pipe:[181880]'
lr-x------ 1 root root 64 Jun 18 07:50 20 -> /usr/local/tomcat/lib/tomcat-i18n-cs.jar
lr-x------ 1 root root 64 Jun 18 07:50 21 -> /usr/local/tomcat/lib/annotations-api.jar
lr-x------ 1 root root 64 Jun 18 07:50 22 -> /usr/local/tomcat/lib/servlet-api.jar
...
lr-x------ 1 root root 64 Jun 18 07:50 42 -> /usr/local/tomcat/lib/tomcat-i18n-ru.jar
lrwx------ 1 root root 64 Jun 18 07:50 43 -> 'socket:[282010]'
lrwx------ 1 root root 64 Jun 18 07:50 44 -> 'socket:[281414]'
l-wx------ 1 root root 64 Jun 18 07:50 45 -> /usr/local/tomcat/logs/localhost_access_log.2023-06-18.txt
lrwx------ 1 root root 64 Jun 18 07:50 46 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 Jun 18 07:50 47 -> 'anon_inode:[eventfd]'
lrwx------ 1 root root 64 Jun 18 07:50 49 -> 'socket:[282022]'
lr-x------ 1 root root 64 Jun 18 07:50 5 -> /usr/local/tomcat/bin/bootstrap.jar
lr-x------ 1 root root 64 Jun 18 07:50 6 -> /usr/local/tomcat/bin/commons-daemon.jar
l-wx------ 1 root root 64 Jun 18 07:50 7 -> /usr/local/tomcat/logs/catalina.2023-06-18.log
l-wx------ 1 root root 64 Jun 18 07:50 8 -> /usr/local/tomcat/logs/localhost.2023-06-18.log
l-wx------ 1 root root 64 Jun 18 07:50 9 -> /usr/local/tomcat/logs/manager.2023-06-18.log

➜  ~ sudo nsenter -n -t $PID ss -tpna | grep $PID
LISTEN   0        1           [::ffff:127.0.0.1]:8005    *:*       users:(("java",pid=38123,fd=49)) 
LISTEN   0        100                          *:8080    *:*       users:(("java",pid=38123,fd=43))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
kubectl logs tomcat-worknode5-0 | less

NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
18-Jun-2023 07:06:57.361 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name:   Apache Tomcat/9.0.76
18-Jun-2023 07:06:57.388 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Jun 5 2023 07:17:04 UTC
18-Jun-2023 07:06:57.389 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 9.0.76.0
18-Jun-2023 07:06:57.389 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
18-Jun-2023 07:06:57.390 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            5.4.0-152-generic
18-Jun-2023 07:06:57.390 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
18-Jun-2023 07:06:57.390 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /opt/java/openjdk
18-Jun-2023 07:06:57.390 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           17.0.7+7
...
18-Jun-2023 07:06:59.137 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
18-Jun-2023 07:06:59.354 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [2996] milliseconds
18-Jun-2023 07:06:59.688 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
18-Jun-2023 07:06:59.689 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/9.0.76]
18-Jun-2023 07:06:59.771 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
18-Jun-2023 07:06:59.845 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [490] milliseconds


------------- 替换后: --------------

NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
18-Jun-2023 07:49:57.887 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name:   Apache Tomcat/9.0.76
18-Jun-2023 07:49:57.894 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Jun 5 2023 07:17:04 UTC
18-Jun-2023 07:49:57.894 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 9.0.76.0
18-Jun-2023 07:49:57.895 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
18-Jun-2023 07:49:57.898 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            5.4.0-152-generic
18-Jun-2023 07:49:57.899 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
18-Jun-2023 07:49:57.899 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /opt/java/openjdk
18-Jun-2023 07:49:57.900 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           17.0.7+7
...

18-Jun-2023 07:49:58.777 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
18-Jun-2023 07:49:58.853 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [1340] milliseconds
18-Jun-2023 07:49:58.944 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
18-Jun-2023 07:49:58.944 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/9.0.76]
18-Jun-2023 07:49:58.976 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
18-Jun-2023 07:49:59.009 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [155] milliseconds

一些使用限制

由于这个方法使用了 libc ,要求目标进程使用了 libc。大多数执行文件会使用 libc 的:

1
2
3
4
5
6
7
8
➜  ~ sudo ldd /proc/$PID/root/opt/java/openjdk/bin/java           
	linux-vdso.so.1 (0x00007ffff7fce000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007ffff7f9e000)
	libjli.so => /proc/38123/root/opt/java/openjdk/bin/../lib/libjli.so (0x00007ffff7f8b000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ffff7f68000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffff7f62000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7d70000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcf000)

但也有一些例外,起码包括:

  • Alpine Linux docker image 的 libc 比较特别,我的 gdb 无法识别
  • Golang 静态 link 了 libc 的情况
分享

Mark Zhu
作者
Mark Zhu
An old developer