• 第十四学 linux内核——进程管理——进程控制块的内容(3)


            这节讲进程控制块中的标识进程之间亲属关系的那些域。

           系统创建的进程具有父子关系。所有的进程都是PID为1的init进程的后代(ps -e命令可以看到系统中进程相关的一些信息)。内核在系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。随后所有创建的进程都是从该进程中创建的子进程。系统中每个进程必有一个子进程,相应的,每个进程也可以拥有一个或多个子进程。拥有统一个父进程的所有进程被称为兄弟。我们在进程PCB中定义了描述这种继承关系的几个域,linux内核(3.9版)这样定义这几个域:

    struct task_struct {
         // 1> 状态信息——描述进程的动态变化,如就绪态、等待态、僵死态等。
    	 volatile long state;  /* -1 unrunnable, 0 runnable, >0 stopped */
    	 
    	 // 2> 亲属关系——描述进程的亲属关系,如祖父进程、父进程、养父进程、子进程、兄进程、孙进程等。
    	 
    	 /*
              * pointers to (original) parent process, youngest child, younger sibling,
              * older sibling, respectively.  (p->father can be replaced with
              * p->real_parent->pid)
              */
             struct task_struct __rcu *real_parent; /* real parent process */
             struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
             /*
              * children/sibling forms the list of my natural children
              */
             struct list_head children;      /* list of my children */
             struct list_head sibling;       /* linkage in my parent's children list */
             struct task_struct *group_leader;       /* threadgroup leader */
    
    	 // 3> 各种标识符——如进程标识符、用户标识符等等用来标识一个进程的数字。
    	 // 4> 进程间通信信息——描述多个进程在同一任务上协作工作,如管道、消息队列、共享内存、套接字等。
    	 // 5> 时间和定时器信息——描述进程在生存周期内使用CPU时间的统计、计费等信息。
    	 // 6> 调度信息——描述进程的优先级、调度策略等信息,如静态优先级、动态优先级、时间片轮转、高优先级以及多级反馈队列等的调度策略。
    	 // 7> 文件系统信息——对进程使用文件情况进行记录,如文件描述符、系统打开文件表、用户打开文件表等。
    	 // 8> 虚拟内存信息——描述每个进程拥有的地址空间,也就是进程编译连接后形成的空间,这里肯定用到前边提到的分页机制。
    	 // 9> 处理器环境信息——描述进程的执行环境(处理器的各种寄存器及堆栈等),这是体现进程动态变化的最主要的场景。
    	 	
    };

            这里说明一点,一个进程可能有两个父亲,一个为亲生父亲(real_parent),一个为养父(parent),因为父进程有可能在子进程之前销毁,就得给子进程重新找个养父,但大多数情况下,生父和养父是相同的,即real_parent和parent指向同一个位置。

            我们可以通过下面的代码获得指向其父进程的进程控制块:

    	struct task_struct *my_parent = current->parent;

            可以按以下方式依次访问子进程:

    	struct task_struct *task;
    	struct list_head *list;
    	
    	list_for_each(list, &currnet->children) {
    		task = list_entry(list, struct tast_struct, sibling);
    		/*task现在指向当前的某个子进程*/
    	}

            init进程的进程控制块是作为init_task静态分配的。下面的代码可以很好地演示所有进程之间的关系:

    	struct task_struct *task;
    	
    	for (task = current; task != init_task; task = task->parent)
    		;
    	/*task 现在指向init*/

            实际上,可以通过这种继承体系从系统的任何一个进程出发找到任意指定的其他进程。但大都数时候,只需呀简单的重复方式就可以遍历系统中所有的子进程,因为我们是通过双向链表组织所有进程的,<linux/sched.h>中给出了获取一个进程的先一个进程的宏(3.9版内核):

    	#define next_task(p) \
            list_entry_rcu((p)->tasks.next, struct task_struct, tasks)

            内核也定义了一个遍历整个任务(一个进程也叫一个任务)队列的宏:

    	#define for_each_process(p) \
    		for (p = &init_task ; (p = next_task(p)) != &init_task ; )

            我们要注意的是,在一个拥有大量进程的系统中通过重复来遍历所有进程的代价是很大的。


     
            
     

  • 相关阅读:
    如何完全禁用或卸载Windows 10中的OneDrive
    ASP.NET Web API从注释生成帮助文档
    c#中使用easyUI的DataGrid组件
    第三方分页控件aspnetPager出现问题解决方法
    ActionResult派生类
    Razor引擎的转换数据类型
    ViewData与ViewBag比较
    .net开源后可以查看的源代码
    如何让一个类可以被foreach枚举?
    .NET Framework中Object基类有哪些方法?
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3122955.html
Copyright © 2020-2023  润新知