Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程的后代。内核在系统启动的最后阶段启动 init 进程。该进程读取系统的初始化脚本(initscript)并执行其他的相关程序,最终完成系统启动的整个过程。

  系统中每个进程必有一个父进程,相应的,每个进程也可以由零个或者多个子进程。拥有同一个父进程的所有进程被称为兄弟。进程之间的关系存放在进程描述符 task_struct 中。每个 task_struct 都包含一个指向其父进程 task_struct 的指针 parent,还有一个被称为 children 的子进程链表。

一、父进程的访问方法

  对于当前进程,可以使用下面代码访问其父进程,获得其进程描述符:

struct task_struct *my_parent = current -> parent;

  其中,current 是一个宏,在 linux/asm-generic/current.h中有定义:

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H #include <linux/thread_info.h> #define get_current() (current_thread_info()->task)
#define current get_current() #endif /* __ASM_GENERIC_CURRENT_H */

  而 current_thread_info() 函数在 arch/arm/include/asm/thread_info.h 中有定义:

/*
* how to get the thread information struct from C
*/
static inline struct thread_info *current_thread_info(void) __attribute_const__; static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)
(current_stack_pointer & ~(THREAD_SIZE - )); // 让SP堆栈指针与栈底对齐
}

  可以看到,current 实际上是指向当前执行进程的 task_struct 指针的。

二、子进程的访问方法

  可以使用以下方法访问子进程:

struct task_struct *task;
struct list_head *list; list_for_each(list,&current->children){
task = list_entry(list,struct task_struct,sibling);
}

  可以看到,这里使用的是链表相关的操作来访问子进程。我们知道, task_struct 是存放在一个双向循环链表 task_list(任务队列)中的,而一个 task_struct 包含了一个具体进程的所有信息,因此,我们只需要找到子进程的 task_struct 即可以访问子进程了,上面代码就是这么做的。那么,具体是如何找到子进程的进程描述符 task_struct的呢?下面对上面的代码进行详细分析:

  list_head: 在 linux/types.h 中定义

struct list_head{
struct list_head *next,*prev;
};

  显然,list_head 其实就是一个双向链表,而且一般来说,都是双向循环链表。

  list_for_each: 在linux/list.h 中定义

#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

  这是一个宏定义。其中,pos 是指向 list_head 的指针,而 head 是双链表 list_head 中的指针,定义了从哪里开始遍历这个链表。这个宏的作用就是对一个双向循环链表进行遍历。

  list_entry: 在 linux/list.h 中定义,也是一个宏定义

/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

  list_entry 实际上就是 container_of。

  container_of : 在 linux/kernel.h 中定义

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *))->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })

  container_of 实现了根据一个结构中的一个成员变量的指针来获取指向整个结构的指针的功能。其中,offsetof 也是一个宏,它的功能是获得成员变量基于其所在结构的地址的偏移量,定义如下:

#define offsetof(TYPE,MEMBER)  ((size_t) &((TYPE *)0) -> MEMBER)        // 获得成员变量member基于其所在结构的地址的偏移量,该宏在 linux/stddef.h 中有定义

  分析一下 offsetof 宏:

1)、((TYPE *) 0) : 将 0 转换成 TYPE 类型的指针。这声明了一个指向 0 的指针,且这个指针是 TYPE 类型的;

2)、((TYPE *) 0) -> MEMBER: 访问结构中的成员MEMBER,一个指向 0 的 TYPE 类型的结构;显然,MEMBER 的地址就是偏移地址;

3)、&((TYPE *) 0) -> MEMBER :取数据成员MEMBER的地址(不是按位与,不要看错了);

4)、((size_t) &((TYPE *) 0) -> MEMBER): 强制类型转换成 size_t 类型。

Linux内核学习笔记(2)-- 父进程和子进程及它们的访问方法的更多相关文章

  1. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  2. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  3. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  4. 20135316王剑桥Linux内核学习笔记

    王剑桥Linux内核学习笔记 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计算机是如何工作的 个人理 ...

  5. Linux内核学习笔记(1)-- 进程管理概述

    一.进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码.通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个 ...

  6. Linux内核学习笔记(3)-- 进程的创建和终结

    一. 进程创建: Unix 下的进程创建很特别,与许多其他操作系统不同,它分两步操作来创建和执行进程: fork() 和 exec() .首先,fork() 通过拷贝当前进程创建一个子进程:然后,ex ...

  7. Linux内核学习之2号进程kthreadd

    Author       : Toney Email         : vip_13031075266@163.com Date          : 2020.12.04 Copyright : ...

  8. 深入理解Linux内核 学习笔记(3)

    第三章 进程 可以看到很多熟悉的结构体 进程状态: 可运行状态(TASK_ RUNNING) 进程要么在CPU上执行,要么准备执行. 可巾断的等待状态(TASK_ INTERRUPTIBLE) 进程被 ...

  9. 深入理解Linux内核 学习笔记(1)

    1.用户和用户组 每个用户是一个或多个用户组的一名成员,组由唯一的用户组标识符(user group ID)标识.每个文件的相关权限也恰好与一个组相对应. root为超级用户, 2.模块 为了达到微内 ...

随机推荐

  1. Gradle Goodness: Copy Files with Filtering

    Gradle Goodness: Copy Files with Filtering Gradle's copy task is very powerful and includes filterin ...

  2. 用jQuery编写简单九宫格抽奖

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. oracle入门(二)

    ### 一,视图 ```1. 什么是视图    视图是一张虚表, 可以把视图看成表使用(增删改查),视图中没有数据,所有的数据都在基本表中(tables)    封装了一个复杂的DQL    操作视图 ...

  4. 3.3.2Qt的按钮部件

    #include "mywidget.h" #include "ui_mywidget.h" #include <QDebug> #include& ...

  5. 利用Git Bash 远程访问服务器

    首先 先在自己的当前主机打开git bash ssh-keygen 生成密钥对 (默认就好,我自己是一直摁着回车的) cat ~/.ssh/id_rsa.pub 查看生成好的公钥,并复制好 打开你远端 ...

  6. echo、print、print_r、var_dump

    echo(): 可以一次输出多个值,多个值之间用逗号分隔.echo是语言结构(language construct),而并不是真正的函数,因此不能作为表达式的一部分使用. print(): 函数pri ...

  7. Delphi 的TStringBuilder防止服务器内存碎片化

    Delphi 2009+ 的 System.SysUtils提供了一个类似.Net的StringBuilder,用于存储字符数组. 很多人不明白为什么要用TStringBuilder, Delphi中 ...

  8. 与“零值”作比较的 if 语句。

    笔试时候遇到的问题,在此做一下记录. 1.if语句中的布尔变量与零值作比较 不能用布尔变量与true,false,1,0直接作比较.布尔变量类型的语义是:零值为“假”,任何非零值都表示“真”.因为tr ...

  9. R语言(自定义函数、循环语句、管道函数)

    学习R语言半年多了,以前比较注重统计方法上的学习,但是最近感觉一些基础知识也很重要.去年的参考资料是<R语言实战>,今年主要是看视频.推荐网易云课堂里的教程,很多资料都是很良心的~ 目前学 ...

  10. 20155339 《信息安全技术》实验二、Windows口令破解实验报告

    20155339 <信息安全技术>实验二.Windows口令破解实验报告 实验目的 了解Windows口令破解原理 对信息安全有直观感性认识 能够运用工具实现口令破解 系统环境 Windo ...