Java线程在操作系统层面的一些'蛛丝马迹'

Netty历险记

共 7351字,需浏览 15分钟

 ·

2021-05-19 15:16

作为Java开发人员,在日常的开发工作中,无时无刻不在和线程打交道.本篇文章并不是讲解线程的相关知识.而是在Linux平台使用一些命令工具,观察下Java线程在操作系统层面的一些'蛛丝马迹'.


实验平台




Win10平台上安装的Ubuntu 18.04 LTS




首先从C语言层面,观察下会有几个线程或进程.代码如下


// thread.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>
int main(int argc, char *argv[]){
// 为了在使用命令观察程序的时候不让它退出,此处让程序睡眠 sleep(1000);
return 0;}



编译并运行



接下来使用命令查看


ps -ef


进程号=367

再查看下这个进程有几个线程


ps -Lf 367



进程ID等于线程ID, 可以理解为进程里面有且只有一个线程, 这个线程就是进程本身.


接下来,在上面代码的基础上,手动创建一个线程,再观察下会有几个线程.


#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <sys/syscall.h>
// 子线程执行的任务void *task(void *arg){ // 线程ID pid_t tid = syscall(SYS_gettid); printf("child thread, thread ID = %d\n", tid);
sleep(1000);
}
int main(int argc, char *argv[]){
// 打印主线程ID pid_t tid = syscall(SYS_gettid); printf("main thread, thread ID = %d\n", tid);
pthread_t thread; // 创建子线程 if(pthread_create(&thread, NULL, task, NULL) != 0) { printf("thread creation failed\n"); exit(1); }
sleep(1000);
return 0;}


编译并执行


打印出来的主线程ID=553, 子线程ID=554
查看进程ID


进程ID=553

查看线程


ps -Lf 553


LWP(light weight process)轻量级进程的意思,即线程.


有两个线程,553号线程就是主线程(进程本身),554号线程就是553号线程创建出来的子线程.

接下来使用查看系统调用的方式,验证下线程是如何被创建出来的.


strace -ff -o res.txt ./a.out


打印了两个文件


搜索

grep clone *

线程594使用clone系统调用创建了线程595.


以上只是从C语言角度查看了下线程的相关信息.


那么通过以下Java代码查看下线程的相关信息.


public class ThreadTest {    private static class MyThread implements Runnable {        public void run() {            try {              System.out.println("child thread");                Thread.sleep(20000);            } catch (Exception x) {}        }    }

public static void main(String[] args) throws Exception {
Thread thread = new Thread(new MyThread(), "my_thread"); thread.start(); System.out.println("main thread"); Thread.sleep(20000); }}

编译之后,通过


strace -ff -o xxx.txt java ThreadTest

命令查看系统调用


生成如下文件(19个文件)





每个文件对应一个线程.同样是一个简单的程序,Java代码创建的线程的确比上面的C语言创建的线程多.毕竟Java会有一些垃圾回收的线程.


查看进程


进程ID=3224


查看线程


共19个线程,与生成的19个文件一一对应.


执行

git clone *

打印的内容如下


xxx.txt.3224:clone(child_stack=0x7f448e75ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f448e7609d0, tls=0x7f448e760700, child_tidptr=0x7f448e7609d0) = 3225xxx.txt.3225:clone(child_stack=0x7f448c12ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f448c1309d0, tls=0x7f448c130700, child_tidptr=0x7f448c1309d0) = 3226xxx.txt.3225:clone(child_stack=0x7f4483ebffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4483ec09d0, tls=0x7f4483ec0700, child_tidptr=0x7f4483ec09d0) = 3227xxx.txt.3225:clone(child_stack=0x7f4483daffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4483db09d0, tls=0x7f4483db0700, child_tidptr=0x7f4483db09d0) = 3228xxx.txt.3225:clone(child_stack=0x7f4483c9ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4483ca09d0, tls=0x7f4483ca0700, child_tidptr=0x7f4483ca09d0) = 3229xxx.txt.3225:clone(child_stack=0x7f4483b8ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4483b909d0, tls=0x7f4483b90700, child_tidptr=0x7f4483b909d0) = 3230xxx.txt.3225:clone(child_stack=0x7f4483a7ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4483a809d0, tls=0x7f4483a80700, child_tidptr=0x7f4483a809d0) = 3231xxx.txt.3225:clone(child_stack=0x7f448396ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44839709d0, tls=0x7f4483970700, child_tidptr=0x7f44839709d0) = 3232xxx.txt.3225:clone(child_stack=0x7f448385ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44838609d0, tls=0x7f4483860700, child_tidptr=0x7f44838609d0) = 3233xxx.txt.3225:clone(child_stack=0x7f44812affb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44812b09d0, tls=0x7f44812b0700, child_tidptr=0x7f44812b09d0) = 3234xxx.txt.3225:clone(child_stack=0x7f448119ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44811a09d0, tls=0x7f44811a0700, child_tidptr=0x7f44811a09d0) = 3235xxx.txt.3225:clone(child_stack=0x7f448108ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44810909d0, tls=0x7f4481090700, child_tidptr=0x7f44810909d0) = 3236xxx.txt.3225:clone(child_stack=0x7f4480c4ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4480c509d0, tls=0x7f4480c50700, child_tidptr=0x7f4480c509d0) = 3237xxx.txt.3225:clone(child_stack=0x7f4480b3ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4480b409d0, tls=0x7f4480b40700, child_tidptr=0x7f4480b409d0) = 3238xxx.txt.3225:clone(child_stack=0x7f4480a2ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f4480a309d0, tls=0x7f4480a30700, child_tidptr=0x7f4480a309d0) = 3239xxx.txt.3225:clone(child_stack=0x7f448091ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44809209d0, tls=0x7f4480920700, child_tidptr=0x7f44809209d0) = 3240xxx.txt.3225:clone(child_stack=0x7f448080ffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44808109d0, tls=0x7f4480810700, child_tidptr=0x7f44808109d0) = 3241xxx.txt.3225:clone(child_stack=0x7f44806fffb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f44807009d0, tls=0x7f4480700700, child_tidptr=0x7f44807009d0) = 3242



线程3224(即进程本身)调用clone系统调用创建出来一个3225这个主线程.主线程3225又把其他17个线程调用clone系统调用也创建出来了.


为什么说3225就是主线程呢?我们在代码中打印了

System.out.println("main thread");

这么一句话, 那么我们就搜索下这么一句话,看它在哪个文件中打印的.


它是在xxx.txt.3225这个文件中打印的, 而文件名中的3225就是线程ID,这样就可以证明3225就是主线程.因为我们是在主线程打印的’main thread’.




结论


  1. JVM进程可以当成一个线程,main线程是JVM创建的第一个线程, 但main线程是第二个线程.

  2. 会使用strace查看系统调用

  3. 创建线程使用的系统调用是哪个?  clone

  4. 会使用ps命令查看进程的线程



浏览 17
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报