进程内核栈,用户栈及 Linux 进程栈和线程栈的区别

进程内核栈,用户栈及 Linux 进程栈和线程栈的区别,第1张

总结:线程栈的空间开辟在所属进程的堆区,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。线程栈的起始地址和大小存放在pthread_attr_t 中,栈的大小并不是用来判断栈是否越界,而是用来初始化避免栈溢出的缓冲区的大小(或者说安全间隙的大小)

进程内核栈、用户栈

1.进程的堆栈

内核在创建进程的时候,在创建task_struct的同事,会为进程创建相应的堆栈。每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存 在于内核空间。当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容是用户堆栈地址,使用用户栈;当进程在内核空间时,cpu堆栈指针寄存器里面的内 容是内核栈空间地址,使用内核栈。

2.进程用户栈和内核栈的切换

当进程因为中断或者系统调用而陷入内核态之行时,进程所使用的堆栈也要从用户栈转到内核栈。

进程陷入内核态后,先把用户态堆栈的地址保存在内核栈之中,然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈向内核栈的转换;当进程从内 核态恢复到用户态之行时,在内核态之行的最后将保存在内核栈里面的用户栈的地址恢复到堆栈指针寄存器即可。这样就实现了内核栈和用户栈的互转。

那么,我们知道从内核转到用户态时用户栈的地址是在陷入内核的时候保存在内核栈里面的,但是在陷入内核的时候,我们是如何知道内核栈的地址的呢?

关键在进程从用户态转到内核态的时候,进程的内核栈总是空的。这是因为,当进程在用户态运行时,使用的是用户栈,当进程陷入到内核态时,内 核栈保存进程在内核态运行的相关信心,但是一旦进程返回到用户态后,内核栈中保存的信息无效,会全部恢复,因此每次进程从用户态陷入内核的时候得到的内核 栈都是空的(为什么?)。所以在进程陷入内核的时候,直接把内核栈的栈顶地址给堆栈指针寄存器就可以了。

3.内核栈的实现

内核栈在kernel-2.4和kernel-2.6里面的实现方式是不一样的。

在kernel-2.4内核里面,内核栈的实现是:

Union task_union {

Struct task_struct task

Unsigned long stack[INIT_STACK_SIZE/sizeof(long)]

}

其中,INIT_STACK_SIZE的大小只能是8K。

内核为每个进程分配task_struct结构体的时候,实际上分配两个连续的物理页面,底部用作task_struct结构体,结构上面的用作堆栈。使用current()宏能够访问当前正在运行的进程描述符。

注意:这个时候task_struct结构是在内核栈里面的,内核栈的实际能用大小大概有7K。

内核栈在kernel-2.6里面的实现是(kernel-2.6.32):

Union thread_union {

Struct thread_info thread_info;

Unsigned long stack[THREAD_SIZE/sizeof(long)]

}

其中THREAD_SIZE的大小可以是4K,也可以是8K,thread_info占52bytes。

当内核栈为8K时,Thread_info在这块内存的起始地址,内核栈从堆栈末端向下增长。所以此时,kernel-2.6中的current宏是需要 更改的。要通过thread_info结构体中的task_struct域来获得于thread_info相关联的task。更详细的参考相应的 current宏的实现。

struct thread_info {

struct task_struct *task;

struct exec_domain *exec_domain;

__u32 flags;

__u32 status

__u32 cpu

… ..

}

注意:此时的task_struct结构体已经不在内核栈空间里面了。

一、堆内存相关配置

设置堆初始值

指令1:-Xms2g

指令2:-XX:InitialHeapSize=2048m

设置堆区最大值

指令1:`-Xmx2g`

指令2: -XX:MaxHeapSize=2048m

缩小堆内存的时机

-XX:MaxHeapFreeRatio=70//堆内存使用率大于70时扩张堆内存,xms=xmx时该参数无效,默认值70

扩张堆内存的时机

