Linux operating system process model analysis process

Introduction to Linux Operating System

Linux has the functions of modern operating systems, such as true preemptive multitasking, support for multi-user memory, protection of virtual memory, support for SMP, UP, POSIX standard networking, graphical user interface and desktop environment with fast, stable and other characteristics . By analyzing the Linux kernel source code, this article explains how Linux, as the operating system kernel, organizes, converts, and schedules processes.

organization of the process

Processes exist in the form of PCBs in the kernel. An operating system has many processes, that is, many PCBs exist at the same time. How to effectively manage the organization of processes is obviously a very important issue. In order to traverse all processes in the kernel , the kernel directly links these PCBs with a doubly linked list, and the tasks field in the task_struct structure is used to connect all process descriptors. In this chain, the head of the linked list is the init_task descriptor, which is the descriptor of the so-called 0 process or the swapper process, and the init_task.tasks.prev field points to the tasks field of the last inserted process descriptor in the linked list.

struct task_struct { 
struct list_head run_list;
struct list_head tasks; 
struct task_struct *real_parent; /*real parent process (when being debugged) */
struct task_struct *parent; /* parent process */
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
/* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group;
};

From the fields in the above source code, you can see how Linux organizes processes.

Field run_list:
There is a queue in the kernel that is a runnable queue, that is, the process PCB whose process status is: TASK_RUNNING is saved, this queue is the run_list field, and the kernel sets a runnable queue for each priority, that is, if a The priority of the process is k (the value range is 0 to 139), then the kernel links the process to the runnable queue with priority k, so that the CPU can select the runnable queue from the high-priority cocoa runnable queue. process, which improves efficiency, but requires splitting all runnable process queues into 140 different queues. These queues are implemented by a separate data structure prio_array_t data structure, defined as follows (taken from linux2.6.11):

typedef struct prio_array prio_array_t;
struct prio_array {
         unsigned int nr_active;
         unsigned long bitmap[BITMAP_SIZE];
         struct list_head queue[MAX_PRIO];
};

Fields children, sibling and thread_group:
These three fields are the child process linked list, sibling linked list and thread group linked list of the process respectively. Processes of a process family can be found based on the children and sibling fields. After the process is created, it must be in a certain group, and a process group is formed through thread_group.

 

process transition

The process switching process in Linux is basically similar to the function call stack

void my_schedule(void)
{
    tPCB * next;
    tPCB * prev;

    if(my_current_task == NULL 
        || my_current_task->next == NULL)
    {
        return;
    }
    printk(KERN_NOTICE ">>>my_schedule<<<\n");
    /* schedule */
    next = my_current_task->next;
    prev = my_current_task;
     /* Process context switch, similar to function call stack */ 
    if (next->state == 0 ) /* -1 unrunnable, 0 runnable, >0 stopped */
    {
        /* switch to next process */ 
        asm volatile (
             /* save current process ebp esp */   
            " pushl %%ebp\n\t "        /* save ebp */ 
            " movl %%esp,%0\n\t "      /* save esp */ 
            /* Build the esp of the next process */ 
            " movl %2,%%esp\n\t "      /* restore esp */ 
            /* $1f refers to the position labeled 1 below, which is the next The location where a process starts */ 
            " movl $1f,%1\n\t "        /* save eip */  
            /* The next process eip is pushed on the stack */ 
            " pushl %3\n\t"  
            /* Restore the scene: eip points to the address of the next process, ebp is restored to the ebp saved by the first instruction push stack */ 
            " ret\n\t "                /* restore eip */ 
            " 1:\t "                   /* next process start here */ 
            " popl %%ebp\n\t " 
            : " =m " (prev->thread.sp ), " =m " (prev-> thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        );
        my_current_task = next; 
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);      
    }
    else
    {
        next ->state = 0 ; // The new process is set to running state 
        my_current_task = next;
        printk(KERN_NOTICE " >>>switch %d to %d<<<\n " ,prev->pid,next-> pid);
         /* switch to new process */ 
        asm volatile (   
             " pushl %%ebp\n\ t "        /* save ebp */ 
            " movl %%esp,%0\n\t "      /* save esp */ 
            /* The new process has never been executed, so the stack is empty esp and ebp point to the same location */ 
            " movl %2,%%esp\n\t "      /* restore esp */ 
            " movl %2,%%ebp\n\t "     /* restore  ebp */
            "movl $1f,%1\n\t"       /* save eip */ 
            "pushl %3\n\t" 
            "ret\n\t"               /* restore  eip */
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        );          
    }   
    return; 
}

Switching process:

1. Save the stack environment (esp, eip, ebp) of the current process, first push the ebp of the current process on the stack, and save the esp and eip in the structure of the current process

2. Build the stack environment for the next process. Then the thread.sp of the next process is read and assigned to esp; thread.ip is read and assigned to eip. If the next process is a new process and is not running, the new process stack is empty, and esp and ebp point to the same address, which is more similar to the function call stack. If the next process is running, then ebp points to the same address as the ebp of the current process.

3. Restore the stack environment and prepare for the next process. To restore eip, that is, assign the thread.ip of the completed process (the instruction address of the next process) to eip. In the above example, the thread.ip of all processes is set to my_process.

as the picture shows:

 

 

 

process scheduling

The Linux system process provides two priorities, one is the normal process priority, and the second is the real-time priority. The former applies the SCHED_NORMAL scheduling policy, and the latter selects the SCHED_FIFO or SCHED_RR scheduling policy. At any time, the priority of the real-time process is higher than that of the ordinary process , and the real-time process will only be preempted by the higher-level real-time process. of.

Linux process state machine:

 

personal opinion

When using a mobile phone or computer on a daily basis, often after closing or exiting the application, there are still many processes running in the background, which made me curious about the existence of the process. It is now understood that not only processes will be generated when the application is running, but the computer system itself will also generate many processes during the running process. It is these processes that allow the computer to call many functions and interact with people. Through the exploration of the Linux operating system process, I have a further understanding of the process, understand why the computer can run so many programs at the same time, good process organization, conversion and calling mode can greatly improve the performance of the computer, Help us do our job better.

 

Guess you like

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