国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

為什么在容器中 1 號進程掛不上 arthas?

這篇具有很好參考價值的文章主要介紹了為什么在容器中 1 號進程掛不上 arthas?。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

最近在容器環(huán)境中,發(fā)現在 Java 進程是 1 號進程的情況下,無法使用 arthas。

提示 AttachNotSupportedException: Unable to get pid of LinuxThreads manager thread。具體操作和報錯如下:

# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.6
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1 com.alibabacloud.mse.demo.ZuulApplication
1
[INFO] arthas home: /home/admin/.opt/ArmsAgent/arthas
[INFO] Try to attach process 1
[ERROR] Start arthas failed, exception stack trace:
com.sun.tools.attach.AttachNotSupportedException: Unable to get pid of LinuxThreads manager thread
    at sun.tools.attach.LinuxVirtualMachine.<init>(LinuxVirtualMachine.java:86)
    at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:78)
    at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:250)
    at com.taobao.arthas.core.Arthas.attachAgent(Arthas.java:117)
    at com.taobao.arthas.core.Arthas.<init>(Arthas.java:27)
    at com.taobao.arthas.core.Arthas.main(Arthas.java:166)
[INFO] Attach process 1 success.

之前也遇到過,總是調整了下鏡像,讓 Java 進程不是 1 號進程就可以了。但這個不是長久之計,還是要抽時間看下這個問題。

復現問題

我們創(chuàng)建如下項目,來復現這個問題:

public class Main {
  public static void main(String args[]) throws Exception {
    while (true) {
      System.out.println("hello!");
      Thread.sleep(30 * 1000);
    }
  }
}
FROM openjdk:8u212-jdk-alpine
COPY ./ /app
WORKDIR /app/src/main/java/
RUN javac Main.java
CMD ["java", "Main"]

然后正常啟動應用,并嘗試用 arthas,或者 jstack:

$ # 構建鏡像
$ docker build . -t example-attach
$ # 啟動容器
$ docker run --name example-attach --rm example-attach

$ # 在另一個終端進入容器,執(zhí)行jstack
$ docker exec -it example-attach sh
/app/src/main/java # jstack 1
1: Unable to get pid of LinuxThreads manager thread

成功復現問題!接下來開始分析。

正常的 attach 流程是什么樣子的?

如下是在排查問題中,梳理出來的 jvm Attach 流程:

  1. 查找 /tmp/.java_pid${pid} 這個 unix socket,如果存在則檢查權限,然后建立連接。
  2. 如果不存在則先創(chuàng)建 /proc/${pid}/cwd/.attach_pid${pid},開始通知 jvm 線程。
  3. 首先判斷是不是 LinuxThread如果是 LinuxThread則找到 LinuxThreadsManager,然后給其所有子進程發(fā)送 SIGQUIT.
  4. 如果不是 LinuxThread,則直接給目標進程發(fā)送 SIGQUIT。
  5. 目標進程收到信號后,創(chuàng)建 Attach Listener,監(jiān)聽 /tmp/.java_pid${pid}。
  6. 開始正常的 socket 通信,根據通信的具體內容,可以是 dumpThread(jstack),也可以是加載 JavaAgent,比如上面提到的 arthas。

Java Attach 機制之 Native 篇[1]也是一個不錯的 attach API 解析。

為什么對1號進程 attach 會報錯?

首先,/tmp/.java_pid${pid} 當時是肯定不存在的,如果存在就是直接通信加載 Arthas 了。也可以通過查看文件來確認這一點。

其次,.attach_pid${pid} 文件也是能夠創(chuàng)建成功的,

我們也可以通過 strace 輸出來確認:

open("/proc/424/cwd/.attach_pid424", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0666 <unfinished ...>。

最有可能的原因就是線程判斷、發(fā)送信號這一步了,我們以 jstack 為例查找為什么 attach 會失敗。

本來類似上一次的查找過程,想著通過調試符號來查,但是在 alpine 上的調試符號無法顯示源碼內容,編譯環(huán)境又很麻煩。所以還是優(yōu)先用 strace 來查,值得注意的是, jstack 的邏輯中有 fork,所以記得使用 strace -f jstack 1 來查。

查了下 strace 的輸出,沒有 kill 請求??磥韱栴}是處在線程模型判定的。

剛剛提到 jvm 會判斷是不是 LinuxThread,那么什么是 LinuxThread 呢?首先看下判斷的源碼:

