linux内核设计与实现(进程管理、进程调度读书笔记)

近期比较清闲,决定要系统的一点一滴地好好学习,首先就从这本书开始,坚持每天都能更新!^_^

     
内核源码树根目录描述:
Directory                Description
 arch                Architecture-specific source
 crypto                  Crypto API
 Documentation       Kernel source documentation
 drivers                 Device drivers
 fs                  The VFS and the individual file systems
 include                 Kernel headers
 init                Kernel boot and initialization
 ipc                 Interprocess communication code
 kernel              Core subsystems, such as the scheduler
 lib                     Helper routines
 mm                  Memory management subsystem and the VM
 net                     Networking subsystem
 scripts             Scripts used to build the kernel
 security                Linux Security Module
 sound                   Sound subsystem
 usr                 Early user-space code (called initramfs)
 
进程管理:

    进程:是处于执行期的程序以及它所包含的资源的总称。
    线程:是在进程中活动的对象。每个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器。内核调度的对象是线程,而不是进程。
进程描述符的结构:task_struct,定义在<linux/sched.h>中,包含一个具体进程的所有信息。在 Linux 中任务和进程是相同的术语,task_struct 就是指 PCB (进程控制块)。
    thread_info结构在文件<asm/thread_info.h>中:
 
struct thread_info {
        struct task_struct    *task;
        struct exec_domain    *exec_domain;
        unsigned long         flags;
        unsigned long         status;
        __u32                 cpu;
        __s32                 preempt_count;
        mm_segment_t          addr_limit;
        struct restart_block  restart_block;
        unsigned long         previous_esp;
        __u8                  supervisor_stack[0];
};

进程状态:
    TASK_RUNNING (运行):无论进程是否正在占用 CPU ,只要具备运行条件,都处于该状态。 Linux 把处于该状态的所有 PCB 组织成一个可运行队列 run_queue ,调度程序从这个队列中选择进程运行。事实上, Linux 是将就绪态和运行态合并为了一种状态。
    TASK_INTERRUPTIBLE (可中断阻塞): Linux 将阻塞态划分成 TASK_INTERRUPTIBLE 、 TASK_UNINTERRUPTIBLE 、 TASK_STOPPED 三种不同的状态。处于 TASK_INTERRUPTIBLE 状态的进程在资源有效时被唤醒,也可以通过信号或定时中断唤醒。
    TASK_UNINTERRUPTIBLE (不可中断阻塞):另一种阻塞状态,处于该状态的进程只有当资源有效时被唤醒,不能通过信号或定时中断唤醒。
    TASK_STOPPED (暂停):第三种阻塞状态,进程被停止,通常是通过接收一个信号(SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU )。正在被调试的进程可能处于停止状态。处于该状态的进程只能通过其他进程的信号才能唤醒。
    TASK_ZOMBILE (僵死):进程已结束但尚未消亡,已经释放了大部分资源, PCB 仍未被释放,在task数据中仍然保留task_struct结构。一旦父进程调用了wait4(),进程描述符就会被释放。 

设置当前进程状态:
           set_task_state(task, state);        /* set task 'task' to state 'state' */

获取进程描述符:
    e.g: 获得父进程的进程描述符:
     struct task_struct *my_parent = current->parent;
    e.g: 依次访问子进程:
     struct task_struct *task;
     struct list_head *list;

     list_for_each(list, &current->children) {
        task = list_entry(list, struct task_struct, sibling);
        /* task now points to one of current's children */
}
    e.g:获取链表中的下一个进程:
     list_entry(task->tasks.next, struct task_struct, tasks)
        获取链表中的前一个进程:
     list_entry(task->tasks.prev, struct task_struct, tasks)

宏for_each_process(task),提供了依次访问整个任务队列的能力,
e.g:
struct task_struct *task;

for_each_process(task) {
        /* this pointlessly prints the name and PID of each task */
        printk("%s[%d]\n", task->comm, task->pid);
}


进程创建:
    fork() 和 exec()
    fork():通过拷贝当前进程创建一个子进程。
    exec():负责读取可执行文件并将其载入地址空间开始运行。
进程终结:
    调用exit()。


进程调度:

    时间片:表明进程在被抢占前所能持续运行的时间。
linux的调度程序定义于kernel/sched.c中。
可执行队列:
struct runqueue {
        spinlock_t          lock;   /* spin lock that protects this runqueue */
        unsigned long       nr_running;         /* number of runnable tasks */
        unsigned long       nr_switches;        /* context switch count */
        unsigned long       expired_timestamp;    /* time of last array swap */
        unsigned long       nr_uninterruptible;   /* uninterruptible tasks */
        unsigned long long  timestamp_last_tick;  /* last scheduler tick */
        struct task_struct  *curr;                /* currently running task */
        struct task_struct  *idle;           /* this processor's idle task */
        struct mm_struct    *prev_mm;        /* mm_struct of last ran task */
        struct prio_array   *active;         /* active priority array */
        struct prio_array   *expired;        /* the expired priority array */
        struct prio_array   arrays[2];       /* the actual priority arrays */
        struct task_struct  *migration_thread; /* migration thread */
        struct list_head    migration_queue;   /* migration queue*/
        atomic_t            nr_iowait; /* number of tasks waiting on I/O */
};


