【Android Linux内存及性能优化】(五) 进程内存的优化 - 线程
发布日期:2021-06-29 14:52:03 浏览次数:2 分类:技术文章

本文共 6027 字,大约阅读时间需要 20 分钟。

【Android Linux内存及性能优化】五 进程内存的优化 - 线程

本文接着

《》
《》
《》
《》

一、内存篇

1.1 系统当前可用内存

1.2 进程的内存使用

1.3 进程内存优化

1.3.1 ELF执行文件

1.3.2 动态库

1.3.3 静态库

1.3.4 线程

在 Linux 中,输入 ps -ef 可以列出当前所有的进程:

ciellee@sh:~$ ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         1     0  0 11:33 ?        00:00:02 /sbin/init splashroot         2     0  0 11:33 ?        00:00:00 [kthreadd]root         4     2  0 11:33 ?        00:00:00 [kworker/0:0H]root         6     2  0 11:33 ?        00:00:00 [mm_percpu_wq]root         7     2  0 11:33 ?        00:00:01 [ksoftirqd/0]ciellee   2893  1620  0 11:35 ?        00:02:09 /usr/share/code/code --no-sandbox --unity-launchciellee   2902  2893  0 11:35 ?        00:00:00 /usr/share/code/code --type=zygote --no-sandboxciellee   2936  2893  1 11:35 ?        00:03:44 /usr/share/code/code --type=gpu-process --field-trial-haciellee   2946  2893  0 11:35 ?        00:00:00 /usr/share/code/code --type=utility --field-trial-handleciellee   3055  2893  0 11:35 ?        00:00:10 /usr/share/code/code --type=renderer --disable-color-cor

可以发现很多具有同样的名字的进程,PPID 一样,但PID 确不一样,

PID=2902 PPID = 2893 的 vscode 工具为便,我当前电脑正在运行 vscode。

可通过命令 cat /proc/2902/status 查看该进程相关信息,

其中 PID 是当前进程号, PPID 是父进程号, Threads 可以看出当前进程中含有多少个线程。

ciellee@sh:~$ cat /proc/2893/status Name:	codeUmask:	0002State:	S (sleeping)Tgid:	2893Ngid:	0Pid:	2893				--------->  子进程 PIDPPid:	1620				--------->  父进程 PIDTracerPid:	0Uid:	1000	1000	1000	1000Gid:	1000	1000	1000	1000FDSize:	128Groups:	4 24 27 30 46 113 128 1000 NStgid:	2893NSpid:	2893NSpgid:	2452NSsid:	2452VmPeak:	  893776 kBVmSize:	  881132 kBVmLck:	       0 kBVmPin:	       0 kBVmHWM:	  152904 kBVmRSS:	  120664 kBRssAnon:	   47724 kBRssFile:	   72940 kBRssShmem:	       0 kBVmData:	  325992 kBVmStk:	     136 kBVmExe:	  106284 kBVmLib:	   41724 kBVmPTE:	    1480 kBVmSwap:	       0 kBHugetlbPages:	       0 kBCoreDumping:	0Threads:	27					--------->  当前进程有27个线程SigQ:	4/63166SigPnd:	0000000000000000ShdPnd:	0000000000000000SigBlk:	0000000000000000SigIgn:	0000000000000000SigCgt:	00000001880056fbCapInh:	0000000000000000CapPrm:	0000000000000000CapEff:	0000000000000000CapBnd:	0000003fffffffffCapAmb:	0000000000000000NoNewPrivs:	0Seccomp:	0Speculation_Store_Bypass:	thread vulnerableCpus_allowed:	ffCpus_allowed_list:	0-7Mems_allowed:	00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001Mems_allowed_list:	0voluntary_ctxt_switches:	435153nonvoluntary_ctxt_switches:	4675ciellee@sh:~/work/code/CN300S_8950_0525$

接下来进入我们的主角,线程。

线程中的代码段、数据段、堆段 以及 栈段 又是如何呢?

在LInux 中,线程有两种方式, 一种是 pthread,另一种是 NPTL。

