Linux查看进程下运行线程信息
2013-10-04 14:48:21 阿炯

本文将介绍一些如何在Linux下查看应用程序多线程的方法,从各个方面进行入手并总结。经典的top工具可使用H快捷键或-H命令行来显示线程信息,使用htop工具指令还可以带以彩色显示和默认就开启线程显示(在其界面上按下F5可查看具体的线程信息),ps命令还有一些指令可显示线程如-L。对于每个进程,它有大量的信息在/proc/目录下的以进程ID为目录名下,/proc/<进程ID>/task/6789,其中6789为该进程在内核中的线程ID。这些信息可供ps、top等工具来获取。

Threads vs Processes

A thread is very similar to a process, it has an identifier (TID, or thread ID), and the kernel schedules and runs threads just like processes.
线程与进程非常相似,它有一个标识符(TID,或线程ID),内核会像进程一样调度和运行线程。

However, unlike separate processes, which usually do not share system resources such as memory and I/O connections with other processes, all threads inside a single process share their system resources and some memory.
但是,与通常不与其它进程共享系统资源(如内存和I/O连接)的独立进程不同,单个进程中的所有线程共享其系统资源和一些内存。

A process with one thread is single-threaded, and a process with more than one thread is multithreaded.
有一个线程的进程是单线程的,有多个线程的进程是多线程的。

All processes start out single-threaded. This starting thread is usually called the main thread. The main thread may then start new threads in order for the process to become multithreaded, similar to the way a process can call fork() to start a new process.
所有进程从单线程开始。这个起始线程通常称为主线程。然后,主线程可以启动新线程,以使进程成为多线程的,类似于进程调用fork()来启动新进程的方式。

The primary advantage of a multithreaded process is that when the process has a lot to do, threads can run simultaneously on multiple processors, potentially speeding up computation.
支持多线程进程的主要优点是,当进程有很多事情要做时,线程可以在多个处理器上同时运行,这可能会加快计算速度。

Although you can also achieve simultaneous computation with multiple processes, threads start faster than processes, and it is often easier and/or more efficient for threads to intercommunicate using their shared memory than it is for processes to communicate over a channel such as a network connection or a pipe.
尽管可以使用多个进程实现同步计算,但线程的启动速度比进程快,而且线程使用其共享内存进行内部通信通常比进程通过网络连接或管道等通道进行通信更容易和/或更高效。

Linux下取得本机进、线程的数量方法大盘点

方法一:ps指令

之前有两种方法:
ps -ejH
ps axjf

前者显示的有些乱,所以采用第二种方式查看。如何显示线程关系:
ps -eLf
ps axms

推荐使用第二种方法,原因同上。'ps xH' 手册:H:Show threads as if they were processes,这样可以查看所有存在的线程;'ps -mp <PID>' 手册:m:Show threads after processes,这样可以查看一个进程所起的线程数。

有两个状态项需要说明:
LWP -- 轻量级进程,即线程,这里显示的是线程id。
NLWP -- 线程数量。

ps -T -p <pid>
ps -e -T | grep <app name or pid>

-e:显示所有的进程
-T:列出所有的线程

查看所有的进、线程:ps -eLf

With "ps" we can list LWP (Light Weight process) which depicts Thread ID of the respective process and NWLP (Number of Threads).

要使用ps显示每个进程的线程,可以使用以下参数:
-L:Show threads, possibly with LWP and NLWP columns.
-e:Select all processes
-f:Do full-format listing

查看某进程的线程信息:ps -T pid

ps huH  -p  pid | wc  -l
ps huH p <PID> | wc -l

将会显示出指定的进程id下运行线程的信息(数量),获取指定进程ID(pid)下线程的数量信息:ps -o nlwp <pid>

Where nlwp stands for Number of Light Weight Processes (threads). Thus ps aliases nlwp to thcount, which means that: ps -o thcount <pid>

如果想盯着一段时间的线程数量情况,可以使用watch指令:watch ps -o thcount <pid>