-XX:MinHeapFreeRatio=40//堆内存使用率小于40时缩减堆内存,xms=xmx时该参数无效,默认值40

新生代内存配置

指令1:-Xmn512m

指令2:-XX:MaxNewSize=512m

2个survivor区和Eden区大小比率

指令:-XX:SurvivorRatio=6 //S区和Eden区占新生代比率为1:6,两个S区2:6

新生代和老年代的占比

-XX:NewRatio=4 //表示新生代:老年代 = 1:4 即老年代占整个堆的4/5;默认值=2

二、方法区内存配置常用参数

初始化的Metaspace大小,

-XX:MetaspaceSize :

Metaspace最大值

-XX:MaxMetaspaceSize

三、线程栈内存配置常用参数

每个线程栈最大值

指令1:-Xss256k

指令2:-XX:ThreadStackSize=256k

注意:

栈设置太大,会导致线程创建减少。

栈设置小,会导致深入不够,深度的递归会导致栈溢出。

建议栈深度设置在3000-5000

四、配置垃圾收集器

Serial垃圾收集器(新生代)

开启:-XX:+UseSerialGC

关闭:-XX:-UseSerialGC

//新生代使用Serial 老年代则使用SerialOld

ParNew垃圾收集器(新生代)

开启 -XX:+UseParNewGC

关闭 -XX:-UseParNewGC

//新生代使用功能ParNew 老年代则使用功能CMS

Parallel Scavenge收集器(新生代)

开启 -XX:+UseParallelOldGC

关闭 -XX:-UseParallelOldGC

//新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器

ParallelOl垃圾收集器(老年代)

开启 -XX:+UseParallelGC

关闭 -XX:-UseParallelGC

//新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器

CMS垃圾收集器(老年代)

开启 -XX:+UseConcMarkSweepGC

关闭 -XX:-UseConcMarkSweepGC

G1垃圾收集器

开启 -XX:+UseG1GC

关闭 -XX:-UseG1GC

五、GC策略配置

GC并行执行线程数

-XX:ParallelGCThreads=16

新生代可容纳的最大对象

-XX:PretenureSizeThreshold=1000000 //大于此值的对象直接会分配到老年代,设置为0则没有限制。 //避免在Eden区和Survivor区发生大量的内存复制,该参数只对Serial和ParNew收集器有效,Parallel Scavenge并不认识该参数

进入老年代的GC年龄

进入老年代最小的GC年龄

-XX:InitialTenuringThreshol=7 //年轻代对象转换为老年代对象最小年龄值,默认值7,对象在坚持过一次Minor GC之后,年龄就加1,每个对象在坚持过一次Minor GC之后,年龄就增加1

进入老年代最大的GC年龄

-XX:MaxTenuringThreshold=15 //年轻代对象转换为老年代对象最大年龄值,默认值15

六、GC日志信息配置

配置GC文件路径

-Xloggc:/data/gclog/gc.log//固定路径名称生成 -Xloggc:/home/GCEASY/gc-%t.log //根据时间生成

滚动生成日志

日志文件达到一定大小后,生成另一个文件。须配置Xloggc

开启 -XX:+UseGCLogFileRotation

关闭 -XX:-UseGCLogFileRotation

-XX:NumberOfGCLogFiles=4 //滚动GC日志文件数,默认0,不滚动 -XX:GCLogFileSize=100k //GC文件滚动大小,需配置UseGCLogFileRotation,设置为0表示仅通过jcmd命令触发

电脑硬件有内存,针对不同的软件,将内存分为不同的层次,linux将内存分为栈和堆,至于进程与线程的区别,进程是一个完成的程序在运行时加载到内存中,而线程是包含在进程中的,一个进程中是有一个或者多个线程的。进程栈存储进程,线程栈存储线程。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/5928102.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-08
下一篇 2023-03-08

发表评论

登录后才能评论

评论列表(0条)

保存