在Linux系统中,线程是一个基本的同步机制,因为它可以以并发方式执行代码,执行多个任务并减少对系统资源的消耗。为了更好地管理和监控Linux系统中的线程,需要知道如何读取线程的信息。本文将介绍,以及相关的工具和命令。
成都创新互联公司服务项目包括广平网站建设、广平网站制作、广平网页制作以及广平网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,广平网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到广平省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
一、线程的基本概念
线程是进程的一部分,它是一个可执行的单位,由内核来调度。线程共享进程的地址空间和系统资源,但是每个线程都有自己的堆栈、寄存器等。因此,线程的开销比进程小,线程的切换也比进程快。
二、读取线程的信息
在Linux系统中,有多种方法可以读取线程的信息。下面介绍几种较常用的方法。
1. ps命令
ps命令是Linux系统中常用的进程查看命令。使用“ps -ef”命令可以列出系统中所有运行的进程,包括线程。可以看到每个进程(线程)的PID、PPID、CPU占用率等信息。
但是,ps命令并不直接支持列出线程信息,需要使用“ps -eLf”或“ps -T -p pid”命令来获取线程信息。其中,之一条命令会列出系统中所有线程的信息;第二条命令会列出指定进程的所有线程信息。
2. top命令
top命令是Linux系统中常用的进程监控命令。使用“top -H”命令可以查看系统中所有进程的线程信息,包括每个线程的PID、CPU占用率、内存占用率等。
可以使用“Shift + H”命令将线程信息显示在顶部。使用“Shift + P”命令按CPU使用率排序,使用“Shift + M”命令按内存使用率排序。使用“q”命令退出top命令。
3. gdb命令
gdb命令可以用于调试进程和线程。使用“gdb -p pid”命令可以连接到指定进程,然后使用“info threads”命令可以列出所有线程的信息,包括线程ID和状态等。
此外,还可以使用“thread apply all bt”命令来打印所有线程的堆栈信息。
4. /proc文件系统
在Linux系统中,/proc文件系统提供了一个虚拟文件系统,可以用于获取系统信息。每个进程和线程都有一个/proc目录,其中包含了有关该进程或线程的信息。可以使用“ls -la /proc/pid/task”命令列出指定进程的所有线程信息。
然后可以进入每个线程的目录,查看线程的状态、CPU占用率、内存占用率等信息。例如,可以使用“cat /proc/pid/task/tid/status”命令查看指定线程的状态信息。
三、
在Linux系统中,读取线程的信息非常重要,可以帮助我们更好地管理和监控系统。本文介绍了几种常用的读取线程信息的方法,包括ps命令、top命令、gdb命令和/proc文件系统等。通过使用这些工具和命令,可以轻松地获取线程的PID、CPU占用率、内存占用率等信息,从而更好地管理和监控系统中的线程。
相关问题拓展阅读:
一:进程和线程
每个进程有自己独立的地址空间。“在同一个进程”还是“不在同一个进程”是系统功能划分的重要决策点。《Erlang程序设计》把进程比喻为人:
每个人有自己的记忆(内存),人与人通过谈话(消息传递)来交流,谈话既可以是面谈姿并野(同一台服务器),也可以在里谈(不同的服务器,有网络通信)。面谈和谈的区别在于,面谈可以立即知道对方是否死了(crash,SIGCHLD),而谈只能通过周期性的心跳来判断对方是否还活着。
有了这些比喻,设计分布式系统时可以采取“角色扮演”,团队里的几个人各自扮演一个进程,人的角色由进程的代码决定(管登录的、管消息分发的、管买卖的等等)。每个人有自己的记忆,但不知道别人的记忆,要想知道别人的看法,只能通过交谈(暂不考虑共享内存这种IPC)。然后就可以思考:迹喊
·容错:万一有人突然死了
·扩容:新人中途加进来
·负载均衡:把甲的活儿挪给乙做
·退休:甲要修复bug,先别派新任务,等他做完手上的事情就把他重启
等等各种场景,十分便利。
线程的特点是共享地址空间,从而可以高效地共享数据。一台机器上的多个进程能高效地共享代码段(操作系统可以映射为同样的物理内存),但不能共享数据。如果多个进程大量共享内存,等于是把多进程程序当成多线程来写,掩耳盗铃。
“多线程”的价值,我认为是为了更好地发挥多核处理器(multi-cores)的效能。在单核时代,多线程没有多大价值(个人想法:如果要完成的任务是CPU密集型的,那多线程没有优势,甚至因为线程切换的开销,多线程反而更慢;如果要完成的任务既有CPU计算,又有磁盘或网络IO,则使用多线程的好处是,当某个线程因为IO而阻塞时,OS可以调度其他线程执行,虽然效率确实要比任务的顺序执行效率要高,然而,这种类型的任务,可以通过单线程的”non-blocking IO+IO multiplexing”的模型(事件驱动)来提高效率,采用多线程的方式,带来的可能仅仅是编程上的简单而已)。Alan Cox说过:”A computer is a state machine.Threads are for people who can’t program state machines.”(计算机是一台状态机。线程是给那些不能编写状态机程序的人准备的)如果只有一块CPU、一个执行单元,那么确实如Alan Cox所说,按状态机的思路去写程序是最蔽谨高效的。
二:单线程服务器的常用编程模型
据我了解,在高性能的网络程序中,使用得最为广泛的恐怕要数”non-blocking IO + IO multiplexing”这种模型,即Reactor模式。
在”non-blocking IO + IO multiplexing”这种模型中,程序的基本结构是一个事件循环(event loop),以事件驱动(event-driven)和事件回调的方式实现业务逻辑:
view plain copy
//代码仅为示意,没有完整考虑各种情况
while(!done)
{
int timeout_ms = max(1000, getNextTimedCallback());
int retval = poll(fds, nfds, timeout_ms);
if (retval0){
处理IO事件,回调用户的IO event handler
}
}
}
这里select(2)/poll(2)有伸缩性方面的不足(描述符过多时,效率较低),Linux下可替换为epoll(4),其他操作系统也有对应的高性能替代品。
Reactor模型的优点很明显,编程不难,效率也不错。不仅可以用于读写socket,连接的建立(connect(2)/accept(2)),甚至DNS解析都可以用非阻塞方式进行,以提高并发度和吞吐量(throughput),对于IO密集的应用是个不错的选择。lighttpd就是这样,它内部的fdevent结构十分精妙,值得学习。
基于事件驱动的编程模型也有其本质的缺点,它要求事件回调函数必须是非阻塞的。对于涉及网络IO的请求响应式协议,它容易割裂业务逻辑,使其散布于多个回调函数之中,相对不容易理解和维护。
三:多线程服务器的常用编程模型
大概有这么几种:
a:每个请求创建一个线程,使用阻塞式IO操作。在Java 1.4引人NIO之前,这是Java网络编程的推荐做法。可惜伸缩性不佳(请求太多时,操作系统创建不了这许多线程)。
b:使用线程池,同样使用阻塞式IO操作。与第1种相比,这是提高性能的措施。
c:使用non-blocking IO + IO multiplexing。即Java NIO的方式。
d:Leader/Follower等高级模式。
在默认情况下,我会使用第3种,即non-blocking IO + one loop per thread模式来编写多线程C++网络服务程序。
1:one loop per thread
此种模型下,程序里的每个IO线程有一个event loop,用于处理读写和定时事件(无论周期性的还是单次的)。代码框架跟“单线程服务器的常用编程模型”一节中的一样。
libev的作者说:
One loop per thread is usually a good model. Doing this is almost never wrong, some times a better-performance model exists, but it is always a good start.
这种方式的好处是:
a:线程数目基本固定,可以在程序启动的时候设置,不会频繁创建与销毁。
b:可以很方便地在线程间调配负载。
c:IO事件发生的线程是固定的,同一个TCP连接不必考虑事件并发。
Event loop代表了线程的主循环,需要让哪个线程干活,就把timer或IO channel(如TCP连接)注册到哪个线程的loop里即可:对实时性有要求的connection可以单独用一个线程;数据量大的connection可以独占一个线程,并把数据处理任务分摊到另几个计算线程中(用线程池);其他次要的辅助性connections可以共享一个线程。
比如,在dbproxy中,一个线程用于专门处理客户端发来的管理命令;一个线程用于处理客户端发来的MySQL命令,而与后端数据库通信执行该命令时,是将该任务分配给所有事件线程处理的。
对于non-trivial(有一定规模)的服务端程序,一般会采用non-blocking IO + IO multiplexing,每个connection/acceptor都会注册到某个event loop上,程序里有多个event loop,每个线程至多有一个event loop。
多线程程序对event loop提出了更高的要求,那就是“线程安全”。要允许一个线程往别的线程的loop里塞东西,这个loop必须得是线程安全的。
在dbproxy中,线程向其他线程分发任务,是通过管道和队列实现的。比如主线程accept到连接后,将表示该连接的结构放入队列,并向管道中写入一个字节。计算线程在自己的event loop中注册管道的读事件,一旦有数据可读,就尝试从队列中取任务。
2:线程池
不过,对于没有IO而光有计算任务的线程,使用event loop有点浪费。可以使用一种补充方案,即用blocking queue实现的任务队列:
view plain copy
typedef boost::functionFunctor;
BlockingQueue taskQueue; //线程安全的全局阻塞队列
//计算线程
void workerThread()
{
while (running) //running变量是个全局标志
{
Functor task = taskQueue.take(); //this blocks
task(); //在产品代码中需要考虑异常处理
}
}
// 创建容量(并发数)为N的线程池
int N = num_of_computing_threads;
for (int i = 0; i task = boost::bind(&Foo::calc,&foo);
taskQueue.post(task);
除了任务队列,还可以用BlockingQueue实现数据的生产者消费者队列,即T是数据类型而非函数对象,queue的消费者从中拿到数据进行处理。其实本质上是一样的。
3:总结
总结而言,我推荐的C++多线程服务端编程模式为:one (event) loop per thread + thread pool:
event loop用作IO multiplexing,配合non-blockingIO和定时器;
thread pool用来做计算,具体可以是任务队列或生产者消费者队列。
以这种方式写服务器程序,需要一个优质的基于Reactor模式的网络库来支撑,muduo正是这样的网络库。比如dbproxy使用的是libevent。
程序里具体用几个loop、线程池的大小等参数需要根据应用来设定,基本的原则是“阻抗匹配”(解释见下),使得CPU和IO都能高效地运作。所谓阻抗匹配原则:
如果池中线程在执行任务时,密集计算所占的时间比重为 P (0 就能立刻列出用到某服务的客户端地址(Foreign Address列),然后在客户端的机器上用netstat或lsof命令找出是哪个进程发起的连接。TCP短连接和UDP则不具备这一特性。二是通过接收和发送队列的长度也较容易定位网络或程序故障。在正常运行的时候,netstat打印的Recv-Q和Send-Q都应该接近0,或者在0附近摆动。如果Recv-Q保持不变或持续增加,则通常意味着服务进程的处理速度变慢,可能发生了死锁或阻塞。如果Send-Q保持不变或持续增加,有可能是对方服务器太忙、来不及处理,也有可能是网络中间某个路由器或交换机故障造成丢包,甚至对方服务器掉线,这些因素都可能表现为数据发送不出去。通过持续监控Recv-Q和Send-Q就能及早预警性能或可用性故障。以下是服务端线程阻塞造成Recv-Q和客户端Send-Q激增的例子:
view plain copy
$netstat -tn
Proto Recv-Q Send-Q Local Address Foreign
tcp.0.0.10:.0.0.10:#服务端连接
tcp.0.0.10:.0.0.10:#客户端连接
tcp.0.0.10:.0.0.4:
五:多线程服务器的适用场合
如果要在一台多核机器上提供一种服务或执行一个任务,可用的模式有:
a:运行一个单线程的进程;
b:运行一个多线程的进程;
c:运行多个单线程的进程;
d:运行多个多线程的进程;
考虑这样的场景:如果使用速率为50MB/s的数据压缩库,进程创建销毁的开销是800微秒,线程创建销毁的开销是50微秒。如何执行压缩任务?
如果要偶尔压缩1GB的文本文件,预计运行时间是20s,那么起一个进程去做是合理的,因为进程启动和销毁的开销远远小于实际任务的耗时。
如果要经常压缩500kB的文本数据,预计运行时间是10ms,那么每次都起进程 似乎有点浪费了,可以每次单独起一个线程去做。
如果要频繁压缩10kB的文本数据,预计运行时间是200微秒,那么每次起线程似 乎也很浪费,不如直接在当前线程搞定。也可以用一个线程池,每次把压缩任务交给线程池,避免阻塞当前线程(特别要避免阻塞IO线程)。
由此可见,多线程并不是万灵丹(silver bullet)。
1:必须使用单线程的场合
据我所知,有两种场合必须使用单线程:
a:程序可能会fork(2);
实际编程中,应该保证只有单线程程序能进行fork(2)。多线程程序不是不能调用fork(2),而是这么做会遇到很多麻烦:
fork一般不能在多线程程序中调用,因为Linux的fork只克隆当前线程的thread of control,不可隆其他线程。fork之后,除了当前线程之外,其他线程都消失了。
这就造成一种危险的局面。其他线程可能正好处于临界区之内,持有了某个锁,而它突然死亡,再也没有机会去解锁了。此时如果子进程试图再对同一个mutex加锁,就会立即死锁。因此,fork之后,子进程就相当于处于signal handler之中(因为不知道调用fork时,父进程中的线程此时正在调用什么函数,这和信号发生时的场景一样),你不能调用线程安全的函数(除非它是可重入的),而只能调用异步信号安全的函数。比如,fork之后,子进程不能调用:
malloc,因为malloc在访问全局状态时几乎肯定会加锁;
任何可能分配或释放内存的函数,比如snprintf;
任何Pthreads函数;
printf系列函数,因为其他线程可能恰好持有stdout/stderr的锁;
除了man 7 signal中明确列出的信号安全函数之外的任何函数。
因此,多线程中调用fork,唯一安全的做法是fork之后,立即调用exec执行另一个程序,彻底隔断子进程与父进程的联系。
在多线程环境中调用fork,产生子进程后。子进程内部只存在一个线程,也就是父进程中调用fork的线程的副本。
使用fork创建子进程时,子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态。如果父进程中的某个线程占有锁,则子进程同样占有这些锁。问题是子进程并不包含占有锁的线程的副本,所以子进程没有办法知道它占有了哪些锁,并且需要释放哪些锁。
尽管Pthread提供了pthread_atfork函数试图绕过这样的问题,但是这回使得代码变得混乱。因此《Programming With Posix Threads》一书的作者说:”Avoid using fork in threaded code except where the child process will immediately exec a new program.”。
b:限制程序的CPU占用率;
这个很容易理解,比如在一个8核的服务器上,一个单线程程序即便发生busy-wait,占满1个core,其CPU使用率也只有12.5%,在这种最坏的情况下,系统还是有87.5%的计算资源可供其他服务进程使用。
linux 读线程信息的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux 读线程信息,如何在Linux上读取线程的信息,如何看懂《Linux多线程服务端编程的信息别忘了在本站进行查找喔。
香港服务器选创新互联,2H2G首月10元开通。
创新互联(www.cdcxhl.com)互联网服务提供商,拥有超过10年的服务器租用、服务器托管、云服务器、虚拟主机、网站系统开发经验。专业提供云主机、虚拟主机、域名注册、VPS主机、云服务器、香港云服务器、免备案服务器等。
分享题目:如何在Linux上读取线程的信息(linux读线程信息)
分享路径:http://www.shufengxianlan.com/qtweb/news16/398516.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联