宁海在线

 找回密码
 立即注册

QQ登录

只需一步,快速开始

快捷登录

客服电话:0574-65520000
搜索
查看: 5108|回复: 18

[软件] Linux 进程管理剖析

[复制链接]

3

主题

17

帖子

110

积分

Lv.1 婴儿

Rank: 1

积分
110
在线时间
5 小时
QQ
发表于 2009-1-21 21:08:00 | 显示全部楼层 |阅读模式 | 来自浙江
Linux® 的用户空间进程的创建和管理所涉及的原理与 UNIX® 有很多共同点,但也有一些特定于 Linux 的独特之处。在本文中,了解 Linux 进程的生命周期,探索用户进程创建、内存管理、调度和销毁的内核内幕。
    Linux 是一种动态系统,能够适应不断变化的计算需求。Linux 计算需求的表现是以进程 的通用抽象为中心的。进程可以是短期的(从命令行执行的一个命令),也可以是长期的(一种网络服务)。因此,对进程及其调度进行一般管理就显得极为重要。
   在用户空间,进程是由进程标识符(PID)表示的。从用户的角度来看,一个 PID 是一个数字值,可惟一标识一个进程。一个 PID 在进程的整个生命期间不会更改,但 PID 可以在进程销毁后被重新使用,所以对它们进行缓存并不见得总是理想的。
   在用户空间,创建进程可以采用几种方式。可以执行一个程序(这会导致新进程的创建),也可以在程序内,调用一个 fork 或 exec 系统调用。fork 调用会导致创建一个子进程,而 exec 调用则会用新程序代替当前进程上下文。接下来,我将对这几种方法进行讨论以便您能很好地理解它们的工作原理。
   在本文中,我将按照下面的顺序展开对进程的介绍,首先展示进程的内核表示以及它们是如何在内核内被管理的,然后来看看进程创建和调度的各种方式(在一个或多个处理器上),最后介绍进程的销毁。

-------------------
进程表示
-------------------

   在 Linux 内核内,进程是由相当大的一个称为 task_struct 的结构表示的。此结构包含所有表示此进程所必需的数据,此外,还包含了大量的其他数据用来统计(accounting)和维护与其他进程的关系(父和子)。对 task_struct 的完整介绍超出了本文的范围,清单 1 给出了 task_struct 的一小部分。这些代码包含了本文所要探索的这些特定元素。task_struct 位于 ./linux/include/linux/sched.h。

清单 1. task_struct 的一小部分
-----------------------------------------------------------------------------------
struct task_struct {

        volatile long state;
        void *stack;
        unsigned int flags;

        int prio, static_prio;

        struct list_head tasks;

        struct mm_struct *mm, *active_mm;

        pid_t pid;
        pid_t tgid;

        struct task_struct *real_parent;

        char comm[TASK_COMM_LEN];

        struct thread_struct thread;

        struct files_struct *files;

        ...

};
-----------------------------------------------------------------------------------

    在清单 1 中,可以看到几个预料之中的项,比如执行的状态、堆栈、一组标志、父进程、执行的线程(可以有很多)以及开放文件。我稍后会对其进行详细说明,这里只简单加以介绍。state 变量是一些表明任务状态的比特位。最常见的状态有:TASK_RUNNING 表示进程正在运行,或是排在运行队列中正要运行;TASK_INTERRUPTIBLE 表示进程正在休眠、TASK_UNINTERRUPTIBLE 表示进程正在休眠但不能叫醒;TASK_STOPPED 表示进程停止等等。这些标志的完整列表可以在 ./linux/include/linux/sched.h 内找到。
   flags 定义了很多指示符,表明进程是否正在被创建(PF_STARTING)或退出(PF_EXITING),或是进程当前是否在分配内存(PF_MEMALLOC)。可执行程序的名称(不包含路径)占用 comm(命令)字段。
    每个进程都会被赋予优先级(称为 static_prio),但进程的实际优先级是基于加载以及其他几个因素动态决定的。优先级值越低,实际的优先级越高。
    tasks 字段提供了链接列表的能力。它包含一个 prev 指针(指向前一个任务)和一个 next 指针(指向下一个任务)。
    进程的地址空间由 mm 和 active_mm 字段表示。mm 代表的是进程的内存描述符,而 active_mm 则是前一个进程的内存描述符(为改进上下文切换时间的一种优化)
   thread_struct 则用来标识进程的存储状态。此元素依赖于 Linux 在其上运行的特定架构,在 ./linux/include/asm-i386/processor.h 内有这样的一个例子。在此结构内,可以找到该进程自执行上下文切换后的存储(硬件注册表、程序计数器等)。

