The first assignment: In-depth source code analysis process model (Linux 0.01)

I. Introduction

Based on the Linux0.01 source code, this article analyzes the process model in the Linux system, including the introduction, organization, conversion, scheduling, etc. of the process.

Linux0.0.1 source code download address:

https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/

 

Second, the process introduction

1. The composition of the process: program segment, related data segment, process control block (PCB) (the only sign of the existence of the process).

2. The characteristics of the process: dynamic, concurrent, independent, asynchronous, and structural.

3. The basic functions of the process: process creation, process running, process termination, process scheduling

 

The organization of the process

From the perspective of the system kernel, a process is just an item in the process control table. Each item in the process control table is a task_struct structure:

struct task_struct {
/* these are hardcoded - don't touch */
    long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
    long counter;
    long priority;
    long signal;
    fn_ptr sig_restorer;
    fn_ptr sig_fn[32];
/* various fields */
    int exit_code;
    unsigned long end_code,end_data,brk,start_stack;
    long pid,father,pgrp,session,leader;
    unsigned short uid, euid, suid;
    unsigned short gid,egid,sgid;
    long alarm;
    long utime,stime,cutime,cstime,start_time;
    unsigned short used_math;
/* file system info */
    int tty;        /* -1 if no tty, so it must be signed */
    unsigned short umask;
    struct m_inode * pwd;
    struct m_inode * root;
    unsigned long close_on_exec;
    struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
    struct desc_struct ldt[3];
/* tss for this task */
    struct tss_struct tss;
};

 The process control table is both an array, a doubly linked list, and a tree at the same time. Its physical implementation is a static array of pointers.

After the system is started, the kernel usually acts as a representative of a process, and a global pointer variable current pointing to the task_struct is used to record the running process, which can only be changed by the process scheduling.

 

Fourth, the process state conversion

In the include/Linux/sched.h file in the Linux0.01 kernel, five different states of the process are defined:

#define TASK_RUNNING  0 
#define
TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE  2 #define TASK_ZOMBIE  3 #define TASK_STOPPED  4

The corresponding functions are shown in the table:

Process state in Linux 0.01
TASK_RUNNING
Indicates that the process is in the "Ready List", and this process has obtained all other resources except the CPU
TASK_INTERRUPTIBLE The process is sleeping, waiting for a signal or a resource (Sleeping)
TASK_UNINTERRUPTIBLE The process is waiting for a resource, the current process is in the "Wait Queue"
TASK_ZOMBIE Zombie process (child process without parent)
TASK_STOPPED Identifies that the process is being debugged

The description of the various state switching relationships of the processes in the table is shown in the following figure:

 

 

Five, process scheduling

 The process in the TASK RUNNING state is moved to the run queue (run queue), and the schedule() function CPU scheduling algorithm will be selected to run at the appropriate time and assigned to the CPU. What the scheduler does is roughly like this:

The current process must be processed before the scheduling function selects another process.

If the scheduling policy of the current process is ring, it is placed at the end of the run queue.

If the task status is TASK_INTERRUPTIBLE and it received a signal last time it was scheduled, its status becomes TASK_RUNNING; if the current process times out, its status becomes TASK_RUNNING; if the current process status is RUNNING, it keeps this status; Processes that are not RUNNING or INTERRUPTIBLE will be removed from the run queue. This means that processes other than RUNNING and INTERRUPTIBLE are not considered when the scheduler finds the most worthwhile processes to run.

The scheduler looks at the processes in the run queue, looking for the best process to run on. If there is a real-time process (with a real-time scheduling policy), it will be heavier than a normal process.

If the best process to run is not the current process, the current process must be suspended so that the new process can run.
schedule() function:

void schedule(void)
{
    int i,next,c;
    struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
        if (*p) {
            if ((*p)->alarm && (*p)->alarm < jiffies) {
                    (*p)->signal |= (1<<(SIGALRM-1));
                    (*p)->alarm = 0;
                }
            if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE)
                (*p)->state=TASK_RUNNING;
        }

/* this is the scheduler proper: */

    while ( 1 ) {                 // This loop is to obtain the task with the highest counter of the task, as the task to be switched 
        c = - 1 ;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
        while (--i) {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                c = (*p)->counter, next = i;
        }
        if (c) break ;             // If all task counters are the same and all are 0, then execute the following code to reset the counter of each task according to the order of tasks and the size of permissions 
        for (p = &LAST_TASK ; p > &FIRST_TASK ; -- p)
             if (* p)
                (*p)->counter = ((*p)->counter >> 1) +
                        (*p)->priority;
    }
    switch_to(next);             // Switch task 
}

Other states of the implementation process in sched.c:

void sleep_on(struct task_struct **p)    //进程休眠
{
    struct task_struct *tmp;

    if (! p)
         return ;
     if (current == &(init_task.task))         // initialization task cannot sleep 
        panic( " task[0] trying to sleep " );
    tmp = *p;
    *p = current;
    current->state = TASK_UNINTERRUPTIBLE;
    schedule();
    if (tmp)
        tmp ->state= 0 ;             // Let the process of **p enter the running state 
}

void interruptible_sleep_on(struct task_struct **p)
{
    struct task_struct *tmp;

    if (!p)
        return;
    if (current == &(init_task.task))
        panic("task[0] trying to sleep");
    tmp=*p;
    *p=current;
repeat:    current->state = TASK_INTERRUPTIBLE;
    schedule();
    if (*p && *p != current) {
        ( **p).state= 0 ;         // Wake up other processes first 
        goto repeat;
    }
    *p=NULL;
    if (tmp)
        tmp ->state= 0 ;             // Wake up the specified process 
}

void wake_up(struct task_struct **p)
{
    if (p && *p) {
        (**p).state=0;
        *p=NULL;
    }
}

 

6. Views and impressions

The process model of Linux 0.01 is very simple, and the scheduling is also very streamlined, but in the process of continuous development of Linux, this initial version has long been unable to meet the needs of users. The current scheduling algorithm has been optimized much more than the original algorithm. On the basis of fair use of CPU, the priority of different processes can also be considered, giving the entire system a better user experience. Updates never stop and there is still a lot to learn.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325117382&siteId=291194637