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. Java职业规划

    java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈 ...

  2. TCP中的三次握手和四次挥手

    三次握手:目的是同步连接双方的序列号和确认号 并交换 TCP窗口大小信息. 理论上跟通话一样: a: 你听的到吗?  b: 我能听到.只需要两次就可以了,但建立连接阶段不是双向即时通信的,且最终的目的 ...

  3. #leetcode刷题之路24-两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表.你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例:给定 1->2->3->4, 你应该返回 2-> ...

  4. 使用Mybatis连接到Mysql报错,WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be esta

    在Eclipse中使用springboot整合Mybatis,连接到5.7版本Mysql报错WARN: Establishing SSL connection without server's ide ...

  5. querySelectorAll 和 getElementBy 方法的区别

    作者:简生 链接:https://www.zhihu.com/question/24702250/answer/28695133 来源:知乎 1. W3C 标准 querySelectorAll 属于 ...

  6. ElasticSearch优化系列七:优化建议

    尽量运行在Sun/Oracle JDK1.7以上环境中,低版本的jdk容易出现莫名的bug,ES性能体现在在分布式计算中,一个节点是不足以测试出其性能,一个生产系统至少在三个节点以上. ES集群节点规 ...

  7. PHP保存数组到数据库

    数组是 PHP 开发中使用最多的数据类型之一,对于结构化的数据尤为重要. 很多时候我们需要把数组保存到数据库中,实现对结构化数据的直接存储和读取. 其中一个案例就是,对于 Form 提交的多选 che ...

  8. 无缘DELPHI的BUG

    有个很简单的小错误,看一眼好象是DELPHI的BUG,结果找了一个晚上,后面才发现出错还是自己造成的原因. CLIENTDATASET.LOCATE ! 以为它工作出问题了,后来仔细比对,原来有个数据 ...

  9. Keil MDK最新版 5.25介绍及下载地址

    看到Keil MDK又出新版咯,分享给大家 Keil MDK-ARM 5.25 uVision5开发工具下载地址:http://www.myir-tech.com/soft.asp?id=1140 K ...

  10. Kafka消息系统

    一.基本概念 Kafka是一个分布式的.可分区的.可复制的消息系统.它提供了普通消息系统的功能,但具有自己独特的设计. 首先让我们看几个基本的消息系统术语: Kafka将消息以topic为单位进行归纳 ...