天天看點

JVM Attach機制實作

attach是什麼

在講這個之前,我們先來點大家都知道的東西,當我們感覺線程一直卡在某個地方,想知道卡在哪裡,首先想到的是進行線程dump,而常用的指令是jstack <pid>,我們就可以看到如下線程棧了

JVM Attach機制實作

大家是否注意過上面圈起來的兩個線程,”attach listener”和“signal dispatcher”,這兩個線程是我們這次要講的attach機制的關鍵,先偷偷告訴各位,其實attach listener這個線程在jvm起來的時候可能并沒有的,後面會細說。

那attach機制是什麼?說簡單點就是jvm提供一種jvm程序間通信的能力,能讓一個程序傳指令給另外一個程序,并讓它執行内部的一些操作,比如說我們為了讓另外一個jvm程序把線程dump出來,那麼我們跑了一個jstack的程序,然後傳了個pid的參數,告訴它要哪個程序進行線程dump,既然是兩個程序,那肯定涉及到程序間通信,以及傳輸協定的定義,比如要執行什麼操作,傳了什麼參數等。

attach能做些什麼

總結起來說,比如記憶體dump,線程dump,類資訊統計(比如加載的類及大小以及執行個體個數等),動态加載agent(使用過btrace的應該不陌生),動态設定vm flag(但是并不是所有的flag都可以設定的,因為有些flag是在jvm啟動過程中使用的,是一次性的),列印vm flag,擷取系統屬性等,這些對應的源碼(attachlistener.cpp)如下

<code>01</code>

<code>static</code> <code>attachoperationfunctioninfo funcs[] = {</code>

<code>02</code>

<code></code><code>{ &amp;quot;agentproperties&amp;quot;, get_agent_properties },</code>

<code>03</code>

<code></code><code>{ &amp;quot;datadump&amp;quot;, data_dump },</code>

<code>04</code>

<code></code><code>{ &amp;quot;dumpheap&amp;quot;, dump_heap },</code>

<code>05</code>

<code></code><code>{ &amp;quot;load&amp;quot;, jvmtiexport::load_agent_library },</code>

<code>06</code>

<code></code><code>{ &amp;quot;properties&amp;quot;, get_system_properties },</code>

<code>07</code>

<code></code><code>{ &amp;quot;threaddump&amp;quot;, thread_dump },</code>

<code>08</code>

<code></code><code>{ &amp;quot;inspectheap&amp;quot;, heap_inspection },</code>

<code>09</code>

<code></code><code>{ &amp;quot;setflag&amp;quot;, set_flag },</code>

<code>10</code>

<code></code><code>{ &amp;quot;printflag&amp;quot;, print_flag },</code>

<code>11</code>

<code></code><code>{ &amp;quot;jcmd&amp;quot;, jcmd },</code>

<code>12</code>

<code></code><code>{ null, null }</code>

<code>13</code>

<code>};</code>

後面是指令對應的處理函數。

attach在jvm裡如何實作的

attach listener線程的建立

前面也提到了,jvm在啟動過程中可能并沒有啟動attach listener這個線程,可以通過jvm參數來啟動,代碼(threads::create_vm)如下:

<code>if</code> <code>(!disableattachmechanism) {</code>

<code></code><code>if</code> <code>(startattachlistener || attachlistener::init_at_startup()) {</code>

<code></code><code>attachlistener::init();</code>

<code></code><code>}</code>

<code>bool attachlistener::init_at_startup() {</code>

<code></code><code>if</code> <code>(reducesignalusage) {</code>

<code></code><code>return</code> <code>true</code><code>;</code>

<code></code><code>}</code><code>else</code> <code>{</code>

<code></code><code>return</code> <code>false</code><code>;</code>

<code>}</code>

其中disableattachmechanism,startattachlistener ,reducesignalusage均預設是false(globals.hpp)

<code>1</code>

<code>product(bool, disableattachmechanism,</code><code>false</code><code>, \</code>

<code>2</code>

<code></code><code>&amp;quot;disable mechanism that allows tools to attach to</code><code>this</code> <code>vm&amp;rdquo;)</code>

<code>3</code>

<code>product(bool, startattachlistener,</code><code>false</code><code>, \</code>

<code>4</code>

<code></code><code>&amp;quot;always start attach listener at vm startup&amp;quot;)</code>

<code>5</code>

<code>product(bool, reducesignalusage,</code><code>false</code><code>, \</code>

<code>6</code>

<code></code><code>&amp;quot;reduce the use of os signals in java and/or the vm&amp;rdquo;)</code>

是以attachlistener::init()并不會被執行,而attach listener線程正是在此方法裡建立的

<code>// starts the attach listener thread</code>

<code>void</code> <code>attachlistener::init() {</code>

<code></code><code>exception_mark;</code>

<code></code><code>klassoop k = systemdictionary::resolve_or_fail(vmsymbols::java_lang_thread(),</code><code>true</code><code>, check);</code>

<code></code><code>instanceklasshandle klass (thread, k);</code>

<code></code><code>instancehandle thread_oop = klass-&amp;gt;allocate_instance_handle(check);</code>

<code></code><code>const</code> <code>char</code> <code>thread_name[] = &amp;quot;attach listener&amp;quot;;</code>

<code></code><code>handle string = java_lang_string::create_from_str(thread_name, check);</code>

<code></code><code>// initialize thread_oop to put it into the system threadgroup</code>

<code></code><code>handle thread_group (thread, universe::system_thread_group());</code>

<code></code><code>javavalue result(t_void);</code>

<code>14</code>

<code></code><code>javacalls::call_special(&amp;amp;result, thread_oop,</code>

<code>15</code>

<code></code><code>klass,</code>

<code>16</code>

<code></code><code>vmsymbols::object_initializer_name(),</code>

<code>17</code>

<code></code><code>vmsymbols::threadgroup_string_void_signature(),</code>

<code>18</code>

<code></code><code>thread_group,</code>

<code>19</code>

<code></code><code>string,</code>

<code>20</code>

<code></code><code>check);</code>

<code>21</code>

<code>22</code>

<code></code><code>klasshandle group(thread, systemdictionary::threadgroup_klass());</code>

<code>23</code>

<code></code><code>javacalls::call_special(&amp;amp;result,</code>

<code>24</code>

<code>25</code>

<code></code><code>group,</code>

<code>26</code>

<code></code><code>vmsymbols::add_method_name(),</code>

<code>27</code>

<code></code><code>vmsymbols::thread_void_signature(),</code>

<code>28</code>

<code></code><code>thread_oop,</code><code>// arg 1</code>

<code>29</code>

<code>30</code>

<code>31</code>

<code></code><code>{ mutexlocker mu(threads_lock);</code>

<code>32</code>

<code></code><code>javathread* listener_thread =</code><code>new</code><code>javathread(&amp;amp;attach_listener_thread_entry);</code>

<code>33</code>

<code>34</code>

<code></code><code>// check that thread and osthread were created</code>

<code>35</code>

<code></code><code>if</code> <code>(listener_thread == null || listener_thread-&amp;gt;osthread() == null) {</code>

<code>36</code>

<code></code><code>vm_exit_during_initialization(&amp;quot;java.lang.outofmemoryerror&amp;quot;,</code>

<code>37</code>

<code></code><code>&amp;quot;unable to create</code><code>new</code> <code>native</code><code>thread&amp;quot;);</code>

<code>38</code>

<code>39</code>

<code>40</code>

<code></code><code>java_lang_thread::set_thread(thread_oop(), listener_thread);</code>

<code>41</code>

<code></code><code>java_lang_thread::set_daemon(thread_oop());</code>

<code>42</code>

<code>43</code>

<code></code><code>listener_thread-&amp;gt;set_threadobj(thread_oop());</code>

<code>44</code>

<code></code><code>threads::add(listener_thread);</code>

<code>45</code>

<code></code><code>thread::start(listener_thread);</code>

<code>46</code>

<code>47</code>

既然在啟動的時候不會建立這個線程,那麼我們在上面看到的那個線程是怎麼建立的呢,這個就要關注另外一個線程“signal dispatcher”了,顧名思義是處理信号的,這個線程是在jvm啟動的時候就會建立的,具體代碼就不說了。

下面以jstack的實作來說明觸發attach這一機制進行的過程,jstack指令的實作其實是一個叫做jstack.java的類,檢視jstack代碼後會走到下面的方法裡

<code>private</code> <code>static</code> <code>void</code> <code>runthreaddump(string pid, string args[])</code><code>throws</code> <code>exception {</code>

<code></code><code>virtualmachine vm =</code><code>null</code><code>;</code>

<code></code><code>try</code> <code>{</code>

<code></code><code>vm = virtualmachine.attach(pid);</code>

<code></code><code>}</code><code>catch</code> <code>(exception x) {</code>

<code></code><code>string msg = x.getmessage();</code>

<code></code><code>if</code> <code>(msg !=</code><code>null</code><code>) {</code>

<code></code><code>system.err.println(pid + &amp;quot;: &amp;quot; + msg);</code>

<code></code><code>x.printstacktrace();</code>

<code></code><code>if</code> <code>((x</code><code>instanceof</code> <code>attachnotsupportedexception) &amp;amp;&amp;amp;</code>

<code></code><code>(loadsaclass() !=</code><code>null</code><code>)) {</code>

<code></code><code>system.err.println(&amp;quot;the -f option can be used when the target &amp;quot; +</code>

<code></code><code>&amp;quot;process is not responding&amp;quot;);</code>

<code></code><code>system.exit(</code><code>1</code><code>);</code>

<code></code><code>// cast to hotspotvirtualmachine as this is implementation specific</code>

<code></code><code>// method.</code>

<code></code><code>inputstream in = ((hotspotvirtualmachine)vm).remotedatadump((object[])args);</code>

<code></code><code>// read to eof and just print output</code>

<code></code><code>byte</code> <code>b[] =</code><code>new</code> <code>byte</code><code>[</code><code>256</code><code>];</code>

<code></code><code>int</code> <code>n;</code>

<code></code><code>do</code> <code>{</code>

<code></code><code>n = in.read(b);</code>

<code></code><code>if</code> <code>(n &amp;gt;</code><code>0</code><code>) {</code>

<code></code><code>string s =</code><code>new</code> <code>string(b,</code><code>0</code><code>, n, &amp;quot;utf-</code><code>8</code><code>&amp;quot;);</code>

<code></code><code>system.out.print(s);</code>

<code></code><code>}</code><code>while</code> <code>(n &amp;gt;</code><code>0</code><code>);</code>

<code></code><code>in.close();</code>

<code></code><code>vm.detach();</code>

請注意virtualmachine.attach(pid);這行代碼,觸發attach pid的關鍵,如果是在linux下會走到下面的構造函數

<code>linuxvirtualmachine(attachprovider provider, string vmid)</code>

<code></code><code>throws</code> <code>attachnotsupportedexception, ioexception</code>

<code></code><code>{</code>

<code></code><code>super</code><code>(provider, vmid);</code>

<code></code><code>// this provider only understands pids</code>

<code></code><code>int</code> <code>pid;</code>

<code></code><code>pid = integer.parseint(vmid);</code>

<code></code><code>}</code><code>catch</code> <code>(numberformatexception x) {</code>

<code></code><code>throw</code> <code>new</code> <code>attachnotsupportedexception(&amp;quot;invalid process identifier&amp;quot;);</code>

<code></code><code>// find the socket file. if not found then we attempt to start the</code>

<code></code><code>// attach mechanism in the target vm by sending it a quit signal.</code>

<code></code><code>// then we attempt to find the socket file again.</code>

<code></code><code>path = findsocketfile(pid);</code>

<code></code><code>if</code> <code>(path ==</code><code>null</code><code>) {</code>

<code></code><code>file f = createattachfile(pid);</code>

<code></code><code>// on linuxthreads each thread is a process and we don't have the</code>

<code></code><code>// pid of the vmthread which has sigquit unblocked. to workaround</code>

<code></code><code>// this we get the pid of the &amp;quot;manager thread&amp;quot; that is created</code>

<code></code><code>// by the first call to pthread_create. this is parent of all</code>

<code></code><code>// threads (except the initial thread).</code>

<code></code><code>if</code> <code>(islinuxthreads) {</code>

<code></code><code>int</code> <code>mpid;</code>

<code></code><code>mpid = getlinuxthreadsmanager(pid);</code>

<code></code><code>}</code><code>catch</code> <code>(ioexception x) {</code>

<code></code><code>throw</code> <code>new</code> <code>attachnotsupportedexception(x.getmessage());</code>

<code></code><code>assert</code><code>(mpid &amp;gt;=</code><code>1</code><code>);</code>

<code></code><code>sendquittochildrenof(mpid);</code>

<code></code><code>sendquitto(pid);</code>

<code></code><code>// give the target vm time to start the attach mechanism</code>

<code></code><code>int</code> <code>i =</code><code>0</code><code>;</code>

<code></code><code>long</code> <code>delay =</code><code>200</code><code>;</code>

<code></code><code>int</code> <code>retries = (</code><code>int</code><code>)(attachtimeout() / delay);</code>

<code></code><code>thread.sleep(delay);</code>

<code></code><code>}</code><code>catch</code> <code>(interruptedexception x) { }</code>

<code>48</code>

<code></code><code>i++;</code>

<code>49</code>

<code></code><code>}</code><code>while</code> <code>(i &amp;lt;= retries &amp;amp;&amp;amp; path ==</code><code>null</code><code>);</code>

<code>50</code>

<code>51</code>

<code></code><code>throw</code> <code>new</code> <code>attachnotsupportedexception(</code>

<code>52</code>

<code></code><code>&amp;quot;unable to open socket file: target process not responding &amp;quot; +</code>

<code>53</code>

<code></code><code>&amp;quot;or hotspot vm not loaded&amp;quot;);</code>

<code>54</code>

<code>55</code>

<code></code><code>}</code><code>finally</code> <code>{</code>

<code>56</code>

<code></code><code>f.delete();</code>

<code>57</code>

<code>58</code>

<code>59</code>

<code>60</code>

<code></code><code>// check that the file owner/permission to avoid attaching to</code>

<code>61</code>

<code></code><code>// bogus process</code>

<code>62</code>

<code></code><code>checkpermissions(path);</code>

<code>63</code>

<code>64</code>

<code></code><code>// check that we can connect to the process</code>

<code>65</code>

<code></code><code>// - this ensures we throw the permission denied error now rather than</code>

<code>66</code>

<code></code><code>// later when we attempt to enqueue a command.</code>

<code>67</code>

<code></code><code>int</code> <code>s = socket();</code>

<code>68</code>

<code>69</code>

<code></code><code>connect(s, path);</code>

<code>70</code>

<code>71</code>

<code></code><code>close(s);</code>

<code>72</code>

<code>73</code>

這裡要解釋下代碼了,首先看到調用了createattachfile方法在目标程序的cwd目錄下建立了一個檔案/proc/&lt;pid&gt;/cwd/.attach_pid&lt;pid&gt;,這個在後面的信号處理過程中會取出來做判斷(為了安全),另外我們知道在linux下線程是用程序實作的,在jvm啟動過程中會建立很多線程,比如我們上面的信号線程,也就是會看到很多的pid(應該是lwp),那麼如何找到這個信号處理線程呢,從上面實作來看是找到我們傳進去的pid的父程序,然後給它的所有子程序都發送一個sigquit信号,而jvm裡除了vm thread,其他線程都設定了對此信号的屏蔽,是以收不到該信号,于是該信号就傳給了“signal dispatcher”,在傳完之後作輪詢等待看目标程序是否建立了某個檔案,attachtimeout預設逾時時間是5000ms,可通過設定系統變量sun.tools.attach.attachtimeout來指定,下面是signal dispatcher線程的entry實作

<code>static</code> <code>void</code> <code>signal_thread_entry(javathread* thread, traps) {</code>

<code></code><code>os::set_priority(thread, nearmaxpriority);</code>

<code></code><code>while</code> <code>(</code><code>true</code><code>) {</code>

<code></code><code>int</code> <code>sig;</code>

<code></code><code>// fixme : currently we have not decieded what should be the status</code>

<code></code><code>// for this java thread blocked here. once we decide about</code>

<code></code><code>// that we should fix this.</code>

<code></code><code>sig = os::signal_wait();</code>

<code></code><code>if</code> <code>(sig == os::sigexitnum_pd()) {</code>

<code></code><code>// terminate the signal thread</code>

<code></code><code>return</code><code>;</code>

<code></code><code>switch</code> <code>(sig) {</code>

<code></code><code>case</code> <code>sigbreak: {</code>

<code></code><code>// check if the signal is a trigger to start the attach listener - in that</code>

<code></code><code>// case don't print stack traces.</code>

<code></code><code>if</code> <code>(!disableattachmechanism &amp;amp;&amp;amp; attachlistener::is_init_trigger()) {</code>

<code></code><code>continue</code><code>;</code>

<code></code><code>// print stack traces</code>

<code></code><code>// any sigbreak operations added here should make sure to flush</code>

<code></code><code>// the output stream (e.g. tty-&amp;gt;flush()) after output. see 4803766.</code>

<code></code><code>// each module also prints an extra carriage return after its output.</code>

<code></code><code>vm_printthreads op;</code>

<code></code><code>vmthread::execute(&amp;amp;op);</code>

<code></code><code>vm_printjni jni_op;</code>

<code></code><code>vmthread::execute(&amp;amp;jni_op);</code>

<code></code><code>vm_finddeadlocks op1(tty);</code>

<code></code><code>vmthread::execute(&amp;amp;op1);</code>

<code></code><code>universe::print_heap_at_sigbreak();</code>

<code></code><code>if</code> <code>(printclasshistogram) {</code>

<code></code><code>vm_gc_heapinspection op1(gclog_or_tty,</code><code>true</code> <code>/* force full gc before heap inspection */,</code>

<code></code><code>true /* need_prologue */</code><code>);</code>

<code></code><code>if</code> <code>(jvmtiexport::should_post_data_dump()) {</code>

<code></code><code>jvmtiexport::post_data_dump();</code>

<code></code><code>break</code><code>;</code>

<code></code><code>&amp;hellip;.</code>

當信号是sigbreak(在jvm裡做了#define,其實就是sigquit)的時候,就會觸發attachlistener::is_init_trigger()的執行

<code>bool attachlistener::is_init_trigger() {</code>

<code></code><code>if</code> <code>(init_at_startup() || is_initialized()) {</code>

<code></code><code>return</code> <code>false</code><code>;</code><code>// initialized at startup or already initialized</code>

<code></code><code>char</code> <code>fn[path_max+</code><code>1</code><code>];</code>

<code></code><code>sprintf(fn, &amp;quot;.attach_pid%d&amp;quot;, os::current_process_id());</code>

<code></code><code>int</code> <code>ret;</code>

<code></code><code>struct stat64 st;</code>

<code></code><code>restartable(::stat64(fn, &amp;amp;st), ret);</code>

<code></code><code>if</code> <code>(ret == -</code><code>1</code><code>) {</code>

<code></code><code>snprintf(fn, sizeof(fn), &amp;quot;%s/.attach_pid%d&amp;quot;,</code>

<code></code><code>os::get_temp_directory(), os::current_process_id());</code>

<code></code><code>if</code> <code>(ret ==</code><code>0</code><code>) {</code>

<code></code><code>// simple check to avoid starting the attach mechanism when</code>

<code></code><code>// a bogus user creates the file</code>

<code></code><code>if</code> <code>(st.st_uid == geteuid()) {</code>

<code></code><code>init();</code>

一開始會判斷目前程序目錄下是否有個.attach_pid&lt;pid&gt;檔案(前面提到了),如果沒有就會在/tmp下建立一個/tmp/.attach_pid&lt;pid&gt;,當那個檔案的uid和自己的uid是一緻的情況下(為了安全)再調用init方法

此時水落石出了,看到建立了一個線程,并且取名為attach listener。再看看其子類linuxattachlistener的init方法

<code>int</code> <code>linuxattachlistener::init() {</code>

<code></code><code>char</code> <code>path[unix_path_max];</code><code>// socket file</code>

<code></code><code>char</code> <code>initial_path[unix_path_max];</code><code>// socket file during setup</code>

<code></code><code>int</code> <code>listener;</code><code>// listener socket (file descriptor)</code>

<code></code><code>// register function to cleanup</code>

<code></code><code>::atexit(listener_cleanup);</code>

<code></code><code>int</code> <code>n = snprintf(path, unix_path_max, &amp;quot;%s/.java_pid%d&amp;quot;,</code>

<code></code><code>if</code> <code>(n &amp;lt; (</code><code>int</code><code>)unix_path_max) {</code>

<code></code><code>n = snprintf(initial_path, unix_path_max, &amp;quot;%s.tmp&amp;quot;, path);</code>

<code></code><code>if</code> <code>(n &amp;gt;= (</code><code>int</code><code>)unix_path_max) {</code>

<code></code><code>return</code> <code>-</code><code>1</code><code>;</code>

<code></code><code>// create the listener socket</code>

<code></code><code>listener = ::socket(pf_unix, sock_stream,</code><code>0</code><code>);</code>

<code></code><code>if</code> <code>(listener == -</code><code>1</code><code>) {</code>

<code></code><code>// bind socket</code>

<code></code><code>struct sockaddr_un addr;</code>

<code></code><code>addr.sun_family = af_unix;</code>

<code></code><code>strcpy(addr.sun_path, initial_path);</code>

<code></code><code>::unlink(initial_path);</code>

<code></code><code>int</code> <code>res = ::bind(listener, (struct sockaddr*)&amp;amp;addr, sizeof(addr));</code>

<code></code><code>if</code> <code>(res == -</code><code>1</code><code>) {</code>

<code></code><code>restartable(::close(listener), res);</code>

<code></code><code>// put in listen mode, set permissions, and rename into place</code>

<code></code><code>res = ::listen(listener,</code><code>5</code><code>);</code>

<code></code><code>if</code> <code>(res ==</code><code>0</code><code>) {</code>

<code></code><code>restartable(::chmod(initial_path, s_iread|s_iwrite), res);</code>

<code></code><code>res = ::rename(initial_path, path);</code>

<code></code><code>set_path(path);</code>

<code></code><code>set_listener(listener);</code>

<code></code><code>return</code> <code>0</code><code>;</code>

看到其建立了一個監聽套接字,并建立了一個檔案/tmp/.java_pid&lt;pid&gt;,這個檔案就是用戶端之前一直在輪詢等待的檔案,随着這個檔案的生成,意味着attach的過程圓滿結束了。

attach listener接收請求

看看它的entry實作attach_listener_thread_entry

<code>static</code> <code>void</code> <code>attach_listener_thread_entry(javathread* thread, traps) {</code>

<code></code><code>thread-&amp;gt;record_stack_base_and_size();</code>

<code></code><code>if</code> <code>(attachlistener::pd_init() !=</code><code>0</code><code>) {</code>

<code></code><code>attachlistener::set_initialized();</code>

<code></code><code>for</code> <code>(;;) {</code>

<code></code><code>attachoperation* op = attachlistener::dequeue();</code>

<code></code><code>if</code> <code>(op == null) {</code>

<code></code><code>return</code><code>;</code><code>// dequeue failed or shutdown</code>

<code></code><code>resourcemark rm;</code>

<code></code><code>bufferedstream st;</code>

<code></code><code>jint res = jni_ok;</code>

<code></code><code>// handle special detachall operation</code>

<code></code><code>if</code> <code>(strcmp(op-&amp;gt;name(), attachoperation::detachall_operation_name()) ==</code><code>0</code><code>) {</code>

<code></code><code>attachlistener::detachall();</code>

<code></code><code>// find the function to dispatch too</code>

<code></code><code>attachoperationfunctioninfo* info = null;</code>

<code></code><code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; funcs[i].name != null; i++) {</code>

<code></code><code>const</code> <code>char</code><code>* name = funcs[i].name;</code>

<code></code><code>assert</code><code>(strlen(name) &amp;lt;= attachoperation::name_length_max, &amp;quot;operation &amp;lt;= name_length_max&amp;quot;);</code>

<code></code><code>if</code> <code>(strcmp(op-&amp;gt;name(), name) ==</code><code>0</code><code>) {</code>

<code></code><code>info = &amp;amp;(funcs[i]);</code>

<code></code><code>// check for platform dependent attach operation</code>

<code></code><code>if</code> <code>(info == null) {</code>

<code></code><code>info = attachlistener::pd_find_operation(op-&amp;gt;name());</code>

<code></code><code>if</code> <code>(info != null) {</code>

<code></code><code>// dispatch to the function that implements this operation</code>

<code></code><code>res = (info-&amp;gt;func)(op, &amp;amp;st);</code>

<code></code><code>st.print(&amp;quot;operation %s not recognized!&amp;quot;, op-&amp;gt;name());</code>

<code></code><code>res = jni_err;</code>

<code></code><code>// operation complete - send result and output to client</code>

<code></code><code>op-&amp;gt;complete(res, &amp;amp;st);</code>

從代碼來看就是從隊列裡不斷取attachoperation,然後找到請求指令對應的方法進行執行,比如我們一開始說的jstack指令,找到 { “threaddump”, thread_dump }的映射關系,然後執行thread_dump方法 再來看看其要調用的attachlistener::dequeue()

<code>attachoperation* attachlistener::dequeue() {</code>

<code></code><code>javathread* thread = javathread::current();</code>

<code></code><code>threadblockinvm tbivm(thread);</code>

<code></code><code>thread-&amp;gt;set_suspend_equivalent();</code>

<code></code><code>// cleared by handle_special_suspend_equivalent_condition() or</code>

<code></code><code>// java_suspend_self() via check_and_wait_while_suspended()</code>

<code></code><code>attachoperation* op = linuxattachlistener::dequeue();</code>

<code></code><code>// were we externally suspended while we were waiting?</code>

<code></code><code>thread-&amp;gt;check_and_wait_while_suspended();</code>

<code></code><code>return</code> <code>op;</code>

最終調用的是linuxattachlistener::dequeue()

<a href="http://ifeve.com/jvm-attach/#viewsource">檢視源代碼</a>

<code>linuxattachoperation* linuxattachlistener::dequeue() {</code>

<code></code><code>int</code> <code>s;</code>

<code></code><code>// wait for client to connect</code>

<code></code><code>struct sockaddr addr;</code>

<code></code><code>socklen_t len = sizeof(addr);</code>

<code></code><code>restartable(::accept(listener(), &amp;amp;addr, &amp;amp;len), s);</code>

<code></code><code>if</code> <code>(s == -</code><code>1</code><code>) {</code>

<code></code><code>return</code> <code>null;</code><code>// log a warning?</code>

<code></code><code>// get the credentials of the peer and check the effective uid/guid</code>

<code></code><code>// - check with jeff on this.</code>

<code></code><code>struct ucred cred_info;</code>

<code></code><code>socklen_t optlen = sizeof(cred_info);</code>

<code></code><code>if</code> <code>(::getsockopt(s, sol_socket, so_peercred, (</code><code>void</code><code>*)&amp;amp;cred_info, &amp;amp;optlen) == -</code><code>1</code><code>) {</code>

<code></code><code>int</code> <code>res;</code>

<code></code><code>restartable(::close(s), res);</code>

<code></code><code>uid_t euid = geteuid();</code>

<code></code><code>gid_t egid = getegid();</code>

<code></code><code>if</code> <code>(cred_info.uid != euid || cred_info.gid != egid) {</code>

<code></code><code>// peer credential look okay so we read the request</code>

<code></code><code>linuxattachoperation* op = read_request(s);</code>

我們看到如果沒有請求的話,會一直accept在那裡,當來了請求,然後就會建立一個套接字,并讀取資料,建構出linuxattachoperation傳回并執行。

整個過程就這樣了,從attach線程建立到接收請求,處理請求,希望對大家有幫助。