获取系统中所有的在运行的线程数量:ps -eo nlwp | tail -n +2 | awk '{ num_threads += $1 } END { print num_threads }'

ps uH p <PID_OF_PROCESS> | wc -l

获取指定进程的线程数量:
ps -eL -q pid | wc -l
ps -L -o pid= -p <pid> | wc -l
ps H p pid-id

H - Lists all the individual threads in a process

得到Linux系统中每个用户的线程数,可使用:
ps -eLf | grep <USER> | awk '{ num += $6 } END { print num }'


方法二:pstree指令

这是系统内置的专为查看进线程间的关系树的工具,查看以上两个进程的树形关系。
pstree $PID
pstree -p 3711

下面可以看到java线程计数和检查java进程的线程数
# pstree -pau -l -G -s PID

方法三:从/proc目录中取得

可以使用/proc/<PID>/task/中的可用子目录列表对线程进行计数,这部分中可用子目录的总数与提供的PID的每个进程的线程数成正比。取得指定进程下的线程信息:cat /proc/<PROCESS_PID>/status | grep Threads

例如,为了检查某java进程下的线程数,可以看其下有多个子目录,所以这意味着这是一个有多线程的进程。在这个路径下使用ls命令可以为该java进程的线程数。

# ls /proc/$(pidof some_java_app)/task/
...
# ls /proc/$(pgrep FreeoaHelper)/task/

cat /proc/3680/status

其中Threads:5 表示该进程下包含5个线程(3680进程对应的线程+由其创建的工作线程)。

cat /proc/$(pgrep java)/status | grep -i Threads

方法四:top指令

在top指令的默认情况下,我们无法看到每个进程的线程数,可在运行top时更改要显示的字段,可以手动将此列添加到显示列中以展示每个进程的线程数。

在运行时的top中按'f',将显示top可以显示的字段列表,粗体显示的字段是top将显示的字段。使用上下键翻至"nTH"(Number of Threads),按下空格来选中它,再按's'可按线程数量来排序,之后按'q'返回top的主界面即可看到线程的数量了(nTH列)。

top -H -p <pid>
查看 id 为 3680 的进程包含的线程。

# top -Hp 3680

按f后再按j键:
* J: P = Last used cpu (SMP)

按回车或空格保存,退回到top界面后在top的显示中会多出P这一列,这是最近一次运行该线程(进程)的CPU。手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程,否则它一行显示一个进程。

方法五:综合使用

进程数量
ls /proc|grep '^[0-9]*$'|wc -l
or
ls /proc | grep ^[1-9] -c
or
ls -d /proc/[1-9]* | wc -l

ps -A --no-headers | wc -l
ps -e --no-headers | wc -l

线程数量
ps -AL --no-headers | wc -l

某进程下的线程情况
ps -o nlwp <pid>
ps -o thcount <pid>

cat /proc/<PROCESS_PID>/status | grep Threads

To get the thread count for the whole system, this suffices:
#!/bin/sh
counthread() {
    printf %s\\n "$#"
}
counthread /proc/[0-9]*/task/[0-9]*

查看某用户(组)下的进、线程数量
ps -U freeoa --no-headers | wc -l
ps -U www-data -u www-data | wc -l

查看某进程的情况
ps -C httpd --no-headers | wc -l
pgrep httpd | wc -l

系统资源与进、线程数量之迷


检查系统中允许的线程数量
# cat /proc/sys/kernel/threads-max
771889

threads-max 中所允许的最大值由常量:FUTEX_TID_MASK (0x3fffffff) 所决定,如果尝试设定超过threads-max的值将会得到EINVAL类的错误。

它的默认值受置于内存大小,可以增加每进程下的最大线程数量限制:The default value depends on memory size. You can use threads-max to check number of threads allowed in Linux, and can increase thread count per process limit like this:# echo 800000 > /proc/sys/kernel/threads-max