為什么在容器中 1 號進程掛不上 arthas?

通俗的講,Linux 內核剛開始是不支持“線程”的,LinuxThread 機制就是通過 fork 機制+共享內存空間的方式來實現線程。但 LinuxThread 在內核看來就是一些獨立的父子進程,在信號處理、同步原語上有很多缺陷,要通過 manager thread 來處理這些邏輯。后來 Red Hat 發(fā)起 NPTL,內核開始支持線程能力,也能夠通過更加標準的方式來處理信號、同步等邏輯。

可以用 getconf GNU_LIBPTHREAD_VERSION 來查看是哪種線程模型,比如我的機器上輸出是 NPTL 2.34。

當然,如上面代碼所寫。可以用 confstr(_CS_GNU_LIBPTHREAD_VERSION,) 來獲取當前的線程模型,詳情參考手冊[2]

  • 如果 confstr(_CS_GNU_LIBPTHREAD_VERSION,) 返回 0,則表示是 glibc 舊版本,認為是 LinuxThread:先找到 manager thread(通過查找父進程),然后給各個子進程發(fā)送 SIGQUIT 信號(這個過程需要遍歷系統(tǒng)內所有進程)。
  • 如果 confstr(_CS_GNU_LIBPTHREAD_VERSION,) 結果包含 NPTL,則認為不是 LinuxThread,按照 NPTL 來處理:直接發(fā)送 SIGQUIT。

但很可惜的是,LinuxThread/confstr(_CS_GNU_LIBPTHREAD_VERSION,) 不是 POSIX 標準,所以 Alpine 自帶的 musl 對這個調用返回 0。

按照上面邏輯,jvm 會認為是 LinuxThread,嘗試找到父進程,如果 pid 是 1 的話,自然找不到父進程,所以報錯 Unable to get pid of LinuxThreads manager thread,導致文章最開始說的 arthas 無法使用。

關于兩種線程模型的詳細比較,可以參考?Linux 線程模型比較:LinuxThreads 和 NPTL[3]。

為什么非1號進程就能 attach?

模擬了下先手動進入 shell(這時 sh 就是 1 號進程),然后再手動執(zhí)行 java Main(pid為 8 ),然后我們看下 getLinuxThreadsManager 是怎么表現的:

為什么在容器中 1 號進程掛不上 arthas?

可以看到,在這種情況下,jvm 認為 manager thread 是 1 號進程。此時會后執(zhí)行 sendQuitToChildrenOf(mpid):

為什么在容器中 1 號進程掛不上 arthas?

為什么在容器中 1 號進程掛不上 arthas?

即遍歷所有的子進程,都發(fā)送 SIGQUIT,這個邏輯其實是有點奇怪的。“超凡的主張,需要有超凡的證據”[4]。我們重新跑一遍,用 strace -f 驗證一下。

進程樹(其中綠色的是線程):

為什么在容器中 1 號進程掛不上 arthas?

jstack 發(fā)送的 kill 信號,可以看到 jstack 給 1 號進程的所有子進程發(fā)送了 SIGQUIT:

為什么在容器中 1 號進程掛不上 arthas?

這個行為和剛剛分析是一致的。不過非常巧合的是,大部分進程是忽略了 SIGQUIT 信號的,所以在這種情況下,jstack 反而是正常工作了的。

怎么解決這個問題?

最快捷 workaround

注:這種方式不需要調整容器參數,不需要重啟容器,比較推薦。

既然 attach 主要卡在了發(fā)送信號上,那我們就用 shell 來模擬這個流程:

pid=1 ;\
touch /proc/${pid}/cwd/.attach_pid${pid} && \
  kill -SIGQUIT ${pid} && \
  sleep 2 &&
  ls /proc/${pid}/root/tmp/.java_pid${pid}
# 接下來就可以正常 java -jar arthas-boot.jar 掛arthas了

通過上面的操作后,Attach Listener 已經啟動并且監(jiān)聽了路徑,第二次 attach 就直接可以連接了;就可以按照正常的方式使用 arthas 了。

其中有一點需要注意,一定需要提前創(chuàng)建 .attach_pid${pid} 文件,不然 jvm 會將這個信號交給默認的 sigaction 處理,對于 pid 1 來說,會導致容器退出!

也有人基于類似原理,做了一個?jattach[5]?工具,可以直接在 Alpine 中,通過 apk add jattach 來安裝,然后 jattach ${pid} properties,也能起到一樣的效果。

