技术笔记:LINUX2.6.32下的进程分析

简介: 技术笔记:LINUX2.6.32下的进程分析

前言:


什么是进程?


   进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体


  狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。


  广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。


Linux下的进程分析:


——1.组织进程——


  Linux中一般使用fork()函数创建进程;


  fork函数


//代码效果参考: http://www.lyjsj.net.cn/wx/art_23384.html

1 #include


2 pid_t fork(void);


  功能:子进程复制父进程。


  特点:调用一次,返回两次。


  一个fork例子:


1 #include


2 #include


3 #include


4 #include


5


6 int main(void)


7 {


8 pid_t pid;//进程id


9 char message;


10 int n;


11 pid = fork();


12 if (pid < 0) { //创建进程失败


13 perror("fork failed");


14 exit(1);


15 }


16 if (pid == 0) { //子进程


17 message = "This is the child\n";


18 n = 6;


19 } else { //父进程


20 message = "This is the parent\n";


21 n = 3;


22 }


23


24 for(; n > 0; n--) {


25 printf(message);


26 sleep(1);


27 }


28 return 0;


29 }


一些与进程相关的函数:


  getpid/getppid


1 #include


2 #include


3


4 pid_t getpid(void); //返回调用进程的PID号


5 pid_t getppid(void); //返回调用进程父进程的PID号


  getuid/geteuid


1 #include


2 #include


3


4 uid_t getuid(void); //返回实际用户ID


5 uid_t geteuid(void); //返回有效用户ID


  getgid/getegid


1 #include


2 #include


3


4 gid_t getgid(void); //返回实际用户组ID


5 gid_t getegid(void); //返回有效用户组ID


  进程的终止


    主要涉及的函数有 _exit() , exit() , atexit() , on_exit()。


    1. 进程的终止分为正常和异常两种。异常终止可能是由于某些信号引起的,其中的一些信号还会导致进程产生一个核心转储文件。


    2. 正常终止可以通过系统调用 _exit() 或者GNU C标准库函数exit() 实现。二者都有一个status参数,用以表示函数退出值。常用的return 函数的效果类似与 exit() 函数(这里是指在带参数的情况下,return不带参数的时候返回值则取决于C语言的版本标准以及所使用的编译器)。


    3. 不管进程正常还是异常终止,内核都会执行多个清理步骤。与系统调用 _exit() 不同的是,调用库函数 exit() 正常终止一个进程时,将会引发执行通过GNU C库函数atexit() 或 on_exit() 注册的退出处理程序(这些退出处理程序在调用函数 _exit() 或者因信号终止的情况下是不会执行的。),然后刷新stdio缓冲区。


——2进程状态转换——


   进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。


  一个进程从创建而产生至撤销而消亡的整个生命期间,有时占有处理器执行,有时虽可运行但分不到处理器、有时虽有空闲处理器但因等待某个事件的发生而无法执行,这一切都说明进程和程序不相同,它是活动的且有状态变化的,这可以用一组状态加以刻画。


     进程状态转换图:


(1)(运行态):进程是可执行的;或者正在执行,或者在运行队列中等待执行。


(2)(可中断睡眠态):进程被阻塞,等待某些条件的完成。一旦完成这些条件,内核就会将该进程的状态设置为运行态。


(3)(不可中断睡眠态):进程被阻塞,等待某些条件的完成。与可中断睡眠态不同的是,该状态进程不可被信号唤醒。


(4)(僵死态):该进程已经结束,但是其父进程还没有将其回收。


(5)(终止态):进程停止执行。通常进程在收到SIGSTOP、SIGTTIN、SIGTTOU等信号的时候会进入该状态。


状态切换的条件:


  就绪---->执行 调度


  执行----->就绪 时间片到


  执行------>等待 等待某个事件发生而睡眠


  等待------->就绪 因等待事情发生而唤醒


——进程调度——


    进程调度的一些方法:


1. 先来先去服务:


概//代码效果参考:http://www.lyjsj.net.cn/wx/art_23382.html

念:

如果早就绪的进程排在就绪队列的前面,迟就绪的进程排在就绪队列的后面,那么先来先服务(FCFS: first come first service)总是把当前处于就绪队列之首的那个进程调度到运行状态。也就说,它只考虑进程进入就绪队列的先后,而不考虑它的下一个CPU周期的长短及其他因素。


要领:


按照进程进入就绪队列的先后顺序调度并分配处理机执行。先来先服务调度算法是一种非抢占式的算法,先进入就绪队列的进程,先分配处理机运行。一旦一个进程占有了处理机,它就一直运行下去,直到该进程完成工作或者因为等待某事件发生而不能继续运行时才释放处理机。


