抽象思想解读Linux进程描述符

来源 | 嵌入式客栈

责编 | Carol

头图 | CSDN 下载自视觉中国

内核是怎么工作的?

首先要理解进程管理,进程调度,本文开始阅读进程管理部分,首先从进程的抽象描述开始。抽象是软件工程的灵魂,而对于Linux操作系统而言,更是将抽象思想体现的淋漓尽致。

本文从抽象建模的角度来对Linux进程描述符进行个人解读,同时也参考了内核文档,一些网络信息。

注:代码基于linux-5.4.31,是一个最新的长期支持稳定版本。

本文为作者个人整理,文中可能有一些解读不到位的地方,真诚请各位擅长此领域的大佬在评论区多多交流指出或讨论,感谢!

进程的基本概念

  • 进程 or 线程 or 任务?

进程:进程是一个正在运行的程序实例,由可执行的目标代码组成,通常从某些硬媒介(如磁盘,闪存等)读取并加载到内存中。但是,从内核的角度来看,涉及很多相关的工作内容。操作系统存储和管理有关任何当前正在运行的程序的其他信息:地址空间,内存映射,用于读/写操作的打开文件,进程状态,线程等。

进程是正在执行的计算机程序的实例。它包含程序代码及其当前活动。取决于操作系统(OS),进程可能由同时执行指令的多个执行线程组成。基于进程的多任务处理使您可以在使用文本编辑器的同时运行Java编译器。在单个CPU中采用多个进程时,使用了各种内存上下文之间的上下文切换。每个过程都有其自己的变量的完整集合。

但是,在Linux中,如果不讨论线程(有时称为轻量级进程),进程的抽象是不完整的。根据定义,线程是流程中的执行上下文或执行流;因此,每个进程至少包含一个线程。包含多个执行线程的进程被称为多线程进程。一个进程中有多个线程可以进行当前编程,并且在多处理器系统上可以实现真正的并行性。

线程:则是某一进程中一路单独运行的程序,也就是说,线程存在于进程之中。一个进程由一个或多个线程构成,各线程共享相同的代码和全局数据,但各有其自己的堆栈。

由于堆栈是每个线程一个,所以局部变量对每一线程来说是私有的。由于所有线程共享同样的代码和全局数据,它们比进程更紧密,比单独的进程间更趋向于相互作用,线程间的相互作用更容易些,因为它们本身就有某些供通信用的共享内存:进程的全局数据。

线程是CPU利用率的基本单位,由程序计数器,堆栈和一组寄存器组成。执行线程是由计算机程序的分支分解为两个或多个同时运行的任务而产生的。线程和进程的实现因一个操作系统而异,但在大多数情况下,线程包含在进程内部。

多个线程可以存在于同一进程中并共享资源(例如内存),而不同进程则不共享这些资源。同一进程中的线程示例是自动拼写检查和写入时自动保存文件。线程基本上是在相同内存上下文中运行的进程。线程在执行时可能共享相同的数据。线程图,即单线程与多线程

任务:是最抽象的,是一个一般性的术语,指由软件完成的一个活动。一个任务既可以是一个进程,也可以是一个线程。简而言之,它指的是一系列共同达到某一目的的操作。与线程非常相似,不同之处在于它们通常不直接与OS交互。

像线程池一样,任务不会创建自己的OS线程。一个任务内部可能有一个线程,也可能没有。例如,读取数据并将数据放入内存中。这个任务可以作为一个进程来实现,也可以作为一个线程(或作为一个中断任务)来实现。在RTOS中,一般会将调度的基本单元称为任务,比如freeRTOS,ucos,embOS等,在RTOS中没有进程的概念。

进程间通讯机制:

  • 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

  • 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数 sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal 函数);

  • 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

  • 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

  • 套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

线程间的同步机制:为啥线程间没有讨论通讯机制?因为同一进程内的线程共享进程的资源。那么资源共享,则需要处理资源共享时的同步问题。

  • 互斥锁(mutex):通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。这部分代码常称为临界区。哪些可能是临界区呢?简言之,多个线程可能竞争访问的资源。以下一些函数是互斥锁的API函数。

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *

全局条件变量(condition variable): 创建一些全局条件变量进行互斥访问控制。以下是其操作的基本接口函数:

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);  
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

信号量(semaphore):如同进程一样,线程也可以通过信号量来实现通信,其基本操作接口API:

int sem_init (sem_t *sem , int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);


进程在内核中如何描述?

Linux中进程描述在./include/linux/sched.h中定义:

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK/* 必须是首个元素  */struct thread_info  thread_info;
#endif/* -1 unrunnable, 0 runnable, >0 stopped: */volatile long   state;/* 前面是与调度密切相关的信息添加在这之前 */randomized_struct_fields_startvoid    *stack;refcount_t   usage;/* Per task flags (PF_*), defined further below: */unsigned int   flags;unsigned int   ptrace;.........
};