注意:写入的值根据可用的内存页进行检查。如果线程结构将占用过多(超过1/8)的可用内存页,则线程数将相应减少。

The minimum number of threads that can be written to threads-max is 20.
The maximum value that can be written to threads-max is given by the constant FUTEX_TID_MASK (0x3fffffff).
If a value outside of this range is written to threads-max an error EINVAL occurs.

对于单个用户可以创建的进程(因此是线程)的数量也有限制,有关这些限制的详细信息,请参阅ulimit:
# ulimit -a | grep -i processes
max user processes    (-u) 385944

在这里,系统总计可以创建771889个线程/进程,单个用户可以创建385944个进程。

这个逻辑非常简单,每个CPU一次可以执行1个进程,如果有8个内核,这意味着一次8到10个进程可以轻松地执行而不会有任何压力,但如果每个CPU的运行或可运行线程数量急剧增加,则会出现性能问题。

Linux系统下的最大进程数量可从proc的man手册查到:From the proc man page(http://man7.org/linux/man-pages/man5/proc.5.html)

/proc/sys/kernel/pid_max (since Linux 2.5.34)

This file specifies the value at which PIDs wrap around (i.e., the value in this file is one greater than the maximum PID). PIDs greater than this value are not allocated; thus, the value in this file also acts as a system-wide limit on the total number of processes and threads.  The default value for this file, 32768, results in the same range of PIDs as on earlier kernels.

确定 kernel.pid_max 的值
# sysctl -a | grep kernel.pid_max
kernel.pid_max = 32768

在此可在当前系统中同时执行32768个进程,这些进程可以在不同的内存空间中运行。

将该值修正为 65534
# echo kernel.pid_max = 65534 >> /etc/sysctl.conf
# sysctl -p

是不是没有看明白,没有关系,下面分开来讲。

系统所能运行的最大进程数量
the maximum number of processes|the maximum processes count allowed

/proc/sys/kernel/pid_max (since Linux 2.5.34)
此文件指定MAX_PID的值(即此文件中的值比最大PID大一个,不能分配大于此值的PID号),故该文件中的值还充当系统范围内进程和线程总数的限制。早期内核(32位)的PID默认值32768。

# cat /proc/sys/kernel/pid_max
32768

使用sysctl命令查看或设置:
# sysctl kernel.pid_max
# sysctl -a | grep kernel.pid_max
32768

通常,4194303是x86_64的最大限制,32767是x86的最大限制。如果要更改最大数量,可以在/etc/sysctl.conf写入:
kernel.pid_max = 4194303

# echo kernel.pid_max = 65534 >> /etc/sysctl.conf
# sysctl -p

每用户的进程数量限制
Process number limit per user

root这类特权用户是不会有限制的,使用'ulimit -a'可以查看普通用户在各娄资源使用上的限制(-u可得知用户可同时运行最大进程数量)。
max user process    (-u) unlimited

通常情况下,非root用户都有此限制,可以为特定用户指定相关的限制,包括这里所说的用户可同时运行的最大进程数量,在/etc/security/limits.conf中指定
user_freeoa - nproc 5000

系统所能运行的最大线程数量
Number of threads allowed in linux system

Linux没有对每个进程单独设置线程限制,只是对系统上(运行的)进程总数进行了限制。此值控制可以使用fork()创建的最大线程数。在主机初始化过程中内核将设置该值,即使创建了最大数量的线程。

检查Linux系统允许的线程数
# cat /proc/sys/kernel/threads-max
35000

The minimum number of threads that can be written to threads-max is 20.
最小线程数为20。

The maximum value that can be written to threads-max is given by the constant FUTEX_TID_MASK (0x3fffffff).
线程的最大值由常量FUTEX_TID_MASK(0x3fffff)给出。

If a value outside of this range is written to threads-max an error EINVAL occurs.
如果将超出此范围值则会发生EINVAL错误。

(同时运行)线程数的默认值取决于内存大小,可以使用threads-max检查Linux中允许的线程数。可以按如下方式增加每个进程的线程数限制:
#echo 100000 > /proc/sys/kernel/threads-max

重要提示:
根据可用内存页检查相应值。如果线程结构将占用太多(超过1/8)的可用内存页,则相应地减少threads-max值。

单个用户可以创建的进程(线程)数量也有限制,有关这些限制的详细信息,请参阅ulimit:
# ulimit -a | grep -i processes
max user processes    (-u) 10000

在这里,系统总共可以创建35000个线程/进程,单个用户可以创建10000个进程。其中的逻辑非常简单,每个CPU一次可以执行一个进程,如果有8个内核,这意味着一次可以轻松执行8到10个进程,而没有任何压力;但是如果每个CPU的运行线程数或可运行线程数急剧增加,系统则会出现性能问题。
 
上例中wc命令的常用用法
wc选项     描述
-c     Print the byte counts
-m     Print the character counts
-l     Print the newline counts
-w     print the word counts
--help     Display the wc command help and exit

上例中ps命令的常用用法
ps选项     描述
-e     Select all processes (GNU/Linux syntax)
aux     Select all processes using BSD syntax
-U user     Select by real user ID (RUID) or name
-u user     Select by effective user ID (EUID) or name
-C cmdlist     Select by command name. This selects the processes whose executable name is given in cmdlist
--no-headers     Print no header line at all. --no-heading is an alias for this option
-L    Show threads, possibly with LWP and NLWP columns.
-e    Select all processes
-f    Do full-format listing

每进程所能运行的最大线程数量
Maximum Number of Threads Per Process in Linux

Linux对每个进程的最大线程数进行了设置,它指定了进程可以处理的最大并发执行数。对此所做的更改可以限制进程,并将发生的执行延迟降至最低。达到这个极限意味着进程在峰值负载时需要那么多线程。但只要它能够及时地处理请求,这个过程就会得到充分的调整。然而,当超过该限制时,线程会排队,从而可能导致进程过载。而进程将延迟创建新线程,直到活动线程的数量低于限制。

内核参数threads max控制线程的最大数量:/proc/sys/kernel/threads-max,当然这其中的值可能通过sysctl指令来调整。还有其它参数的限制。

kernel.pid_max和vm.max_map_count指定了另外两个限制,这两个限制也将在峰值负载时阻止新线程的创建:
1.pid_max参数指定pid环绕的值。

2.max_map_count参数指定进程可以拥有的虚拟内存区域(VMA)的最大数量,是一个进程可能具有的最大内存映射区域数。

Linux内核以相同的方式处理进程和线程,因此限制进程数量的值也会间接限制线程数量,kernel.pid_max必须大于同时执行的线程和进程的总数。拥有多个线程可能会消耗服务器工作所需的太多内存。vm.max_map_count限制了虚拟内存和需要这些内存来设置自己的私有堆栈的线程数。

在systemd系统上,cgroup pids.max参数强制执行另一个限制。默认设置为12288。在某些情况下,这种默认资源限制不够,或者限制的太多。或者对系统的某些TasksMax设置执行特定调整可能会很有用。/etc/systemd/logind.conf的[Login]部分中的UserTasksMax参数覆盖默认限制:
$grep-i '^UserTasksMax' /etc/systemd/logind.conf
UserTasksMax=50000

这正是systemd对从登录shell运行的程序应用线程限制的方式。

如何设置最大线程数

有几种方法可以设置每个进程的最大线程数。这些值决定了一个进程允许的线程数。在运行时临时设置threads max内核参数:
echo 120000 > /proc/sys/kernel/threads-max

#在/etc/sysctl.conf中设定后可保存
sysctl -w kernel.threads-max=120000 >> /etc/sysctl.conf

接下来,将pid_max参数设置为200000,意味着内核最多可以同时执行200000个进程:
echo 200000>/proc/sys/kernel/pid_max

类似地,将max_map_count参数设置为600000,意味着一个进程可以拥有最多600000个虚拟内存区域(VMA):
echo 600000>/proc/sys/vm/max_map_count

在systemd系统上,UserTasksMax为所有用户(All Users)指定TasksMax设置,并确定线程限制:
grep -i "UserTasksMax" /etc/systemd/logind.conf
#UserTasksMax=50000
UserTasksMax=60000

系统级设定(Global)
/etc/systemd/system.conf
DefaultTasksMax=Value

为指定的服务设定(Unit/Service)
For specific freeoa service
/etc/systemd/system/freeoa.service.d/override.conf
TasksMax=Value

为指定的用户设定
Individual Users:
For example, if the target user's UID is 1001, create:
/etc/systemd/system/user-1001.slice.d/50-tasksmax.conf

[Slice]
TasksMax=18000

Upon the next login by that user, the value should come into effect.  You could also give the following command to change the user's current value on-the-fly:
systemctl set-property user-1001.slice TasksMax=18000

Show system level DefaultTasksMax settings:
systemctl show --property DefaultTasksMax

影响最大线程数的因素

尽管有一些系统参数对每个进程的线程数设置了限制,但在这之前,操作系统和内存很可能成为限制因素。进程可以拥有的线程数限制使用以下公式计算:
number of threads = total virtual memory / (stack size*1024*1024)
线程数=总虚拟内存/(堆栈大小*1024*1024)

因此,可以通过增加总虚拟内存来增加每个进程的线程数。每个线程的堆栈大小很可能是限制,减少每线程堆栈大小也是增加线程总数的一种方法。可以使用ulimit检查每个线程的堆栈大小:
ulimit -a | grep "stack size"
stack size    (kbytes, -s) 10240

该值表示每个线程将获得分配给其堆栈的内存量(10MB)。对于32位程序和4GB的最大地址空间,最大线程数为:
4096MB/10MB=409

在64位处理器上,我们可以使用ulimit调整每个线程的堆栈大小:
ulimit -s 8192

kernel/fork.c

/* The default maximum number of threads is set to a safe
 * value: the thread structures can take up at most half of memory.
*/
max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);