(1)系统只要有按FIFO规则建立的后备作业队列或就绪进程队列即可,就是一个作业控制块JCB或进程控制块PCB加入队列时加在相应队列末尾。


(2)调度退出队列时从相应队列首开始顺序扫描,将相关的JCB或PCB调度移出相应队列。


优点:有利于长作业以及CPU繁忙的作业


缺点:不利于短作业以及I/O繁忙的作业


2. 短作业(进程)优先调度算法SJ(P)F


概念:对预计执行时间短的作业(进程)优先分派处理机.通常后来的短作业不抢先正在执行的作业.


优点:


比FCFS改善平均周转时间和平均带权周转时间,缩短作业的等待时间;


提高系统的吞吐量;


缺点:


对长作业非常不利,可能长时间得不到执行;


未能依据作业的紧迫程度来划分执行的优先级;


难以准确估计作业(进程)的执行时间,从而影响调度性能。


3. 轮转法


概念:让每个进程在就绪队列中的等待时间与享受服务的时间成正比例。


定义:


时间片轮转法类似于“轮流坐庄”的思想,条件是:各作业近似认为“同时”到达,题中条件是后面作业依次比前一个作业迟到一个时间单位,分析时要严格按照RR调度算法的实现思想:系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的对首进程,让它在CPU上运行一个时间片的时间。当进程用完分给它的时间片后,调度程序便停止该进程的运行,并把它放入就绪队列的末尾。


4. 多级反馈队列算法


概念:


设置多个就绪队列,分别赋予不同的优先级,如逐级降低,队列1的优先级最高。每个队列执行时间片的长度也不同,规定优先级越低则时间片越长,如逐级加倍。


新进程进入内存后,先投入队列1的末尾,按FCFS算法调度;若按队列1一个时间片未能执行完,则降低投入到队列2的末尾,同样按FCFS算法调度;如此下去,降低到最后的队列,则按“时间片轮转”算法调度直到完成。


仅当较高优先级的队列为空,才调度较低优先级的队列中的进程执行。如果进程执行时有新进程进入较高优先级的队列,则抢先执行新进程,并把被抢先的进程投入原队列的末尾。


多级反馈队列调度算法又称反馈循环队列或多队列策略,主要思想是将就绪进程分为两级或多级,系统相应建立两个或多个就绪进程队列,较高优先级的队列一般分配给较短的时间片。处理器调度先从高级就绪进程队列中选取可占有处理器的进程,只有在选不到时,才从较低级的就绪进程队列中选取。


优点:


为提高系统吞吐量和缩短平均周转时间而照顾短进程。


为获得较好的I/O设备利用率和缩短响应时间而照顾I/O型进程。


不必估计进程的执行时间,动态调节


1.LINUX下影响进程调度的因素


  Linux的调度是基于分时技术的,给每个可运行进程分配一个时间片,当进程运行结束或时间片到期时,进程就发生切换。在Linux调度算法中,每次进程切换,内核扫描可运行进程链表,计算进程的优先级,有时会用复杂的算法求出进程的当前优先级,选择“最适合”的进程运行(在未说明SMP时,默认为单处理器系统),即每个进程都有一个值与之相关联,这个值用来表示进程如何适当地分配给CPU,也就是进程的优先级。


  例子:LINUX进程调度之CFS算法


  2.1进程权重


在完全公平调度算法中,最重要的是根据优先级确定的权重,以及由权重不同产生的CPU时间。


不同优先级的权重如下:


static constint prio_to_weight【40】 = {


/ -20 / 88761, 71755, 56483, 46273, 36291,


/ -15 / 29154, 23254, 18705, 14949, 11916,


/ -10 / 9548, 7620, 6100, 4904, 3906,


/ -5/ 3121, 2501, 1991, 1586, 1277,


/ 0/ 1024, 820, 655, 526, 423,


/ 5/ 335, 272, 215, 172, 137,


/ 10/ 110, 87, 70, 56, 45,


/ 15/ 36, 29, 23, 18, 15,


};


对应优先级从100-139(也可以说是从nice值-20-19),其权重基准是0对应1024,从0提高到1,权重减少10%,从0到-1,即优先级提高一个等级,权重增加10%的CPU时间。


就绪进程有个权重的总和,当有进程启动或者加入到队列里,系统就会根据每个进程的权重算出总的权重,留待后面使用;反之,当有进程没有就绪则将其权重从总和中减掉,可参考函数inc_nr_running()和dec_nr_running()


  2.2时钟统计