该结构非常大,集总抽象了进程的所有信息,包括进程ID,状态,父进程,子进程,同级,处理器寄存器,打开的文件,地址空间等。系统使用循环双向链接列表进行存储 所有过程描述符。

像这样的大型结构肯定会占用大量内存空间。为每个进程提供较小的内核堆栈大小(可以使用编译时选项进行配置,但默认情况下限制为一页,即对于32位体系结构严格为4KB(一个页),对于64位体系结构严格为8KB(两个页) –内核堆栈不具备增长或收缩),以这种浪费的方式使用资源并不是很方便。因此,决定在堆栈中放置一个更简单的结构,并带有指向实际task_struct的指针,从而引申出thread_info。

抽象建模思想看进程描述符

进程首先是操作系统对底层进行抽象而提供面向应用接口的一种抽象,而进程描述符则将底层资源、进程本身的调度从以下几个大的方面进行高级别的抽象封装:

  • 应用程序信息抽象

  • 操作系统资源抽象

  • 调度接口抽象

  • 内存管理抽象

  • 账户信息抽象

  • ......

通过预读进程描述符,个人将进程描述相关信息大致分为以下几个大类抽象:

  • 调度相关抽象

涉及thread_info、优先级、栈、上下文切换、调度相关链表等关键数据。

  • CPU相关抽象

涉及SMP多核处理抽象、CPUSET子系统相关、当前CPU等相关数据抽象。

  • 保护机制抽象

  • 内存管理抽象

  • 缓存相关抽象

参考阅读:https://www.cnblogs.com/20135228guoyao/p/5334985.html

https://blog.csdn.net/wang_xya/article/details/35234429

  • 信号通信抽象

  • 接口相关抽象

  • 调试跟踪抽象

  • 安全机制抽象

  • 资源管理抽象

  • 杂项信息抽象

最后附上些整理搜集到数据域的一些较详细的介绍。

  • thread_info

该字段保存特定于处理器的状态信息,并且是进程描述符的关键元素。具体定义在./arch/xxx/include/asm/thread_info.h中。

  • entry.S需要立即访问此结构的低级任务数据应完全适合一个缓存行,此结构共享主管堆栈页面

  • 如果更改此结构的内容,则还必须更改汇编代码。

  • 因为thread_info包含了当前进程的指针,存储在栈底或栈顶,取决于不同体系架构栈的增长方向,利用thread_info可以快速的访问当前进程的信息,而不必依次遍历。

ARM32的定义:

struct thread_info {unsigned long flags;  /* low level flags */int       preempt_count; /* 0 => preemptable, <0 => bug */mm_segment_t addr_limit; /* address limit */struct task_struct *task;  /* main task structure */__u32   cpu;  /* cpu */__u32   cpu_domain; /* cpu domain */
#ifdef CONFIG_STACKPROTECTOR_PER_TASKunsigned long  stack_canary;
#endifstruct cpu_context_save cpu_context; /* cpu context */__u32   syscall; /* syscall number */__u8   used_cp[16]; /* thread used copro */unsigned long  tp_value[2]; /* TLS registers */
#ifdef CONFIG_CRUNCHstruct crunch_state crunchstate;
#endifunion fp_state  fpstate __attribute__((aligned(8)));union vfp_state  vfpstate;
#ifdef CONFIG_ARM_THUMBEEunsigned long  thumbee_state; /* ThumbEE Handler Base register */
#endif
};

从书上和网上看到都是前面这样描述的,但是对于ARM64的却没有当前进程指针,这是为何呢?没弄明白,有谁知道告诉下我呗。

struct thread_info {unsigned long flags;  /* low level flags */mm_segment_t    addr_limit; /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PANu64   ttbr0;     /* saved TTBR0_EL1 */
#endifunion {u64  preempt_count;    /* 0 => preemptible, <0 => bug */struct {
#ifdef CONFIG_CPU_BIG_ENDIANu32 need_resched;u32 count;
#elseu32 count;u32 need_resched;
#endif} preempt;};
};

利用如下的几种方式,可以获取thread_info信息:

  • static inline struct thread_info *current_thread_info(void)

  • #define GET_THREAD_INFO(reg)

  • ...

  • SLUB 分配器

thread_info实现了进程存储对描述符的引用以及如何访问它们。但是,如果task_struct不是在内核堆栈内部,则task_struct到底位于内存中的什么位置?为此,Linux提供了一种特殊的内存管理机制,称为SLUB层。SLUB动态生成task_struct,并把thread_info存在栈底或栈顶。

  • volatile long state

