通过Linux理解操作系统(三):进程管理(下)

通过Linux理解操作系统(三):进程管理(下)

  在前文我们大致了解了程序中如何使用系统调用实现我们想要的进程管理方式,在本文中我们将要看看linux系统内核又是如何实现进程的管理的。正如在概述中讲的,操作系统本身也只是一个计算机程序,只要是程序,就会有数据结构和算法,就同样会利用到内存空间甚至磁盘空间,在往下看之前,读者不妨先根据自己的知识思考一下可以用什么样的方式来实现,说不定就搞出了一个新的系统哦~

1、进程的表示方式

linux将一个个进程抽象为一个个任务,并定义了一个结构体task_struct用于表示一个任务,对于每一个进程,在其生命周期里都会有一个相应task_struct类型的进程描述符存在于内存中,保存了内核用于管理进程所需要的重要信息,一个task_struct包含了以下这些域:

调度参数:进程优先级,已使用的CPU时间,已休眠的时间,用于系统决定调度哪个进程执行;

内存镜像:指向进程text,data,stack内存段或page table的指针;

信号:指定哪些信号将被处理,哪些信号将被忽略等;

寄存器:进程切换至内核模式时,用于保存当前正在运行的寄存器信息;

系统调用状态:保存当前进行系统调用的信息,包括参数,结果等;

文件描述符表:保存进程打开的文件的i-node数据;

统计信息:记录了进程使用的cpu时间,栈空间大小,分页帧数等;

内核栈空间:本进程专属的内核栈空间地址;

其他:当前进程状态,正在等待的事件,进程ID,父进程ID,用户ID等信息。

通过为每一个进程保存以上这些信息,系统内核才得以合理地进行进程的管理。比如当进行进程调度时,内核需要得到每个进程的优先级,来决定要分多少时间片;在进程接收到一个信号时,内核需要查看进程指定了什么方式进行处理,这些所有的信息都需要通过在进程描述符中查找。

由于系统中同时运行了多个进程,内存中也就保存了多个进程描述符,为了方便地管理和支持快速查找,内核维护了一个哈希表,使用PID作键值,采用开散列的方式解决冲突,同一个槽的元素使用双向链表连接,如下图所示:(只是示意图)

通过上面这种存储方式,当内核需要查找一个进程描述符时,只需要将进程的ID映射到哈希表的一个槽中,并在该槽的链表上进行顺序查找即可。接下来我们再看看一个进程在linux系统中是如何创建的,有了以上信息,这个过程就很容易理解了。

当一个fork系统调用执行时,调用fork的进程将切换至内核模式并创建一个task_struct类型的进程描述符(还有其他一些结构,这里就不提了),新创建的进程描述符中的大多数内容将根据父进程的进程描述符进行设置,然后系统分配一个新的PID,并根据这个PID,映射到哈希表里对应的槽,若该槽已被占,则新建一个元素添加到链表里,该元素保存了新的进程描述符的内存地址。接下来,系统再为子进程分配内存空间,并将父进程的内存空间中的内容复制过来,香港服务器租用,这个过程完成之后,子进程便可以开始运行了。

2、进程调度

在了解了进程的表示方式之后,我们再看看linux如何实现进程调度。

(1)关于线程:linux系统的调度是基于线程的,一般将进程视为资源容器,而将线程视为一个执行单元(也就是一段连续,独立的执行过程),事实上前面对应的task_struct是对应了一个线程,一个单线程的进程表示为一个task结构,而一个多线程的进程有多个task结构,每一个线程有一个。(这里可能有点乱,因为linux里进程和线程的概念有些模糊,不像其他系统还区分了进程,轻量级进程和线程,不过我们编程的时候不需要考虑这么多,所以没什么影响)

(2)优先级:linux将线程分成三类:实时FIFO线程、实时轮转线程、普通分时线程

人生的成功不过是在紧要处多一份坚持,

通过Linux理解操作系统(三):进程管理(下)

相关文章:

你感兴趣的文章:

标签云: