~Linux C_4_进程的内存映像
之后会涉及到:程序表头用来创建进程映像。那么进程的本质是什么?
进程间通信的本质是什么?
From: 进程(1)进程的内存映像
进程上下文
当一个程序进入计算机的主存储器进行计算就构成了进程,主存储器中的进程到底是如何组成的?操作系统中把进程物理实体和支持进程运行的环境合称为进程上下文(process context)。当系统调度新进程占有处理器时,新老进程随之发生上下文切换,因此,进程的运行被认为是在进程的上下文中执行的。在操作系统中,进程上下文包括三个组成部分:
- 用户级上下文(user -level context) :由用户进程的程序块、用户数据块(含共享数据块)和用户堆栈组成的进程地址空间。
- 系统级上下文(system -level context):包括进程的标识信息、现场信息和控制信息,进程环境块,以及系统堆栈等组成的进程地址空间。
- 寄存器上下文(register context) :由程序状态字寄存器、各类控制寄存器、地址寄存器、通用寄存器、用户栈指针等组成。
进程的内存映像
进程的内存映像可以很好地说明进程的组成。简单的说,一个进程映像(Process Image)包括四个要素:
- 进程程序块,即被执行的程序,规定了进程一次运行应完成的功能。通常它是纯代码,作为一种系统资源可被多个进程共享。
- 进程数据块,即程序运行时加工处理对象,包括全局变量、局部变量和常量等的存放区以及开辟的工作区,常常为一个进程专用。
- 进程控制块,每一个进程都将捆绑一个进程控制块,用来存储进程的标志信息、现场信息和控制信息。进程创建时,建立进程控制块;进程撤销时,回收进程控制块,它与进程一一对应。
- 系统/用户堆栈,每一个进程都将捆绑一个系统/用户堆栈。用来解决过程调用或系统调用时的信息存储和参数传递。
创建一个进程 系统调用的过程 “内核态”与“用户态"
创造子进程 管理子进程
内核线程 用户“轻量级线程 LWP" 用户线程 绑定模式:用户线程+LWP LInux 调度器 硬中断 软中断
定时器 进程优先级 -CFS(完全公平调度算法)- 之红黑树
轮到了这个进程,放飞它
struct task_struct {
volatile long state; /* -1 不可运行, 0 可运行, >0 已停止 */ void *stack; /* 内核栈 */ atomic_t usage; unsigned int flags; /* 一组标志 */ unsigned int ptrace; /* ... */ int prio, static_prio, normal_prio; /* 优先级 */ /* ... */ struct list_head tasks; /* 执行的线程(可以有很多) */ struct plist_node pushable_tasks; struct mm_struct *mm, *active_mm; /* 内存页(进程地址空间) */ /* 进行状态 */ int exit_state; int exit_code, exit_signal; int pdeath_signal; /* 当父进程死亡时要发送的信号 */ /* ... */ pid_t pid; /* 进程号 */ pid_t tgid; /* ... */ struct task_struct *real_parent; /* 实际父进程real parent process */ struct task_struct *parent; /* SIGCHLD的接受者,由wait4()报告 */ struct list_head children; /* 子进程列表 */ struct list_head sibling; /* 兄弟进程列表 */ struct task_struct *group_leader; /* 线程组的leader */ /* ... */ char comm[TASK_COMM_LEN]; /* 可执行程序的名称(不包含路径) */ /* 文件系统信息 */ int link_count, total_link_count; /* ... */ /* 特定CPU架构的状态 */ struct thread_struct thread; /* 进程当前所在的目录描述 */ struct fs_struct *fs; /* 打开的文件描述信息 */ struct files_struct *files; /* ... */
};
内核栈在kernel-2.6里面的实现是:
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。
structthread_info { struct task_struct *task; struct exec_domain *exec_domain; __u32 flags; __u32 status; __u32 cpu; … .. };
注意:此时的task_struct结构体已经不在内核栈空间里面了。
总结一下,
1. 为什么把内核栈和thread_info放在一起:
内核2.6版本之后,task_struct不再存放于内核栈中,而是通过slab分配器动态分配和复用。
所以为了获取当前运行进程信息, 新引入一个thread_info的结构体, 里面保存task_struct的指针, 放在内核栈底。
这样当进程从用户态陷入内核态后, esp寄存器会记录内核栈的地址, 通过计算就可以知道thread_info的地址, 进而获得task_struct的指针,也就获取了当前正在运行进程的所有信息。
2. 进程从整体上来看有两部分构成:
(1) 进程地址空间 (2)存在内核中的信息: 内核栈, thread_info, task_struct
End.