1 static void __sched_fork(struct task_structp)


2


3 {


4


5 p->se.exec_start = 0; //进程开始运行时间


6


7 p->se.sum_exec_runtime = 0;//本次总的运行时间


8


9 p->se.prev_sum_exec_runtime = 0;//上次总的运行时间,该值在调度到另外一个进程时由当前的运行时间赋值,即se->prev_sum_exec_runtime = se->sum_exec_runtime;


10


11 }


12


13 在时钟中断函数中会周期执行task_tick_fair()函数,里面统计一些时间变量。


14


15 static voidupdate_curr(struct cfs_rq cfs_rq)


16


17 {


18


19 struct sched_entity curr =cfs_rq->curr;


20


21 u64 now = rq_of(cfs_rq)->clock; //取得当前时间


22


23 unsigned long delta_exec;


24


25 ……


26


27 delta_exec = (unsigned long)(now -curr->exec_start); //进程执行的时间


28


29 update_curr(cfs_rq, curr, delta_exec);


30


31 curr->exec_start = now; //更新进程开始的运行时间


32


33 …..


34


35 }


36


37


38


39 static inlinevoid


40


41 update_curr(structcfs_rq cfs_rq, struct sched_entity curr,


42


43 unsigned long delta_exec)


44


45 {


46


47 ……


48


49 curr->sum_exec_runtime += delta_exec;


50


51 ……


52


53 delta_exec_weighted = delta_exec;//从时钟计算中得出的运行时间


54


55 if (unlikely(curr->load.weight !=NICE_0_LOAD)) {


56


57 delta_exec_weighted =calc_delta_fair(delta_exec_weighted,


58


59 &curr->load);//考虑了权重以后的运行时间


60


61 }


62


63 curr->vruntime += delta_exec_weighted; //该值判断进程在红黑树中的位置,该值越小,越靠近左边,越容易被重新调度到,反之,越到,越是右移,调度机会变小


64


65 }


66


67


68


69 在时钟周期中统计完时间后,就要判断该进程运行时间是否已经到达分配给它的份额:


70


71 static void


72


73 check_preempt_tick(structcfs_rq cfs_rq, struct sched_entity curr)


74


75 {


76


77 unsigned long ideal_runtime, delta_exec;


78


79


80


81 ideal_runtime = sched_slice(cfs_rq,curr);//计算当前进程允许占用的时间。


82


83 delta_exec = curr->sum_exec_runtime -curr->prev_sum_exec_runtime;


84


85 if (delta_exec > ideal_runtime)


86


87 resched_task(rq_of(cfs_rq)->curr);//设立调度标记


88


89 }


——自己对该操作系统进程模型的看法——


  由于先前接触的操作系统就是windows,对LINUX了解不多,但是开源的LINUX方便初学者更好的深入学习操作系统,也许对LINUX具体模型搭建还不了解,但是对进程的核心有了更多了解


2018-05-01

相关文章
|
18天前
|
Ubuntu Linux Python
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
在Linux系统中,使用Tkinter库时可能会遇到中文显示乱码的问题,这通常是由于字体支持问题导致的,可以通过更换支持中文的字体来解决。
65 0
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
|
5天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
8 1
|
12天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
52 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
16天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
15 1
|
21天前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
20 0
Linux c/c++之IPC进程间通信
|
21天前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
17 0
Linux c/c++进程间通信(1)
|
21天前
|
Linux C++
Linux c/c++之进程的创建
这篇文章介绍了在Linux环境下使用C/C++创建进程的三种方式:system函数、fork函数以及exec族函数,并展示了它们的代码示例和运行结果。
23 0
Linux c/c++之进程的创建
|
21天前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
15 0
|
存储 Unix Linux
浅入分析Linux
Linux 操作系统必须完成的两个主要目的 与硬件部分交互, 为包含在硬件平台上的所有底层可编程部件提供服务 为运行在计算机系统上的应用程序(即所谓的用户空间)提供执行环境 一些操作系统运行所有的用户程序都直接与硬件部分进行交互, 比如典型的MS-DOS。
997 0
|
8天前
|
运维 安全 Linux
Linux中传输文件文件夹的10个scp命令
【10月更文挑战第18天】本文详细介绍了10种利用scp命令在Linux系统中进行文件传输的方法,涵盖基础文件传输、使用密钥认证、复制整个目录、从远程主机复制文件、同时传输多个文件和目录、保持文件权限、跨多台远程主机传输、指定端口及显示传输进度等场景,旨在帮助用户在不同情况下高效安全地完成文件传输任务。
75 5