設置啟動參數

注:這種方式需要調整啟動參數或者環(huán)境變量,需要重啟應用/容器,可能會丟失業(yè)務現場。

Jvm 支持設置 -XX:+StartAttachListener,這樣就能在啟動 Jvm 的時候,自動啟動 Attach Listener 線程并監(jiān)聽,也可以正常使用 arthas。

對于容器環(huán)境下,更加容易的做法是給容器添加環(huán)境變量 JAVA_TOOL_OPTIONS=-XX:+StartAttachListener,這樣不用修改啟動腳本也能達到效果。

上游優(yōu)先,修改鏡像

注:這種方式需要修改鏡像。

OpenJDK 8 官方沒有修復這個問題,所以如果直接使用 openjdk:8-jdk-alpine,是避免不了這個問題的。Docker 鏡像倉庫也有人討論這個問題[6]。

OpenJDK 11 就已經解決了這個問題了(見源碼[7]),不再對古舊的 LinuxThread 模型做判斷,這樣 arthas 也能工作。

不過 Alpine 官方倉庫中的 OpenJDK 8 已經通過自己打 patch 的方式,修復了這個問題:

https://gitlab.alpinelinux.org/alpine/aports/-/issues/13032

作為比較知名的 JDK 發(fā)行版,也在 eclipse-temurin:8-jdk-alpine 中修復了這個問題,可以直接使用這個鏡像。相關討論見:https://github.com/adoptium/jdk8u/pull/8

總結

在 arthas 的 issue 中,或者網上相關的文章中,總是重復著 Java 不能作為 1 號進程。很多時候,就因為如此,我們沒有辦法掛上診斷工具,導致現場丟失,故障原因不能及時定位。

作為技術人員還是需要了解底層,這樣在排查問題、架構設計上才會有更多自由度,更能夠抓住問題、解決問題。

后續(xù)還會出系列文章,來解決容器環(huán)境下奇奇怪怪的 jvm 問題,歡迎關注!

相關鏈接

[1] Java Attach 機制之 Native 篇

https://my.oschina.net/u/3784034/blog/5526214

[2] 詳情參考手冊

https://man7.org/linux/man-pages/man3/confstr.3.html

[3] Linux 線程模型比較:LinuxThreads 和 NPTL

https://www.jianshu.com/p/6c507b966ad1

[4] 超凡的主張,需要有超凡的證據

https://zh.wikipedia.org/zh-hans/%E8%96%A9%E6%A0%B9%E6%A8%99%E6%BA%96

[5] jattach

https://github.com/apangin/jattach

[6] Docker 鏡像倉庫也有人討論這個問題

https://github.com/docker-library/openjdk/issues/76

[7] 源碼

https://github.com/openjdk/jdk11u/blob/jdk-11%2B28/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java#L78

作者:卜比

原文鏈接

本文為阿里云原創(chuàng)內容,未經允許不得轉載。文章來源地址http://www.zghlxwxcb.cn/news/detail-449292.html

