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. C# 中的#if、#elif、#else、#endif等条件编译符号 (转载)

    这些是C#中的条件编译符号.这些指令我在项目中遇到过,查过网络,问过人(当然,既不认识大牛,也不认识小牛,所以没什么收获).今天翻看一本资料,有提到这个方面的东西,所以写下来和能看到这篇文章的人一起学 ...

  2. stm32 晶振不起振

    1. STM32f103有内部晶振.刚刚上电时,所有Clock都是源于内部晶振,所以当片内没有程序或内部程序没有使能外部晶振时,外部晶振是不会起振的.2. STM32f103有内部复位电路,只有当检测 ...

  3. C语言入门编程思维引导

    编程思维引导: C语言中 include<stdio.h>  称之为导包,导入写好的函数库,多个则依次写 #define N 3  意思是将N这个字母定义为数字3 当使用的时候就直接用 i ...

  4. jdk 下载地址 服务器

    https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

  5. 网页静态化解决方案-Freemarker

    1.1    技术简介与使用 1.1.1     简介 为什么使用: 1.  减轻数据库的访问压力,静态化比较适合大规模且相对变化不太频繁的数据: 2.  有利于SEO(搜索引擎优化); 纯的HTML ...

  6. iOS:UITableView相关(18-10-20更)

    UITableView用得较多,遇到的情况也较多,单独记录一篇. 一.零散的技巧 二.取cell 三.cell高度 四.导航栏.TableView常见问题相关 五.自定义左滑删除按钮图片 六.仅做了解 ...

  7. Token ,Cookie和Session的区别

    在做接口测试时,经常会碰到请求参数为token的类型,但是可能大部分测试人员对token,cookie,session的区别还是一知半解. Cookie cookie 是一个非常具体的东西,指的就是浏 ...

  8. JavaScript日期格式转换

    //日期格式转换 function dateFormat(val) {//val需要转换的日期 var fmt = "yyyy-MM-dd";//日期格式 val = val.re ...

  9. golang总结-Redis整合

    目录 1. 基本用法 2. Redis连接池 go get github.com/gomodule/redigo/redis 1. 基本用法 获取连接 package conn import ( &q ...

  10. PHP Ajax跨域问题解决办法

    在项目开发中,经常会遇到跨域访问资源,上传图片等,那么这些都怎么解决呢,下面简单介绍一下ajax请求时,解决跨域问题. 原文地址:小时刻个人博客 > http://small.aiweimeng ...