进程状态,可取的进程状态:

  • TASK_RUNNING:  可执行态

  • TASK_INTERRUPTIBLE:可中断

  • TASK_UNINTERRUPTIBLE:不可中断

  • __TASK_STOPPED:停止态

  • __TASK_TRACED:被其他进程跟踪的进程

为何用volatile修饰。由于内核经常需要从不同位置更改进程的状态,例如,如果在单个CPU硬件上同时将两个进程设置为RUNNABLE。熟悉单片机编程的朋友一定知道,当在中断函数中需要修改以及在中断外部也会被修改的变量,就会使用到volatile修饰变量。

  • randomized_struct_fields_start

这是gcc的一个插件(插件来自于Grsecurity),其作用就是这之后的变量不会按照声明顺序存储在内存中,而会按照一定的随机顺序存放,这样做是基于安全考虑,比如应用程序的进程描述符被劫持,如果按顺序存放,则容易篡改其内容。

今日福利

遇见大咖

由 CSDN 全新专为技术人打造的高端对话栏目《大咖来了》来啦!

CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛携手京东集团技术副总裁、IEEE Fellow、京东人工智能研究院常务副院长、深度学习及语音和语言实验室负责人何晓冬,来也科技 CTO 胡一川,共话中国 AI 应用元年来了,开发者及企业的路径及发展方向!

戳链接或点击阅读原文,直达报名:https://t.csdnimg.cn/uZfQ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS Linux 7.7 安装kafka zookeeper

文章目录一、软件下载1. zookeeper2. kafka二、安装与启动2.1. jdk2.2. zookeeper2.3. kafka三、 kafka 基本演示一、软件下载 1. zookeeper http://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz 2. kafka https://archive.apach…

《Java开发手册》2019最新版发布!

致全球Java开发者&#xff1a; 代码是二进制世界的交流方式&#xff0c;极致的代码是我们的荣耀。 2017年春天&#xff0c;《阿里巴巴Java开发手册》发布&#xff0c;我们希望在涵盖编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程规约、设计规约等7个维度上为开…

IDEA 惊天 bug:进程已结束,退出代码 1073741819

来源 | 沉默王二责编 | Carol头图 | CSDN 下载自视觉中国今天要写的文章中涉及到一串代码&#xff0c;关于 Undertow 的一个入门示例&#xff0c;贴出来大家看一下。public class UndertowTest {public static void main(final String[] args) {Undertow server Undertow.buil…

python3-Anaconda3 基本使用

1、下载 最新版本官网下载&#xff1a; https://www.anaconda.com/distribution/ 历史版本 清华镜像&#xff08;国内首选&#xff09;&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 历史版本 官网镜像&#xff1a; https://repo.anaconda.com/archi…

淘宝应用柔性架构的探索

导读&#xff1a;随着淘宝业务的飞速发展&#xff0c;微服务架构在持续演进的过程中&#xff0c;也受到了越来越多的挑战&#xff1a;如同步模型带来的资源利用率有限、依赖调用并发度有限、下游故障引发应用自身出问题&#xff1b;又如静态限流随着业务代码的演进、依赖拓扑的…

iOS13 一次Crash定位 - 被释放的NSURL.host

每年一次的iOS升级&#xff0c;都会给开发者带来一些适配工作&#xff0c;一些原本工作正常的代码可能就会发生崩溃。 本文讲到了一种 CoreFoundation 对象的内存管理方式在iOS13上遇到的问题。 1. 问题 iOS 13 Beta 版本上&#xff0c;手淘出现了一个必现的崩溃&#xff1a; …

面试官吐槽:“Python程序员就是不行!”网友:我能把你面哭!

最近几年&#xff0c;Python莫名火了起来&#xff0c;很多公司都想赶上这“莫名”的热潮&#xff0c;招聘到大牛人才。但是&#xff0c;最近一个HR在社交网站的吐槽又火了&#xff1a;那么问题来了&#xff0c;市面上为什么鲜有企业满意的优秀的Python程序员&#xff1f;企业到…

python3-pandas 数据结构 Series、DataFrame 基础

Pandas 应用 Pandas 的主要数据结构是 Series &#xff08;一维数据&#xff09;与 DataFrame&#xff08;二维数据&#xff09;&#xff0c;这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。 数据结构 Series 是一种类似于一维数组的对象&#xf…

十年磨一剑 | 淘宝如何打造承载亿级流量的首页?

阿里妹导读&#xff1a;手机淘宝作为整个互联网领域旗舰 APP 之一&#xff0c;装机量和用户访问量都是名列前茅的。而首页作为打开手机淘宝的门面&#xff0c;是淘宝电商领域的主要流量入口和服务消费者的核心阵地&#xff0c;其业务的复杂性之高、系统的稳定性之重都有着极高的…

