重庆邮电大学移通学院毕业设计(论文) 第1章 Linux进程相关内核代码简要分析
就绪进程。每一个就绪进程都用一个struct prio_array的结构表示。
1.4.3 runqueque结构分析[3]
1. spinlock_t lock
runqueue 的自旋锁,当需要对 runqueue 进行操作时,仍然应该锁定,但这个锁定操作只影响一个 CPU 上的就绪队列,因此,竞争发生的概率要小多了。
2.task_t *curr
本 CPU 正在运行的进程。 3.tast_t *idle
指向本 CPU 的 idle 进程,相当于 2.4 中 init_tasks[this_cpu()] 的作用。 4. int best_expired_prio
记录 expired 就绪进程组中的最高优先级(数值最小)。该变量在进程进入 expired 队列的时候保存(schedule_tick()),用途见 \的解释)。
5.unsigned long expired_timestamp
当新一轮的时间片递减开始后,这一变量记录着最早发生的进程耗完时间片事件的时间(jiffies 的绝对值,在 schedule_tick() 中赋),它用来表征 expired 中就绪进程的最长等待时间。它的使用体现在 EXPIRED_STARVING(rq) 宏上。
上面已经提到,每个 CPU 上维护了两个就绪队列,active 和 expired。一般情况下,时间片结束的进程应该从 active 队列转移到 expired 队列中(schedule_tick()),但如果该进程是交互式进程,调度器就会让其保持在 active 队列上以提高它的响应速度。这种措施不应该让其他就绪进程等待过长时间,也就是说,如果 expired 队列中的进程已经等待了足够长时间了,即使是交互式进程也应该转移到 expired 队列上来,排空 active。这个阀值就体现在EXPIRED_STARVING(rq) 上:在 expired_timestamp 和 STARVATION_LIMIT 都不等于 0 的前提下,如果以下两个条件都满足,则 EXPIRED_STARVING() 返回真:
1).(当前绝对时间 - expired_timestamp) >= (STARVATION_LIMIT * 队列中所
11
重庆邮电大学移通学院毕业设计(论文) 第1章 Linux进程相关内核代码简要分析
有就绪进程总数 + 1),也就是说 expired 队列中至少有一个进程已经等待了足够长的时间;
2).正在运行的进程的静态优先级比 expired 队列中最高优先级要低(best_expired_prio,数值要大),此时当然应该尽快排空 active 切换到expired 上来。
6.struct mm_struct *prev_mm
保存进程切换后被调度下来的进程(称之为 prev)的 active_mm 结构指针。 7.unsigned long nr_running
本 CPU 上的就绪进程数,该数值是 active 和 expired 两个队列中进程数的总和,是说明本 CPU 负载情况的重要参数。
8. unsigned long nr_switches
记录了本 CPU 上自调度器运行以来发生的进程切换的次数。 9.unsigned long nr_uninterruptible
记录本 CPU 尚处于 TASK_UNINTERRUPTIBLE 状态的进程数,和负载信息有关。
10.atomic_t nr_iowait
记录本 CPU 因等待 IO 而处于休眠状态的进程数。 11.unsigned long timestamp_last_tick
本就绪队列最近一次发生调度事件的时间,在负载平衡的时候会用到。 12.int prev_cpu_load[NR_CPUS]
记录进行负载平衡时各个 CPU 上的负载状态。
13.atomic_t *node_nr_running; int prev_node_load[MAX_NUMNODES] 这两个属性仅在 NUMA 体系结构下有效,记录各个 NUMA 节点上的就绪进程数和上一次负载平衡操作时的负载情况。
14.task_t *migration_thread
指向本 CPU 的迁移进程。每个 CPU 都有一个核心线程用于执行进程迁移操作。
15. struct list_head migration_queue
12
重庆邮电大学移通学院毕业设计(论文) 第1章 Linux进程相关内核代码简要分析
需要进行迁移的进程列表。
1.5 进程的撤销[3]
进程销毁可以通过几个事件驱动 — 通过正常的进程结束、通过信号或是通过对 exit 函数的调用。不管进程如何退出,进程的结束都要借助对内核函数 do_exit(在 ./linux/kernel/exit.c 内)的调用。
do_exit 的目的是将所有对当前进程的引用从操作系统删除(针对所有没有共享的资源)。销毁的过程先要通过设置 PF_EXITING 标志来表明进程正在退出。内核的其他方面会利用它来避免在进程被删除时还试图处理此进程。将进程从它在其生命期间获得的各种资源分离开来是通过一系列调用实现的,比如 exit_mm(删除内存页)和 exit_keys(释放线程会话和进程安全键)。do_exit 函数执行释放进程所需的各种统计,这之后,通过调用 exit_notify 执行一系列通知(比如,告知父进程其子进程正在退出)。最后,进程状态被更改为 PF_DEAD,并且还会调用 schedule 函数来选择一个将要执行的新进程。请注意,如果对父进程的通知是必需的(或进程正在被跟踪),那么任务将不会彻底消失。如果无需任何通知,就可以调用 release_task 来实际收回由进程使用的那部分内存。
13
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
第2章Linux多进程编程
2.1 进程的创建[1][2][3][7][15]
2.1.1 fork()[1][3][15]
fork()系统调用是随着UNIX的诞生一起诞生的,fork()系统调用是众多UNIX经典的系统掉用中出类拔萃的一个,几乎所有的UNIX及类UNIX系统中都有fork()。fork在英文中的意义为分枝,显而易见fork()是所有类UNIX系统中最常用的进程创建系统调用。
头文件: #include
函数定义:pid_t fork( void ); (pid_t 是一个宏定义,其实质是int 被定义在#include
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1 。
函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。
为什么fork会返回两次?由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因为fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。
实例:
fork_test.c
#include
14
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
#include
int child(int a) /*子进程函数*/ {
int i;
for(i=a;i<20;i++) { printf(\ sleep(1); if(i==6) { exit(0); } }
return 0; }
int main(void) {
pid_t pid; /* 定义子进程PID */ int i;
pid=fork(); /*获得子进程PID*/ int a=3; if(pid<0) { printf(\ }
else if(pid==0) /*创建子进程*/ { child(3);/*子进程代码*/ } else { for(i=0;i<10;i++)/*父进程代码*/ { printf(\ sleep(1); } }
return 0; }
程序输出:
i am the parent process 0
15