本文针对 pthread 分析下。

从编程的角度来讲,线程与进程的重要区别就是进程拥有自已的地址空间,而线程则是完全共享的。

从这个角度来讲,进程和动态库的数据段在线程之间是完全共享的,任何一个线程都可以访问到进程的全局变量。
堆段也是在进程内共享的,一个线程中申请到的一块内存,在另一个线程也能访问。
每个线程的栈段都是私有的,不可共享,因为每个线程都要有自已的运行过程。

在进程中,每创建一个线程,新创建的线程将调用 mmap在虚拟内存顶部分配一个 2MB 的虚拟内存,并且使用一个页面做隔离保护,以此作为线程的栈空间

当线程执行完毕退出后,每个线程分配的栈空间依然存在,并没有释放,只有当调用 phread_join 进行线程同步后,才会释放。

头文件 : #include 
函数定义: int pthread_join(pthread_t thread, void **retval);描述 :pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

示例如下:

int main(int argc, char *argv[]){
int first = 0; int i = 0; void * ret = NULL; pthread_t tid[N] = {
0}; printf("first=%p\n", &first); for( i=0; i

1.3.4.1 设置进程栈空间 ulimit -s

进程和线程的栈大小,是可以在 Linux 内核中设置的。

使用 ulimit 命令,可以查看和设置一个进程的栈空间的大小。

ciellee@sh:~$ ulimit  -acore file size          (blocks, -c) 	0data seg size           (kbytes, -d) 	unlimitedscheduling priority     (-e) 			0file size               (blocks, -f) 	unlimitedpending signals         (-i) 			63166max locked memory       (kbytes, -l) 	64max memory size         (kbytes, -m) 	unlimitedopen files              (-n) 			1024pipe size            	(512 bytes, -p) 8POSIX message queues    (bytes, -q) 	819200real-time priority      (-r) 			0stack size              (kbytes, -s) 	8192		// 进程栈空间cpu time                (seconds, -t) 	unlimitedmax user processes      (-u) 			63166virtual memory          (kbytes, -v) 	unlimitedfile locks              (-x) 			unlimited

可以看出,在Linux 中,默认栈空间大小为 8M。

当进程使用栈大于8M 时,运行时会报 Segmentation fault(core dumped) 错误,此时增通过 ulimit -s 16384 增大栈空间大小即可。

1.3.4.2 设置线程栈空间 pthread_attr_setstacksize

在前面测试,我们知道一个线程的栈内存空间为2MB,

有些情况下需要调整栈空间的大小,

比如说为了节省内存,

可以通过将多个进程以为线程而合并到一个进程,这样2MB 的线程栈空间可能对由进程来修改而成的线程来说,显得有点小,(可以节省(N-1)× 6M 的内存)。如果这些线程数据量大的话,那么每个线程2MB 的空间,显得有点小,就需要将线程的栈空间扩大。

另外,对于服务器来讲,其服务进程有可能会创建数个个线程,那么每个线程2MB 的空间,又显得太奢侈了,这时就需要将线程的栈空间减小。

可以使用 pthread_attr_setstacksize 来设置栈空间的大小,设置时一定要慎重,防止过小导致栈溢出。

演示代码如下:

#include 
pthread_attr_t tattr;pthread_t tid;int ret;size_t size = PTHREAD_STACK_MIN + 0x4000;//initialized with default attributesret = pthread_attr_init(&tattr);// setting the size of the stack alsoret = pthread_attr_setstacksize(&tattr, sieze);// only size specified in tattrret = pthread_create(&tid, &tattr, start_routine, arg);

1.3.4.3 减少线程数量

一般情况下,一个进程所拥有的线程数量很少,大概在 10个以内,如果每个线程栈使用20kb 的内存,总共消耗200kb 的内存,对系统的影响还是很小的。

可是对于某些网络服务器的进程,每个用户请求创建一个线程为其服务。

如果每个线程的工作时间很长,不能及时退出的话,会导致进程中同时并发大量的线程,这时其线程栈所占用的内存,就不可以忽视了。比如每个线程20kb,100 个就 2M了,对于内存稀缺的嵌入式设备来讲,是个不小的消耗。

对于线程众多的进程,需要考虑使用异步通信的方式来替代以前的线程+同步通信方式,以达到减少线程的目的。

好处在于: 一方面可以减少内存的使用,另一方面又可以减少线程数量,减轻Linux 在内核做进程调试时的负担。
缺点在于: 如果使用异步通信方式的话,会带来编码的复杂性。

1.3.5 Linux 共享内存

本文主要特指 Linux 共享内存,并不是介绍如何通过编程来实现共享内存,而是共享内存背后的故事。

有关编程方面,可以参考我之前写的文章《》

1.3.5.1 内核如何支持共享内存的?

进程间需要共享的数据放在一个叫 IPC 共享内存区域的地方,

所有需要访问该共享内存区域的进程都需要把该共享区域映射到本进程的地址空间中去。

系统共享内存通过 shmget 获得或创建一个IPC 共享内存区域,关返回相应的标识符。

内核保证shmget 获得或创建一个共享内存区,初始化该共享内存区域相应的shmid_kernel 结构的同时,还将在特殊文件系统 shm 中,创建并打开一个同名文件,并在内存中建立起该文件相应的dentry 及 inode 结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。

所有这一切都是系统调用 shmget 完成的。

在这里插入图片描述

在创建 一个共享内存区域后,还要将它映射到进程地址空,系统调用 shmat() 完成此项功能。

由于在调用 shmget() 时,已经创建了文件系统 shm 中的一个同名文件与共享内存区域相对应,因此,调用 shmat() 的过程相当于映射文件系统 shm 中同名文件过程,原理与mmap() 大同小异。

1.3.5.2 共享内存在不同的进程中,是否地址相同?

由于在不同的进程中,读写时都要调用 shmat 进行重映射,所以地址不一定相同。

转载地址:https://ciellee.blog.csdn.net/article/details/106334607 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【FFMPEG】华为新老两手机平台编码的视频,在同一车机上投屏解码时间慢的问题分析
下一篇:【Android Linux内存及性能优化】(四) 进程内存的优化 - 动态库- 静态库

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月08日 11时37分19秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

拿下计网协议后,我就是公园里最靓的仔 2019-04-29
再见,数据库!MySQL千亿数据分库分表架构,堪称惊艳! 2019-04-29
为什么曾经优秀的人突然变得平庸? 2019-04-29
某阿里程序员求助:绩效背1,老板让他主动走!敢要n+1就在背调时说坏话!怎么办?网友:大不了鱼死网破!... 2019-04-29
知识图谱在小米的应用与探索 2019-04-29
某美团程序员爆料:美团虽然屏蔽职级,但可以通过椅子判断!坐人体工学椅的至少是3-1和3-2的大佬!真是这样吗?... 2019-04-29
图解|打工人看腾讯这道多线程面试题 2019-04-29
23张图!万字详解「链表」,从小白到大佬! 2019-04-29
高德全链路压测——语料智能化演进之路 2019-04-29
打工人到什么状态,就可以离职了? 2019-04-29
面向领域的微服务架构 2019-04-29
某程序员吐槽清华北大不值钱了!过去清北毕业生去企业上班就是丢人现眼!现在互联网基层员工一堆清北人!清北怎么混成这样了?... 2019-04-29
为什么不建议把数据库部署在Docker容器内 2019-04-29
待在小公司好多年了,微服务还没怎么玩过。。。 2019-04-29
万字长文,理解Elasticsearch和面试总结 2019-04-29
面试官:数据量很大,分页查询很慢,有什么优化方案? 2019-04-29
编写 if 时不带 else,你的代码会更好! 2019-04-29
字节跳动总结的设计模式 PDF 火了,完整版开放下载! 2019-04-29
新款 iPad 写代码 真香,包邮送一个! 2019-04-29
7年,从“游戏少年”到大厂技术总监的逆袭之路 2019-04-29