-------------------
进程管理
-------------------

    现在,让我们来看看如何在 Linux 内管理进程。在很多情况下,进程都是动态创建并由一个动态分配的 task_struct 表示。一个例外是 init 进程本身,它总是存在并由一个静态分配的 task_struct 表示。在 ./linux/arch/i386/kernel/init_task.c 内可以找到这样的一个例子。
   Linux 内所有进程的分配有两种方式。第一种方式是通过一个哈希表,由 PID 值进行哈希计算得到;第二种方式是通过双链循环表。循环表非常适合于对任务列表进行迭代。由于列表是循环的,没有头或尾;但是由于 init_task 总是存在,所以可以将其用作继续向前迭代的一个锚点。让我们来看一个遍历当前任务集的例子。
   任务列表无法从用户空间访问,但该问题很容易解决,方法是以模块形式向内核内插入代码。清单 2 中所示的是一个很简单的程序,它会迭代任务列表并会提供有关每个任务的少量信息(name、pid 和 parent 名)。注意,在这里,此模块使用 printk 来发出结果。要查看具体的结果,可以通过 cat 实用工具(或实时的 tail -f /var/log/messages)查看 /var/log/messages 文件。next_task 函数是 sched.h 内的一个宏,它简化了任务列表的迭代(返回下一个任务的 task_struct 引用)。

清单 2. 发出任务信息的简单内核模块(procsview.c)
-----------------------------------------------------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>

int init_module( void )
{
  /* Set up the anchor point */
  struct task_struct *task = &init_task;

  /* Walk through the task list, until we hit the init_task again */
  do {

    printk( KERN_INFO "*** %s [%d] parent %s\n",
                task->comm, task->pid, task->parent->comm );

  } while ( (task = next_task(task)) != &init_task );

  return 0;

}

void cleanup_module( void )
{
  return;
}
-----------------------------------------------------------------------------------

    可以用清单 3 所示的 Makefile 编译此模块。在编译时,可以用 insmod procsview.ko 插入模块对象,也可以用 rmmod procsview 删除它。

清单 3. 用来构建内核模块的 Makefile
-----------------------------------------------------------------------------------
obj-m += procsview.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
-----------------------------------------------------------------------------------

    插入后,/var/log/messages 可显示输出,如下所示。从中可以看到,这里有一个空闲任务(称为 swapper)和 init 任务(pid 1)。

--------------------------------------------------------------------------------------------------------------------
Nov 12 22:19:51 mtj-desktop kernel: [8503.873310] *** swapper [0] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904182] *** init [1] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904215] *** kthreadd [2] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904233] *** migration/0 [3] parent kthreadd
...
--------------------------------------------------------------------------------------------------------------------

    注意,还可以标识当前正在运行的任务。Linux 维护一个称为 current 的符号,代表的是当前运行的进程(类型是 task_struct)。如果在 init_module 的尾部插入如下这行代码:
