被问到两个问题, 后来想了下如果要讲明白还不太容易,需要对进程的概念,进程管理有清晰的认识:

1. 父进程打开了一个文件,然后通过fork创建一个子进程, 子进程是否共享父进程的文件描述符?

2. 在shell中执行脚本、程序的机制或者流程是什么?

下面就对这两个问题涉及到的Linux进程的基本概念,进程的创建、 线程的实现, 进程的终结等做一下总结:

1. Linux进程基本概念和数据结构

进程-处于执行期的程序和它相关资源的总称。
理解: 进程是程序执行的实时结果, 这个实时结果包括很多资源和状态,比如:
可执行程序代码,数据段,打开的文件,挂起的信号, 内核的内部数据,处理器状态,线程,地址空间等。
Linux操作系统为了便于管理所有这些细节,会在内核中使用task_struct结构体来记录一个进程的各种信息,这个结构体的实例就是进程描述符。

(1)进程地址空间:
为什么会有地址空间这个概念, 因为现在os都采用了虚拟内存技术,在c编程中取得的内存地址都是虚拟地址,真实的物理地址是内核来完成映射的,
这样避免了程序直接操作物理地址引起的混乱。 每个进程都有自己独立的虚拟地址空间, 让进程在分配和管理内存的时候觉得自己是拥有整个内存资源。

进程地址空间分为内核空间和用户空间, 32位CPU的linux上其大小分别是1G和3G。
内核地址空间固定且连续映射, 所有进程共享此区域, 用户无法直接访问内核空间。
用户地址空间如上图所示,分为堆栈区,bss段, 数据段, 代码段(Text segment)用来存放不同的资源

(2)进程描述符 (process descriptor)
进程描述符是task_struct结构体类型, 用来记录进程的所有信息。
每个进程在内核中都有一个进程描述符对应, 内核把进程的列表存放在叫task list的双向循环链表中。

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;
/* ... */
};

(3)thread_info结构和内核栈:
每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存在于内核空间。当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容是用户堆栈地址,使用用户栈;当进程在内核空间时,cpu堆栈指针寄存器里面的内容是内核栈空间地址,使用内核栈。
在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里面的实现是:
 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。
 struct thread_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

(4)进程状态

2. 进程创建

3. 线程在Linux中的实现

4. 进程终结

5. 两个问题的解释

Reference:

Linux kernal 2.6.24

Linux内核设计与实现

fork: http://blog.csdn.net/cywosp/article/details/27316803

Linux进程管理: http://blog.csdn.net/xinyuan510214/article/details/50516279

Linux进程-基本概念: http://blog.sina.com.cn/s/blog_53ce9f9301018l1m.html

应用程序执行机制: http://www.cnblogs.com/li-hao/archive/2011/09/24/2189504.html

Linux进程管理(一、 基本概念和数据结构)的更多相关文章

  1. Linux进程管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...

  2. Linux进程管理 (2)CFS调度器

    关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...

  3. Linux进程管理知识整理

    Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...

  4. Linux性能及调优指南(翻译)之Linux进程管理

    本文为IBM RedBook的Linux Performanceand Tuning Guidelines的1.1节的翻译原文地址:http://www.redbooks.ibm.com/redpap ...

  5. Linux进程管理 (1)进程的诞生

    专题:Linux进程管理专题 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 L ...

  6. 《Linux 性能及调优指南》1.1 Linux进程管理

    https://blog.csdn.net/ljianhui/article/details/46718835 本文为IBM RedBook的Linux Performanceand Tuning G ...

  7. Linux进程管理与调度-之-目录导航【转】

    转自:http://blog.csdn.net/gatieme/article/details/51456569 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:// ...

  8. 《Linux性能及调优指南》----1.1 Linux进程管理

    翻译:飞哥 ( http://hi.baidu.com/imlidapeng ) 版权所有,尊重他人劳动成果,转载时请注明作者和原始出处及本声明. 原文名称:<Linux Performance ...

  9. 《Linux性能及调优指南》 Linux进程管理

    版权所有: 原文名称:<Linux Performance and Tuning Guidelines> 原文地址:http://www.redbooks.ibm.com/abstract ...

随机推荐

  1. 用Python输出一个杨辉三角的例子

    用Python输出一个杨辉三角的例子 这篇文章主要介绍了用Python和erlang输出一个杨辉三角的例子,同时还提供了一个erlang版杨辉三角,需要的朋友可以参考下 关于杨辉三角是什么东西,右转维 ...

  2. jeecms首页模板自定义

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qxy369/article/details/50387465我们在点击[查看首页]时,发现出现的并不 ...

  3. 记一次msf入侵win10,并拍照

    好久没有玩kali了,刚才看到一位大佬msf渗透win10的思路,我感觉不错,我就来复现一下 kali :192.168.45.136 win10 : 192.168.45.137 1  首先,我们查 ...

  4. JDK配置环境变量 (JDK安装成功后进行配置)

    "我的电脑" -- 右键"属性" --- "高级系统设置" --- “环境变量” 第一步:配置     JAVA_HOME 变量 找到 变量 ...

  5. [Day3] Nginx配置Https

    一. 网络安全之Http与Https Http协议是互联网行业中设计的最好架构之一.20多年间,应用Http协议传输数据的软件越来越多,企图从http协议传输中非法获取.篡改用户重要数据的非法行为也越 ...

  6. 原型模式(Prototype)(对象、克隆广告邮件)

    有些对象创建过程较为复杂,而且有些时候需要频繁的创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后复制这个原型对象的方法创建更多同类型的对象.这就是原型模式的动机. 原型模式的主要思想 ...

  7. 2019-8-31-dotnet-控制台读写-Sqlite-提示-no-such-table-找不到文件

    title author date CreateTime categories dotnet 控制台读写 Sqlite 提示 no such table 找不到文件 lindexi 2019-08-3 ...

  8. Spring Cloud Alibaba迁移指南(二):零代码替换 Eureka

    自 Spring Cloud 官方宣布 Spring Cloud Netflix 进入维护状态后,我们开始制作<Spring Cloud Alibaba迁移指南>系列文章,向开发者提供更多 ...

  9. Mysql 遇到的一些坑

    1.命令行按回车,或是输入\c,quit 都无法结束编辑状态,如图: 出现了"> ,然后不管输入什么都无法退出,这时输入>前的字符作为结束字符,再输入\c,既需要输入 " ...

  10. docker-4-Dockerfile配置文件详解

    ​ Dockerfile简单一点就是描述你这个镜像安装了哪些软件包,有哪些操作,创建了什么东西.有些人喜欢用 docker commit 命令去打包镜像,这样是不好的,首先commit出来的镜像比你使 ...