优先级数组:
struct prio_array {
        int               nr_active;         /* number of tasks in the queues */
        unsigned long     bitmap[BITMAP_SIZE];  /* priority bitmap */
        struct list_head  queue[MAX_PRIO];      /* priority queues */
};

schedule() 函数
    schedule( ) 是 Linux 系统实现进程调度的函数,因此也被称为调度器。作用是选择一个进程运行。当内核代码想要休眠时,会直接调用该函数,另外,如果有哪个进程将被抢占,那么该函数也会被唤起执行。每个CPU都要对下一次该运行哪个进程做出自己的判断。
    The following code determines the highest priority task:
struct task_struct *prev, *next;
struct list_head *queue;
struct prio_array *array;
int idx;

prev = current;
array = rq->active;
idx = sched_find_first_bit(array->bitmap);
queue = array->queue + idx;
next = list_entry(queue->next, struct task_struct, run_list);
说明:函数sched_find_first_bit()提供快速查找算法。

effective_prio() 函数可以返回一个进程的动态优先级。


睡眠和唤醒:

/* 'q' is the wait queue we wish to sleep on */
DECLARE_WAITQUEUE(wait, current);

add_wait_queue(q, &wait);
while (!condition) {     /* condition is the event that we are waiting for */
        set_current_state(TASK_INTERRUPTIBLE); /* or TASK_UNINTERRUPTIBLE */
        if (signal_pending(current))
                /* handle signal */
        schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(q, &wait);
进程通过执行下面步骤将自己加入到一个等待队列中:
1) 调用DECLARE_WAITQUEUE()创建一个等待队列的项;
2) 调用add_wait_queue()把自己加入到等待队列中。该队列会在进程等待的条件满足时唤醒它。在其他地方写相关代码,在事件发生时,对等的队列执行wake_up()操作。
3) 将进程状态变更为: TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE。
4) 如果状态被置为TASK_INTERRUPTIBLE ,则信号唤醒进程。即为伪唤醒(唤醒不是因为事件的发生),因此检查并处理信号。
5) 检查condition是否为真,为真则没必要休眠,如果不为真,则调用scheduled()。
6) 当进程被唤醒的时候,它会再次检查条件是否为真。真就退出循环,否则再次调用scheduled()并一直重复这步操作。
7) condition满足后,进程将自己设置为TASK_RUNNING 并通过remove_wait_queue()退出。

负载平衡程序:
    作用:它负责保证可执行队列间的负载处于均衡状态。它会拿当前处理器的可执行队列和系统中的其它可执行队列做比较。若不均衡,就会把相对繁忙的队列中的进程抽到当前的可执行队列中来。
函数load_balance()实现:
操作步骤:
1) load_balance()调用find_busiest_queue() ,找到最繁忙的可执行队列,即该队列中的进程数目最多;
2) load_balance()从最繁忙的队列中选择一个优先级数组以便抽取进程,最好是过期数组。
3) 接着,load_balance()寻找到含有进程并且优先级最高(值最小)的链表,要把优先级高的进程平均分散开来。
4) 分析找到的所有这些优先级相同的进程,选择一个不是正在运行,也不会因为CPU相关性而不可移动,并且不在高速缓存中的进程。若满足,调用pull_task()将其从最繁忙的队列中抽取到当前队列。
5) 仍不平衡,重复上面两个步骤。

static int load_balance(int this_cpu, runqueue_t *this_rq,
                        struct sched_domain *sd, enum idle_type idle)
{
        struct sched_group *group;
        runqueue_t *busiest;
        unsigned long imbalance;
        int nr_moved;

        spin_lock(&this_rq->lock);

        group = find_busiest_group(sd, this_cpu, &imbalance, idle);
        if (!group)
                goto out_balanced;

        busiest = find_busiest_queue(group);
        if (!busiest)
                goto out_balanced;

        nr_moved = 0;
        if (busiest->nr_running > 1) {
                double_lock_balance(this_rq, busiest);
                nr_moved = move_tasks(this_rq, this_cpu, busiest,
                                      imbalance, sd, idle);
                spin_unlock(&busiest->lock);
        }
        spin_unlock(&this_rq->lock);

        if (!nr_moved) {
                sd->nr_balance_failed++;

                if (unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2)) {
                        int wake = 0;

                        spin_lock(&busiest->lock);
                        if (!busiest->active_balance) {
                                busiest->active_balance = 1;
                                busiest->push_cpu = this_cpu;
                                wake = 1;
                        }
                        spin_unlock(&busiest->lock);
                        if (wake)
                                wake_up_process(busiest->migration_thread);
                        sd->nr_balance_failed = sd->cache_nice_tries;
                }
        } else
                sd->nr_balance_failed = 0;

        sd->balance_interval = sd->min_interval;

        return nr_moved;

out_balanced:
        spin_unlock(&this_rq->lock);

        if (sd->balance_interval < sd->max_interval)
                sd->balance_interval *= 2;

        return 0; 
}

转载于:https://www.cnblogs.com/parrynee/archive/2010/01/14/1648155.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值