-------------------------------------------------------------------------------------------------------
printk( KERN_INFO, "Current task is %s [%d], current->comm, current->pid );
-------------------------------------------------------------------------------------------------------
会看到:
-----------------------------------------------------------------------------------------------------------------
Nov 12 22:48:45 mtj-desktop kernel: [10233.323662] Current task is insmod [6538]
-----------------------------------------------------------------------------------------------------------------
    注意到,当前的任务是 insmod,这是因为 init_module 函数是在 insmod 命令执行的上下文运行的。current 符号实际指的是一个函数(get_current)并可在一个与 arch 有关的头部中找到(比如 ./linux/include/asm-i386/current.h 内找到)。

----------------
进程创建
----------------

    让我们不妨亲自看看如何从用户空间创建一个进程。用户空间任务和内核任务的底层机制是一致的,因为二者最终都会依赖于一个名为 do_fork 的函数来创建新进程。在创建内核线程时,内核会调用一个名为 kernel_thread 的函数(参见 ./linux/arch/i386/kernel/process.c),此函数执行某些初始化后会调用 do_fork。
------------------------------------------------------------------------------------
*                                              系统调用函数                                       *
*    您可能已经看到过系统调用的模式了。在很多情况下,系     *
*    统调用都被命名为 sys_* 并提供某些初始功能以实现调用     *
*    (例如错误检查或用户空间的行为)。实际的工作常常会     *
*    委派给另外一个名为 do_* 的函数。                                          *
------------------------------------------------------------------------------------
    创建用户空间进程的情况与此类似。在用户空间,一个程序会调用 fork,这会导致对名为 sys_fork 的内核函数的系统调用(参见 ./linux/arch/i386/kernel/process.c)。函数关系如图 1 所示。

图 1. 负责创建进程的函数的层次结构
---------------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------

    从图 1 中,可以看到 do_fork 是进程创建的基础。可以在 ./linux/kernel/fork.c 内找到 do_fork 函数(以及合作函数 copy_process)。
    do_fork 函数首先调用 alloc_pidmap,该调用会分配一个新的 PID。接下来,do_fork 检查调试器是否在跟踪父进程。如果是,在 clone_flags 内设置 CLONE_PTRACE 标志以做好执行 fork 操作的准备。之后 do_fork 函数还会调用 copy_process,向其传递这些标志、堆栈、注册表、父进程以及最新分配的 PID。
    新的进程在 copy_process 函数内作为父进程的一个副本创建。此函数能执行除启动进程之外的所有操作,启动进程在之后进行处理。copy_process 内的第一步是验证 CLONE 标志以确保这些标志是一致的。如果不一致,就会返回 EINVAL 错误。接下来,询问 Linux Security Module (LSM) 看当前任务是否可以创建一个新任务
   接下来,调用 dup_task_struct 函数(在 ./linux/kernel/fork.c 内),这会分配一个新 task_struct 并将当前进程的描述符复制到其内。在新的线程堆栈设置好后,一些状态信息也会被初始化,并且会将控制返回给 copy_process。控制回到 copy_process 后,除了其他几个限制和安全检查之外,还会执行一些常规管理,包括在新 task_struct 上的各种初始化。之后,会调用一系列复制函数来复制此进程的各个方面,比如复制开放文件描述符(copy_files)、复制符号信息(copy_sighand 和 copy_signal)、复制进程内存(copy_mm)以及最终复制线程(copy_thread)。
    之后,这个新任务会被指定给一个处理程序,同时对允许执行进程的处理程序进行额外的检查(cpus_allowed)。新进程的优先级从父进程的优先级继承后,执行一小部分额外的常规管理,而且控制也会被返回给 do_fork。在此时,新进程存在但尚未运行。do_fork 函数通过调用 wake_up_new_task 来修复此问题。此函数(可在 ./linux/kernel/sched.c 内找到)初始化某些调度程序的常规管理信息,将新进程放置在运行队列之内,然后将其唤醒以便执行。最后,一旦返回至 do_fork,此 PID 值即被返回给调用程序,进程完成。

---------------
进程调度
---------------

    存在于 Linux 的进程也可通过 Linux 调度程序被调度。虽然调度程序超出了本文的讨论范围,但 Linux 调度程序维护了针对每个优先级别的一组列表,其中保存了 task_struct 引用。任务通过 schedule 函数(在 ./linux/kernel/sched.c 内)调用,它根据加载及进程执行历史决定最佳进程。

---------------
进程销毁
---------------

    进程销毁可以通过几个事件驱动 — 通过正常的进程结束、通过信号或是通过对 exit 函数的调用。不管进程如何退出,进程的结束都要借助对内核函数 do_exit(在 ./linux/kernel/exit.c 内)的调用。此过程如图 2 所示。

图 2. 实现进程销毁的函数的层次结构
---------------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------

    do_exit 的目的是将所有对当前进程的引用从操作系统删除(针对所有没有共享的资源)。销毁的过程先要通过设置 PF_EXITING 标志来表明进程正在退出。内核的其他方面会利用它来避免在进程被删除时还试图处理此进程。将进程从它在其生命期间获得的各种资源分离开来是通过一系列调用实现的,比如 exit_mm(删除内存页)和 exit_keys(释放线程会话和进程安全键)。do_exit 函数执行释放进程所需的各种统计,这之后,通过调用 exit_notify 执行一系列通知(比如,告知父进程其子进程正在退出)。最后,进程状态被更改为 PF_DEAD,并且还会调用 schedule 函数来选择一个将要执行的新进程。请注意,如果对父进程的通知是必需的(或进程正在被跟踪),那么任务将不会彻底消失。如果无需任何通知,就可以调用 release_task 来实际收回由进程使用的那部分内存。

-------------
结束语
-------------

    Linux 还在不断演进,其中一个有待进一步创新和优化的领域就是进程管理。在坚持 UNIX 原理的同时,Linux 也在不断突破。新的处理器架构、对称多处理(SMP)以及虚拟化都将促使在内核领域内取得新进展。其中的一个例子就是 Linux 版本 2.6 中引入的新的 O(1) 调度程序,它为具有大量任务的系统提供了可伸缩性。另外一个例子就是使用 Native POSIX Thread Library (NPTL) 更新了的线程模型,与之前的 LinuxThreads 模型相比,它带来了更为有效的线程处理。

评分

参与人数 1积分 +10 收起 理由
忆嘉 + 10 优秀主题:作业认真完成,给你小红旗

查看全部评分

下载宁海在线客户端

53

主题

942

帖子

5989

积分

历届版主

威豪百货商店

Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26

积分
5989
在线时间
538 小时
QQ
发表于 2009-1-21 21:35:49 | 显示全部楼层 | 来自浙江
原帖由 因帅判10年 于 2009-1-21 21:08 发表
Linux&reg; 的用户空间进程的创建和管理所涉及的原理与 UNIX&reg; 有很多共同点,但也有一些特定于 Linux 的独特之处。在本文中,了解 Linux 进程的生命周期,探索用户进程创建、内存管理、调度和销毁的内核内幕。 ...


论坛上用linux的人恐怕凤毛鳞角
讲如此深入没几个人能看
不如普及下linux基本知识
合理引导有兴趣的朋友进行研究
回复 支持 反对

使用道具 举报

3

主题

17

帖子

110

积分

Lv.1 婴儿

Rank: 1

积分
110
在线时间
5 小时
QQ
 楼主| 发表于 2009-1-21 21:52:49 | 显示全部楼层 | 来自浙江

回复 “忆嘉” 兄

忆嘉兄言之有理,对于骨灰级的计算机使用者而言,的确有点好菜无下肚之人的感觉!不过现在的 Linux 在桌面应用方面也有很大的进步,丝毫不输于Windows。如 Ubuntu 的桌面版本绑定的GNOME。KUbuntu 绑定的 KDE 等桌面系统都是习惯图形用户界面拿来就能上手的。忆嘉兄既然提出了介意,我改天会发布一些 Linux 桌面系统入门级的文章。内容大致包括系统的安装、日常使用的办公套件的安装、第3方应用软件的安装。如果感兴趣的话还可以聊一聊服务器的架设等等。
回复 支持 反对

使用道具 举报

53

主题

942

帖子

5989

积分

历届版主

威豪百货商店

Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26

积分
5989
在线时间
538 小时
QQ
发表于 2009-1-21 23:05:16 | 显示全部楼层 | 来自浙江
原帖由 因帅判10年 于 2009-1-21 21:52 发表
忆嘉兄言之有理,对于骨灰级的计算机使用者而言,的确有点好菜无下肚之人的感觉!不过现在的 Linux 在桌面应用方面也有很大的进步,丝毫不输于Windows。如 Ubuntu 的桌面版本绑定的GNOME。KUbuntu 绑定的 KDE 等桌面 ...


呵呵,有劳了,帅哥
有关linux方面的介绍
从基础、现状、安装使用等方方面面
如果帅哥有心做的话
建议设立一个linux索引
类似米花糖的那些置顶帖
就linux使用的方方面面,从入门到精通全方位进行系统整理!
可以从基础部分开始
索引帖可以慢慢一步步更新
索引帖建立以后我帮你置顶上去

至于现在
论坛上了解linux的人是不多,使用的更少
如果帅哥能给出足够理由
相信其中一部分人还是很乐意改投ubuntu或其他linux版本旗下
毕竟使用windows纯大部分人是X版,不是长久之计
回复 支持 反对

使用道具 举报

53

主题

942

帖子

5989

积分

历届版主

威豪百货商店

Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26

积分
5989
在线时间
538 小时
QQ
发表于 2009-1-21 23:12:33 | 显示全部楼层 | 来自浙江
原帖由 因帅判10年 于 2009-1-21 21:52 发表
忆嘉兄言之有理,对于骨灰级的计算机使用者而言,的确有点好菜无下肚之人的感觉!不过现在的 Linux 在桌面应用方面也有很大的进步,丝毫不输于Windows。如 Ubuntu 的桌面版本绑定的GNOME。KUbuntu 绑定的 KDE 等桌面 ...


我也曾安装试用过redhat,fedora等版本linux
1.当时处学校网络,搞不定上网设置问题
2.简单如windows环境下我的电脑概念,在linux下的概念很不相同
基本上安装后几天就废掉了
如果你能详尽解析这个系统
并牺牲部分时间解答菜鸟们关于新系统的安装设置问题
我想可能会有一批朋友在你带动下钻研linux

开拓性工作一直都是最难的
要有思想准备啊
春节期间大家都有事
不妨在这段时间考虑考虑下
是否明年能认真做下这件事情
回复 支持 反对

使用道具 举报

27

主题

437

帖子

2732

积分

Lv.7 探花

Rank: 7Rank: 7Rank: 7

积分
2732
在线时间
215 小时
发表于 2009-1-22 11:57:23 | 显示全部楼层 | 来自浙江
你们帮我找找大便(DEBIAN)的服务器版本,我找来找没找到,我要稳定版本的最好,没有另一个版本也行  LINUX装无线网卡大麻烦  UNBUNT好一点 自己会认 但要求电脑的配置比XP高多了。
在我们普通百姓家里,LINUX看起来用的不多,但实际上LINUX部分内容已经广泛运用在我们现在使用的XP电脑中,比如说GRUB 我们看到软件叫“矮人工作室”之类,全部是基于GRUB。 还有U盘操作系统,光盘操作系统等等。LINUX可以太杂,应用软件不多,尤其是我们常见的应用软件,比如  QQ 网络电视 讯雷等。
回复 支持 反对

使用道具 举报

3

主题

17

帖子

110

积分

Lv.1 婴儿

Rank: 1

积分
110
在线时间
5 小时
QQ
 楼主| 发表于 2009-1-22 15:37:43 | 显示全部楼层 | 来自浙江

回复 " wangrupin" 留言

Debian 的发行版比任何Linux发行版来的干净,没有必要找什么服务器版。Ubuntu也是基于Debian的系统。如果要架设服务器的话,可以使用APT系统安装任何常见第3方软件。至于以上你提的常见软件都有可用的替代方案,如 QQ有Linux版的(在 im.qq.com/qq/linux/ 上可以下载Debian的 deb 包或者rpm包)。迅雷等下载工具可以用Wget或者Kget代替。家庭网络下载的速度都差不多。
回复 支持 反对

使用道具 举报

3

主题

17

帖子

110

积分

Lv.1 婴儿

Rank: 1

积分
110
在线时间
5 小时
QQ
 楼主| 发表于 2009-1-22 15:43:18 | 显示全部楼层 | 来自浙江

回复 “忆嘉” 兄

可以!在此先谢过回复 “忆嘉” 兄对小可的信任。其实,使用Linux的用户大都都具备一种精神。那就是《开源精神》。大多问题都可以在互联网上找到答案。不过为了能给新手提供一个具体完整的 Linux 思想也是相当有必要的。台湾成大环工的鸟哥已经做了这件事,我就是读他的文章成长的。 地址在: linux.vbird.org
回复 支持 反对

使用道具 举报

53

主题

942

帖子

5989

积分

历届版主

威豪百货商店

Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26Rank: 26

积分
5989
在线时间
538 小时
QQ
发表于 2009-1-22 20:04:50 | 显示全部楼层 | 来自浙江
原帖由 因帅判10年 于 2009-1-22 15:43 发表
可以!在此先谢过回复 “忆嘉” 兄对小可的信任。其实,使用Linux的用户大都都具备一种精神。那就是《开源精神》。大多问题都可以在互联网上找到答案。不过为了能给新手提供一个具体完整的 Linux 思想也是相当有必要 ...


这个站点的确不错
唯一不好的就是繁体中文
当然有心学习的话这点也不是问题
有空的话
还是希望你能做做这方面工作
毕竟人家是人家
在宁海论坛里欠缺这方面的工作
回复 支持 反对

使用道具 举报

3

主题

17

帖子

110

积分

Lv.1 婴儿

Rank: 1

积分
110
在线时间
5 小时
QQ
 楼主| 发表于 2009-1-23 11:29:06 | 显示全部楼层 | 来自浙江

回复 “忆嘉” 兄

那好!来年开始实施这项计划吧。到时还希望“忆嘉” 兄能支持一下!
风吹鸡蛋壳
财去人安乐

[ 本帖最后由 因帅判10年 于 2009-1-23 11:31 编辑 ]
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|客户端|浙公网安备案 33022602000116|宁海在线 ( 浙B2-20200368

关于我们|电话:0574-65520000 ,GMT+8, 2024-11-23 20:31 , Processed in 0.098383 second(s), 26 queries , Apc On.

Powered by Discuz! X3.4

© 2000-2015 NHZJ Inc.

违法和不良信息举报电话:13819844444  邮箱:admin@nhzj.com
 未成年人保护服务电话:13819844444  邮箱:admin@nhzj.com
快速回复 返回顶部 返回列表