switchhosts 没有修改hosts的权限解决方案

使用swtichHost工具切换开发环境时候提示没有权限问题&#xff0c;如下图。。 解决方案有两点 1、进入 C:\Windows\System32\drivers\etc右键点击hosts的属性查看 属性的只读是否被勾选了&#xff0c;如果被勾选了将勾选勾去掉 上述完成后以管理员身份运行&#xff08;管理员…

闲鱼亿级商品结构化背后的思考和演进

1. 缘起 闲鱼是一个典型的C2C场景的闲置交易平台。每个在闲鱼的用户都能享受到自由交易的乐趣。在这里&#xff0c;可能你只要简单的输入商品名&#xff0c;商品价格&#xff0c;库存等信息就能完成一个商品的发布。即便是发布以后&#xff0c;你也可以随时修改价格&#xff0…

QingStor NeonSAN跻身四强 新风口下的青云QingCloud正在厚积薄发

人类以日新月异的速度刷新着科技的成果&#xff0c;其中存储的发展历史尤其悠久&#xff0c;堪称万年进化史。自文明诞生以来&#xff0c;我们就一直在寻求能够更有效存储信息的方式&#xff0c;从4万年前的洞穴壁画、6000年前泥板上的楔形文字&#xff0c;到今天正在普及的SSD…

python3-pandas DataFrame 索引、bool索引、pandas 字符串方法

1、DataFrame 索引 1.1 普通索引取值 pandas 取行或者列的注意点&#xff1a; 方括号写数组&#xff0c;表示取行&#xff0c;对行进行操作方括号写字符串&#xff0c;表示取列&#xff0c;对列进行操作 import pandas as pd import numpy as np # pandas 取行或者列的注意…

记一次Cassandra Java堆外内存排查经历

背景 最近准备上线cassandra这个产品&#xff0c;同事在做一些小规格ECS(8G)的压测。压测时候比较容易触发OOM Killer&#xff0c;把cassandra进程干掉。问题是8G这个规格我配置的heap(Xmx)并不高&#xff08;约6.5g&#xff09;已经留出了足够的空间给系统。只有可能是Java堆…

程序员内功修炼系列:10 张图解谈 Linux 物理内存和虚拟内存

来源 | 后端技术学堂责编 | Carol封图 | CSDN 付费下载于视觉中国我们都知道&#xff0c;程序可没这么好骗&#xff0c;任你内存管理把虚拟地址空间玩出花来&#xff0c;到最后还是要给程序实实在在的物理内存&#xff0c;不然程序就要罢工了。所以物理内存这么重要的资源一定要…

阿里高级技术专家方法论:如何写复杂业务代码?

阿里妹导读&#xff1a;张建飞是阿里巴巴高级技术专家&#xff0c;一直在致力于应用架构和代码复杂度的治理。最近&#xff0c;他在看零售通商品域的代码。面对零售通如此复杂的业务场景&#xff0c;如何在架构和代码层面进行应对&#xff0c;是一个新课题。结合实际的业务场景…

Android Studio 安装教程

注意安装之前请配置好java 和 Android SDK 1、下载 官网地址&#xff1a; https://developer.android.google.cn/studio/ 点击下载后&#xff0c;需要同意协议&#xff1a; 2、安装 1、双击程序 2、一路 next&#xff0c;如果想修改路径可自行修改。 3、安装完成点击Fi…

Schedulerx2.0工作流支持数据传输

1. 前言 Schedulerx2.0是阿里中间件自研的基于akka架构的新一代分布式任务调度平台&#xff0c;提供定时、任务编排、分布式跑批等功能&#xff0c;具有高可靠、海量任务、秒级调度等能力。 Schedulerx2.0提供可视化的工作流进行任务编排&#xff0c;该文章将详细介绍如何使用…

应用实时监控 ARMS 上线用户行为回溯功能

随着前端技术日新月异迅猛发展&#xff0c;为了实现更好的前端性能&#xff0c;最大程度提高用户体验&#xff0c;支持单页应用的框架逐渐占领市场&#xff0c;如众所周知的React&#xff0c;Vue等等。但是在单页应用的趋势下&#xff0c;快速定位并解决JS错误却成为一大难题。…

Android Studio 创建第一个Android工程项目

1、打开Android Studio 2、点击 New Project 选择 Empty Activity 3、点击 Next 4、点击 Finish 等待加载完成&#xff0c;取决于网络&#xff0c;第一次加载可能要很久。。。 5、下载完成 可以在右上角切换为 Project 模式 6、创建一个虚拟设备 https://www.bilibili…