目录
示例程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void* run(void* arg)
{
sleep(10000);
return NULL;
}
int main()
{
char* arr[10240]={0};
for(int i=0;i<10240;i++)
{
char *tmp = (char*)malloc(1024);
if (tmp == NULL)
{
perror("malloc exception");
exit(-1);
}
memset(tmp,1024,1);
arr[i] =tmp;
}
pthread_t t;
pthread_create(&t,NULL,run,NULL);
pthread_join(t,NULL);
pause();
return 0;
}
cat /proc/pid/status
Name: a.out
State: S (sleeping)
Tgid: 4869
Ngid: 0
Pid: 4869
PPid: 2072
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256
Groups: 0
NStgid: 4869
NSpid: 4869
NSpgid: 4869
NSsid: 2072
VmPeak: 25004 kB
VmSize: 25004 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 11492 kB
VmRSS: 11492 kB
VmData: 18692 kB
VmStk: 132 kB
VmExe: 4 kB
VmLib: 2040 kB
VmPTE: 52 kB
VmPMD: 12 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
Threads: 2
SigQ: 0/15567
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Seccomp: 0
Speculation_Store_Bypass: thread vulnerable
Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
Cpus_allowed_list: 0-127
Mems_allowed: 00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 1
nonvoluntary_ctxt_switches: 1
各个字段说明:
Name
进程名
State
进程状态,"R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "T(tracing stop)", "Z (zombie)", or "X (dead)"
Tgid
线程ID,准确的说是主线程ID,因为程序a.out 有两个线程。
Pid
进程ID
PPid
父进程ID
TracerPid
追逐当前进程的进程ID
测试:
strace top
Uid
0 0 0 0
Gid
0 0 0 0
解释:
第一列RUID:实际UID,表示程序执行的用户UID。
第二列EUID:有效UID,表示进程执行时,对文件的访问权限。
第三列SUID:保存设置用户ID,作为effective user ID的副本,在执行exec调用时后能重新恢复原来的effectiv user ID。
第四列FSUID:目前进程的文件系统的用户识别码.一般情况下,文件系统的用户识别码(fsuid)与有效的用户识别码(euid)是相同的。
使用普通用户执行top:
su - humou
top
cat /proc/`pgrep top|grep -v grep`/status
程序运行时,带有1001用户权限。
chmod +s /usr/bin/top
程序运行时,带有root权限。
FDSize
进程分配的文件描述符的最大值。只增不减。
Groups
启动该进程的用户所处组。由于是root,所以为0。
切换到humou用户,cp ./a.out /home/humou && cd /home/humou && chmod 777 a.out && su - humou && ./a.out
VmPeak
表示进程运行过程中占用内存的峰值。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
int main(int argc,const char* argv[])
{
if (argc != 2)
{
printf("invalid param\n");
exit(-1);
}
size_t mb = strtoul(argv[1],NULL,0);
size_t nbytes = mb << 20;
char *ptr =(char*)malloc(nbytes);
if (ptr == NULL)
{
printf("malloc failed\n");
exit(-1);
}
printf("malloc %d MB\n",mb);
free(ptr);
pause();
return 0;
}
gcc main.c && ./a.out 10
cat /proc/`pgrep a.out|grep -v grep`/status
10MB = 10240 KB < 14588KB
因为进程加载动态链接库也需要内存。
VmSize
表示进程所占用的内存,包含交换分区。
VmLck
表示进程被锁住的内存,不允许交换到磁盘。需要使用系统调用mlock实现锁内存。相对于VMPin, VmLck被锁住的内存可以被内核在RAM中移动,程序下一次访问该页可能会触发软缺页中断,这对于一般程序来说没有影响,但对于硬实时的程序来说,这种错误不能被接受。
https://lwn.net/Articles/600502/
示例程序:
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char arr[4097]={0};
if ( mlock(arr,sizeof(arr)) != 0 )
{
perror("mlock failed");
exit(-1);
}
sleep(5);
if ( munlock(arr,sizeof(arr)) != 0 )
{
perror("munlock failed");
exit(-1);
}
sleep(5);
printf("success lock and unlock 8KB memory\n");
return 0;
}
0~5s,因为操作系统内存页为4KB,所以申请4097会占用两页。
5~10s
VmPin
固定内存是固定在特定页框位置的锁定内存。固定的页面既不能被移除物理内存,也不能被内核在RAM中移动。
VmHWM
表示程序分配到的物理内存峰值
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
int main(int argc,const char* argv[])
{
if (argc != 2)
{
printf("invalid param\n");
exit(-1);
}
size_t mb = strtoul(argv[1],NULL,0);
size_t nbytes = mb << 20;
char *ptr =(char*)malloc(nbytes);
if (ptr == NULL)
{
printf("malloc failed\n");
exit(-1);
}
size_t psize = sysconf(_SC_PAGE_SIZE);
for (int i = 0;i < nbytes; i += psize)
{
ptr[i] = 0;
}
printf("malloc %d MB\n",mb);
//free(ptr);
pause();
return 0;
}
cat /proc/`pgrep a.out|grep -v grep`/status
103496kb > 102400kb
VmRSS
表示程序现在使用的物理内存
测试:
在执行程序前,free -m,空闲内存为3651MB ,交换分区空闲832MB
./a.out 2000 & 申请2000MB
cat /proc/`pgrep a.out|grep -v grep`/status
可以看到程序确实占用2G;
free -m,物理内存锐减2000MB,交换分区未被使用。
然后再启动另外一个进程,分配2000M
./a.out 2000 & ,很显然物理内存不够用,必须动用交换分区。
查看第一个a.out进程的物理内存占用情况: cat /proc/12915/status
查看第二个a.out进程的物理内存占用情况:cat /proc/12946/status
从现象来看,一定是第一个进程的部分数据被交换到 交换分区,腾出足够的内存来给第二个进程使用。
free -m,可以看到物理内存所剩无几,交换内存也使用了很大一部分。
VmData
表示进程数据段的大小.
VmStk
表示进程堆栈段的大小.
VmExe
表示进程代码的大小.
VmLib
表示进程所使用LIB库的大小.
关于代码段,堆栈段,数据段:
代码段可以为机器中运行同一程序的数个进程共享
堆栈段存放的是子程序(函数)的返回地址、子程序的参数及程序的局部变量
数据段则存放程序的全局变量、常数以及动态数据分配的数据空间(比如用malloc函数申请的内存)
与代码段不同,如果系统中同时运行多个相同的程序,它们不能使用同一堆栈段和数据段.
VmPTE
占用的页表的大小。(since Linux 2.6.10)
VmPMD
占用二级页表大小。(since Linux 4.0)
VmSwap
进程占用交换分区的大小。
Threads
线程数
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *run(void *arg)
{
pause();
return NULL;
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, run, NULL);
pthread_join(t, NULL);
pause();
return 0;
}
上述示例的进程会启动两个线程。
SigQ
表示当前进程待处理信号数。
示例程序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
volatile int done = 0;
void handler (int sig)
{
const char *str = "handled...\n";
write (1, str, strlen(str));
done = 1;
}
void child(void)
{
int i;
for (i = 0; i < 3; i++){
kill(getppid(), SIGRTMIN);
printf("child - BANG!\n");
}
exit (0);
}
int main (int argc, char *argv[])
{
signal (SIGRTMIN, handler);
sigset_t newset, oldset;
sigfillset(&newset);
sigprocmask(SIG_BLOCK, &newset, &oldset);
pid_t pid = fork();
if (pid == 0)
child();
printf("parent sleeping \n");
int r = sleep(30);
printf("woke up! r=%d\n", r);
sigprocmask(SIG_SETMASK, &oldset, NULL);
while (!done){
};
printf("exiting\n");
exit(0);
}
子进程向父进程发送3个信号,同时子进程退出时也会给父进程发送一个信号。
SigPnd
0000000000000000
ShdPnd
0000000000000000
SigBlk
0000000000000000
SigIgn
0000000001001206
SigCgt
0000000180014c21
解释:
SigPnd:屏蔽位,存储了该线程的待处理信号,等同于线程的PENDING信号.
ShnPnd:屏蔽位,存储了该线程组的待处理信号.等同于进程组的PENDING信号.
SigBlk:存放被阻塞的信号,等同于BLOCKED信号.
SigIgn:存放被忽略的信号,等同于IGNORED信号.
SigCgt:存放捕获的信号,等同于CAUGHT信号.
CapInh
0000000000000000
CapPrm
ffffffffffffffff
CapEff
ffffffffffffffff
CapBnd
ffffffffffffffff
解释:
CapInh:表示能够被当前进程执行的程序继承的能力.
CapPrm:表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力,这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集.
CapEff:当一个进程要进行某个特权操作时,操作系统会检查cap_effective的对应位是否有效,而不再是检查进程的有效UID是否为0.
CapBnd:是系统的边界能力,我们无法改变它.
Cpus_allowed
3
Cpus_allowed_list
0-1
解释:
Cpus_allowed:3指出该进程可以使用CPU的亲和性掩码,因为我们指定为两块CPU,所以这里就是3,如果该进程指定为4个CPU(如果有话),这里就是F(1111).
Cpus_allowed_list: 0-1指出该进程可以使用CPU的列表,这里是0-1.
修改前:
taskset -p 16184 ,限定该进程在第一个CPU上跑。
voluntary_ctxt_switches
1
nonvoluntary_ctxt_switches
0
说明:
voluntary_ctxt_switches表示进程主动切换的次数.
nonvoluntary_ctxt_switches表示进程被动切换的次数.