理解进程和线程

    进程和线程是操作系统里很重要的概念,但是所有的东西都会落实到代码。看起来很复杂的进程线程,其实在操作系统的代码里。也只是一些数据结构和算法。只不过他比一般的数据结构和算法可能复杂点。但是学习方法还是一样的,就是深入源码,一探究竟。
    进程在操作系统里,是用一个task_struct结构体表示的。因为操作系统是大部分是用c语言实现的,没有对象这个概念。如果我们用高级语言来理解的话,每个进程就是一个对象。每次新建一个进程,就是新建一个对象。task_struct结构体可以说是类的定义。我们看一下一个task_struct的定义。
在这里插入图片描述
我们看到task_struct结构体里其实就是保持了一个进程所需要的一些信息,报包括执行状态,执行上下文tss,打开的文件、根目录、工作目录、收到的信号、信号处理函数、代码段、数据段的位置,进程id,执行时间、退出码等等。我们具体看一下这些数据结构的作用。

进程的管理

操作系统里会维护一个task_struct数组或者链表来记录当前系统中所有的进程。每次新建一个进程的时候,就会往里面追加一个task_struct结构体,每次销毁一个进程的时候,该进程的父进程会删除对应的task_struct。

进程的调度

操作系统的运作,很大程度上是由时钟驱动的,电脑中会有一个硬件间歇性地产生时钟中断。中断间隔是由操作系统初始化该硬件时决定的(定时器也是由该硬件驱动的,我们在应用层使用的定时器功能,归根到底还是使用系统的定时器去实现的)。每次时钟中断的时候如果当前执行的进程时间片已到,则会发生进程调度。另外进程阻塞的时候,也会发送进程调度。被调度到的进程,系统就会把task_struct里的tss信息加载到cpu。包括当前执行的代码位置,各种寄存器的值。然后就完成了进程的切换。

进程的执行时间

每次时钟中断的时候,时钟中断处理程序都会累加当前进程的执行时间,我们平时查看的进程的执行时间,这些数据就是由这些字段记录的。一个进程在内核态和用户态下执行的时间,是分开计算的。

信号

task_struct中用三个字段实现了信号相关的功能,一个是signal,记录了进程收到的信号,按位计算。blocked就是记录当前进程不接收哪些信号。sigaction则是记录每个信号对应的处理函数,和signal一一对应。每次我们调用kill的时候,其实就是修改signal字段的值。然后在某些时机下,系统会执行sigaction里对应的函数。这些时机包括系统调用返回,时钟中断处理程序返回、还有其他的硬件中断返回等等。

状态

task_struct用一个字段state记录了进程当前的状态,exit_code记录进程退出时的退出码。

文件系统相关

当前进程的根目录、工作目录。我们平时在进程里打开一个文件的时候,如果没有写明绝对路径,系统就会以工作目录为基础,加上我们传的相对路径拼出绝对路径。从而找到文件。另外filp字段是维护进程打开的文件信息。我们平时拿到的文件描述符就是filp字段的索引。他会逐步找到底层对应的文件或者socket。executable是保存进程对应的二进制文件所在的文件信息。我们都知道程序加载到内存变成进程。executable保存的就是程序对应的文件的信息。

权限

task_struct里用uid、euid、gid、egid等字段记录进程的权限信息。

进程关系信息

pid字段记录了当前进程的id,father记录了父进程的id。pgrp,session,leader分别是组id,会话id,是不是会话leader。多个进程组成一个组,多个组组成一个会话。如果一个进程是这个组或者会话的leader,则他的id会成为组或者会话的id,比如
组1有进程a的id是1(组leader、会话leader)进程b的id是2。
组2有进程c的id是3(组leader),进程d的id是4。
所有进程在一个会话,则组1的所有进程的组id和会话id都是1。组2所有进程的组id是3,会话id是1。

执行上下文

tss_struct和desc_struct结构体记录了进程执行的上下文,每次进程切换的时候,如果是被调度执行,则上下文加载到cpu和对应的硬件中,如果是被挂起,则cpu和硬件的信息保存到上下文。下次执行的时候恢复。

以上就是一个进程所具有的一些属性。我们发现,进程也没有那么难以理解,好比我们平时定义一个人,他有名字,身高,年龄属性一样。每个对象,他都有属于自己的一些属性。

下面我们再来看一下线程。相比进程,线程对很多同学来说可能更难理解。其实对于操作系统来说,没有单独去实现线程这个概念,操作系统把进程和线程抽象成执行上下文。可以说他们是一个东西。但是他们又有一点点区别。我们以linuxthreads线程库为例。了解一下线程是什么。我们知道fork可以新建一个进程。但是这个进程太重了,尽管有些属性是可以共享的。所以操作系统重新实现了一个系统调用clone。他支持更细粒度的属性共享。所以我们称线程是轻量级的进程。顾名思义,线程也是进程,但是他是轻量级的,因为他很多属性都是共享于父进程(父线程)的。共享的属性可以通过clone函数的参数来控制。当我们新建一个线程的时候,系统里会新建一个进程。clone函数会把栈的位置和代码执行的位置(就是我们传进去的函数)告诉系统。系统会从我们定义的函数开始执行。大概如下。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值