到了這里,關于為什么在容器中 1 號進程掛不上 arthas?的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 什么是線程?為什么需要線程?和進程的區(qū)別?

    什么是線程?為什么需要線程?和進程的區(qū)別?

    目錄 前言 一.線程是什么? 1.1.為什么需要線程 1.2線程的概念 1.3線程和進程的區(qū)別 ?二.線程的生命周期 三.認識多線程 總結 ??個人主頁:tq02的博客_CSDN博客-C語言,Java,Java數據結構領域博主 ?? 本文由 tq02 原創(chuàng),首發(fā)于 CSDN?? ???本章講解內容: 線程的講解 ??學習專欄:

    2024年02月14日
    瀏覽(21)
  • 僵尸進程?孤兒進程?為什么他有如此慘烈的身世...

    僵尸進程?孤兒進程?為什么他有如此慘烈的身世...

    ??作者簡介: 花想云 ,在讀本科生一枚,C/C++領域新星創(chuàng)作者,新星計劃導師,阿里云專家博主,CSDN內容合伙人…致力于 C/C++、Linux 學習。 ?? 專欄簡介:本文收錄于 Linux從入門到精通 ,本專欄主要內容為本專欄主要內容為Linux的系統(tǒng)性學習,專為小白打造的文章專欄。

    2024年02月05日
    瀏覽(96)
  • 進程切換的開銷為什么比線程更大

    進程切換的開銷通常比線程更大,這是因為進程是操作系統(tǒng)中的基本執(zhí)行單元,而線程是在進程內部的執(zhí)行單元。 主要原因: 上下文切換的開銷: 進程切換需要保存和恢復更多的上下文信息。上下文信息包括進程的程序計數器、寄存器狀態(tài)、內存映射、I/O 狀態(tài)等,這些信息

    2024年01月18日
    瀏覽(30)
  • 為什么要用開源容器?

    說到開源容器,大家首先想起來的應該是Docker吧,那么我們就以Docker來從個人角度理解一下為什么要用開源容器。 通常都會說Docker開源容器,但是Docker 實際上是一個開源的應用容器引擎。Docker是一個基于輕量級虛擬化技術的容器,整個項目基于Go語言開發(fā),并采用了Apache 2

    2024年04月16日
    瀏覽(61)
  • 31、Spring容器啟動時,為什么先加載BeanFactoryPostProcess

    因為BeanDefinition會在ioc容器加載的時候先注冊, 而BeanFactoryPostProcess就是在所有的BeanDefinition注冊完后做擴展的,所以要先加載BeanFactoryPostProcess 解析配置類的組件 它就實現BeanFactoryPostProcess, 所以要先去加載BeanFactoryPostProcess 方式一:通過BeanFactory獲取 方式二 :通過BeanFactor

    2024年02月04日
    瀏覽(27)
  • 『Linux從入門到精通』第 ? 期 - 僵尸進程?孤兒進程?為什么他有如此慘烈的身世...

    『Linux從入門到精通』第 ? 期 - 僵尸進程?孤兒進程?為什么他有如此慘烈的身世...

    ??作者簡介: 花想云 ,在讀本科生一枚,C/C++領域新星創(chuàng)作者,新星計劃導師,阿里云專家博主,CSDN內容合伙人…致力于 C/C++、Linux 學習。 ?? 專欄簡介:本文收錄于 Linux從入門到精通 ,本專欄主要內容為本專欄主要內容為Linux的系統(tǒng)性學習,專為小白打造的文章專欄。

    2024年02月16日
    瀏覽(20)
  • 06 為什么需要多線程;多線程的優(yōu)缺點;程序 進程 線程之間的關系;進程和線程之間的區(qū)別

    06 為什么需要多線程;多線程的優(yōu)缺點;程序 進程 線程之間的關系;進程和線程之間的區(qū)別

    CPU、內存、IO之間的性能差異巨大 多核心CPU的發(fā)展 線程的本質是增加一個可以執(zhí)行代碼工人 多線程的優(yōu)點 多個執(zhí)行流,并行執(zhí)行。(多個工人,干不一樣的活) 多線程的缺點 上下文切換慢,切換上下文典型值1us vs 0.3ns/cycle CPU在執(zhí)行A任務(A沒有執(zhí)行完)時,切換到任務B,需

    2024年02月14日
    瀏覽(17)
  • 多進程運行含有任意參數的函數、為什么multiprosessing會進行多次初始化

    目錄 多進程運行含有任意個參數的函數,以map_async為例 為什么multiprocessing 的了進程會多次初始化? ????????使用偏函數:偏函數有點像數學中的偏導數,可以讓我們只關注其中的某一個變量而不考慮其他變量的影響。 如以下代碼中,我們要將set_seq、tokenizer和model作為變量

    2024年02月03日
    瀏覽(17)
  • 在容器鏡像中為了安全為什么要刪除 setuid 和 setgid?

    在容器鏡像中刪除 setuid(set user ID)和 setgid(set group ID)權限通常是出于安全考慮。這兩個權限位允許進程在執(zhí)行時以文件所有者或文件所屬組的身份運行,而不是以調用進程的用戶身份運行。 刪除 setuid 和 setgid 權限的主要原因包括: 減少潛在的權限濫用: 如果容器中的

    2024年02月20日
    瀏覽(17)
  • 【容器架構】你知道有 Docker 為什么還要 K8s 嗎?

    【容器架構】你知道有 Docker 為什么還要 K8s 嗎?

    ?? 博主介紹 : 博主從事應用安全和大數據領域,有8年研發(fā)經驗,5年面試官經驗,Java技術專家,WEB架構師,阿里云專家博主,華為云云享專家,51CTO TOP紅人 Java知識圖譜點擊鏈接: 體系化學習Java(Java面試專題) ???? 感興趣的同學可以收藏關注下 , 不然下次找不到喲

    2024年02月16日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包