max_threads = totalram_pages / (8 * 8192 / 4096);

x86_64 page size (PAGE_SIZE) is 4K;

Like all other architectures, x86_64 has a kernel stack for every active thread. These thread stacks are THREAD_SIZE (2*PAGE_SIZE) big;

线程查看使用示例

查看java进程中有哪些线程在消耗cpu资源

首先来找到有哪些java进程:jps -v

再来确定其下的线程使用cpu的情况:
在Unix或Linux下可以这样实现: top -n 1 -H -p [java_pid]
在Solaris上可以执行此命令:prstat -L -p [java_pid]


使用ps得到系统中的进程ID、父进程ID、线程数量、命令行并按序输出
ps axo pid,ppid,nlwp,cmd | sort -nr | head -10

ps ax  - see every process
o - format
nlwp: number of lightweight processes (threads) in the process

如果想查询指定的进程下的线程情况,可做如下调用:
ps p <pid> o pid,ppid,nlwp,cmd

查看系统中所有进程的状态统计:cat /proc/*/status

找出最多线程的应用
ps -eo nlwp,pid,args --sort nlwp
将显示按线程数排序的进程列表。

对于类似top的视图,可以执行以下操作:
watch -n 1 'ps -eo nlwp,pid,args --sort -nlwp | head'

ps -eLo pid,lwp,pcpu |grep bypid

取得运行的进程所在的工作目录
Linux Get Running Process Current Working Directory

使用system中的指令获取

ps -o cwd -p PID
lsof -a -d cwd -p PID
pwdx PID

从/proc文件系统目录中取得
cd /proc/PID
readlink cwd
Or
ls -l

realpath /proc/PID/exe

On Linux, the symlink /proc/<pid>/exe has the path of the executable. Use the command readlink -f /proc/<pid>/exe to get the value.