Experiment 4 - tracking and statistics of process running track

1, Experimental purpose

  1. Master multi process programming technology under Linux;
  2. Visualize the concept of process by tracking the running track of process;
  3. The algorithm can be further compared with the actual scheduling process on the basis of quantitative data, so as to further understand the scheduling algorithm and obtain the actual operation experience.

2, Experimental content

  1. Based on template process C write a multi process sample program to realize the following functions:

    1. All sub processes run in parallel, and the actual running time of each sub process generally does not exceed 30 seconds;

    2. The parent process prints the IDs of all child processes to the standard output, and exits only after all child processes exit;

  2. In Linux 0 11 to track the running track of the process.

    1. The basic task is to maintain a log file / var / process in the kernel Log, which records the running tracks of all processes from the startup of the operating system to the shutdown of the system.
  3. Run the sample program on the modified 0.11, count the waiting time, completion time (turnaround time) and running time of all processes established by the program by analyzing the log file, and then calculate the average waiting time, average completion time and throughput. You can write your own statistical program or use python script - stat_log.py (under / home/teacher /) -- make statistics.

  4. Modify the time slice of 0.11 process scheduling, then run the same sample program, count the same time data, compare with the original situation, and experience the differences brought by different time slices.

3, Experimental steps

1. Modified process C code, analog CPU, IO time-consuming operation

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>

#define HZ    100

void cpuio_bound(int last, int cpu_time, int io_time);

int main(int argc, char * argv[])
{
    int pid, a;
    printf("....begin..... \n");
    if ((pid = fork()) == 0){
        printf("..pid1 begin..\n");
        cpuio_bound(10, 1, 0); //cpu 10s;
        return 0;
    }
    printf("..pid1 : %d\n", pid);
    if ((pid = fork()) == 0){
        printf("..pid2 begin..\n");
        cpuio_bound(10, 0, 1); //io 10s;
        return 0;
    }
    printf("..pid2 : %d\n", pid);
    if ((pid = fork()) == 0){
        printf("..pid3 begin..\n");
        cpuio_bound(10, 1, 1); //cpu io 10s;
        return 0;
    }
    printf("..pid3 : %d\n", pid);
    if ((pid = fork()) == 0){
        printf("..pid4 begin..\n");
        cpuio_bound(10, 1, 8); //cpu 1 : 8 io 10s;
        return 0;
    }
    printf("..pid4 : %d\n", pid);
    if ((pid = fork()) == 0){
        printf("..pid5 begin..\n");
        cpuio_bound(10, 9, 1); //cpu 9 : 1 io 10s;
        return 0;
    }
    printf("..pid5 : %d\n", pid);
    while ((a = wait(NULL)) != -1)
        printf("pid %d exit \n", a);
    printf(".....end....\n");
    return 0;
}

void cpuio_bound(int last, int cpu_time, int io_time)
{
    struct tms start_time, current_time;
    clock_t utime, stime;
    int sleep_time;

    while (last > 0)
    {
        /* CPU Burst */
        times(&start_time);
        do
        {
            times(&current_time);
            utime = current_time.tms_utime - start_time.tms_utime;
            stime = current_time.tms_stime - start_time.tms_stime;
        } while (((utime + stime) / HZ)  < cpu_time);
        last -= cpu_time;

        if (last <= 0)
            break;
        sleep_time = 0;
        while (sleep_time < io_time)
        {
            sleep(1);
            sleep_time++;
        }
        last -= sleep_time;
    }
}

2. printk.c file defines the kernel output file function fprintk

/*
 *  linux/kernel/printk.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * When in kernel-mode, we cannot use printf, as fs is liable to
 * point to 'interesting' things. Make a printf with fs-saving, and
 * all is well.
 */
#include <stdarg.h>
#include <stddef.h>
#include "linux/sched.h"
#include "sys/stat.h"

#include <linux/kernel.h>

static char buf[1024];
static char logbuf[1024];
.......
.......
int fprintk(int fd, const char *fmt, ...)
{
    va_list args;
    int count;
    struct file * file;
    struct m_inode * inode;

    va_start(args, fmt);
    count=vsprintf(logbuf, fmt, args);
    va_end(args);

    if (fd < 3)    /* ?????stdout?stderr?????sys_write?? */
    {
        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
            "pushl $logbuf\n\t" /* ????Windows??????_logbuf,?? */
            "pushl %1\n\t"
            "call sys_write\n\t" /* ????Windows??????_sys_write,?? */
            "addl $8,%%esp\n\t"
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r" (fd):"ax","cx","dx");
    }
    else    /* ??>=3?????????????????????????????????*/
    {
        if (!(file=task[0]->filp[fd]))    /* ???0?????????????? */
            return 0;
        inode=file->f_inode;

        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
            "pushl $logbuf\n\t"
            "pushl %1\n\t"
            "pushl %2\n\t"
            "call file_write\n\t"
            "addl $12,%%esp\n\t"
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r" (file),"r" (inode):"ax","cx","dx");
    }
    return count;
}

3. main. Open the log file in C

void main(void)        /* This really IS void, no error here. */
{            /* The startup routine assumes (well, ...) this */
........
........
    sti();
    move_to_user_mode();
    /***************????***************/
    setup((void *) &drive_info);
    (void) open("/dev/tty0",O_RDWR,0);    //???????0?/dev/tty0???
    (void) dup(0);        //?????1??/dev/tty0??
    (void) dup(0);        //?????2??/dev/tty0??
    (void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);
    /***************????***************/
    if (!fork()) {        /* we count on this going ok */
        init();
    }

4. sched.c , fork.c , exit.c add the print process status code to the corresponding function

  1. sched.c

    void schedule(void)
    {
        int i,next,c;
            .......
            .......
        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
            if (*p) {
                            ......
                            ......
                (*p)->state==TASK_INTERRUPTIBLE){
                    (*p)->state=TASK_RUNNING;
                    fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies);
                }
            }
    
    /* this is the scheduler proper: */
    
        while (1) {
            . . . 
            . . . 
        if(current!=task[next]){
            fprintk(3,"%ld\t%c\t%ld\n",task[next]->pid,'R',jiffies);
            if(current->state==TASK_RUNNING)
                fprintk(3,"%ld\t%c\t%ld\n",current->pid,'J',jiffies);
        }
        switch_to(next);
    }
    int sys_pause(void)
    {
        if(current->pid!=0){
            current->state = TASK_INTERRUPTIBLE;
            fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
        }        
        schedule();
        return 0;
    }
    void sleep_on(struct task_struct **p)
    {
            ......
            ......
        *p = current;
        current->state = TASK_UNINTERRUPTIBLE;
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
        schedule();
        *p = tmp;
        if (tmp){
            tmp->state=0;
            fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies);
        }
    }
    void interruptible_sleep_on(struct task_struct **p)
    {
            ,,,,,,,,,,
            ,,,,,,,,,,
        *p=current;
    repeat:    current->state = TASK_INTERRUPTIBLE;
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
        schedule();
        if (*p && *p != current) {
            if((**p).state!=0)
                fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies);
            (**p).state=0;
            goto repeat;
        }
        *p=tmp;
        if (tmp){
            tmp->state=0;
            fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies);
        }
    }
    void wake_up(struct task_struct **p)
    {
        if (p && *p) {
            (**p).state=0;
            fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies);        
            *p=NULL;
        }
    }
    ,,,,,,,,
    ,,,,,,,,,
    
  2. fork.c

    int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
            long ebx,long ecx,long edx,
            long fs,long es,long ds,
            long eip,long cs,long eflags,long esp,long ss)
    {
           ..........
           ..........
        *p = *current;    /* NOTE! this doesn't copy the supervisor stack */
        fprintk(3,"%ld\t%c\t%ld\n",last_pid,'N',jiffies);
        p->state = TASK_UNINTERRUPTIBLE;
            ..........
            ..........
        p->state = TASK_RUNNING;    /* do this last, just in case */
        fprintk(3,"%ld\t%c\t%ld\n",last_pid,'J',jiffies);
        return last_pid;
    }
    
  3. exit.c

    int do_exit(long code)
    {
        .........
            .........
        current->state = TASK_ZOMBIE;
        current->exit_code = code;
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'E',jiffies);
        tell_father(current->father);
        schedule();
        return (-1);    /* just to suppress warnings */
    }
    .............
    ..............
    int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
    {
        int flag, code;
             ................
             ......................
        if (flag) {
            if (options & WNOHANG)
                return 0;
            current->state=TASK_INTERRUPTIBLE;
            fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
            schedule();
            if (!(current->signal &= ~(1<<(SIGCHLD-1))))
                goto repeat;
            else
                return -EINTR;
        }
        return -ECHILD;
    }
    

Screenshot of experimental statistical results

  1. [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-pr2egrzi-1598341316422) (assets / 157251410944. PNG)]

  2. Modify time slice sched h

    /*
     *  INIT_TASK is used to set up the first task table, touch at
     * your own risk!. Base=0, limit=0x9ffff (=640kB)
     */
    #define INIT_TASK \
    /* state etc */    { 0,5,5, \     //Change 15 to 5
    /* signals */    0,{{},},0, \
    
  3. [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-3LVzgys3-1598341316425)(assets/1572751513201.png)]

  4. 15 is changed to 50 [the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-4ovxqwym-1598341316428) (assets / 15727545739. PNG)]

4, Experimental question answer

  1. Combined with my own experience, what is the biggest difference between single process programming and multi process programming from the perspective of programmer?

Compared with multi process programming, single process programming is simpler and CPU utilization is low. Because single process programming is executed sequentially, while multi process programming is executed synchronously, complex and flexible scheduling algorithm is required to make full use of CPU resources, so the situation is much more complex. When designing multi process programming, we should consider the allocation of resources and time slices to achieve the balance of system scheduling. We should comprehensively consider the situation of all processes to achieve the best parallel execution effect. And the function of multi process programming is more powerful, and the application range is wider than that of single process programming.

  1. How did you modify the time slice? Only for the process established by the sample program, what are the statistical results of the log file (excluding Graphic) before and after modifying the time slice? Combined with your modification, analyze why it changes like this, or why it doesn't change?

    After changing 15 to 5, the statistical results of the log file do not change much. The reason may be that the time occupied by I/O operation in the program has too much weight on the total time, so the processing time is not obvious. Or the reason for the small change is that the sub process continuously occupies a lot more cpu time than the time slice.

5, Experimental summary

  • Understand the task scheduling mode of Linux
    Program programming is executed synchronously, which requires complex and flexible scheduling algorithms and makes full use of CPU resources, so the situation is much more complex. When designing multi process programming, we should consider the allocation of resources and time slices to achieve the balance of system scheduling. We should comprehensively consider the situation of all processes to achieve the best parallel execution effect. And the function of multi process programming is more powerful, and the application range is wider than that of single process programming.
  1. How did you modify the time slice? Only for the process established by the sample program, what are the statistical results of the log file (excluding Graphic) before and after modifying the time slice? Combined with your modification, analyze why it changes like this, or why it doesn't change?

    After changing 15 to 5, the statistical results of the log file do not change much. The reason may be that the time occupied by I/O operation in the program has too much weight on the total time, so the processing time is not obvious. Or the reason for the small change is that the sub process continuously occupies a lot more cpu time than the time slice.

5, Experimental summary

  • Understand the task scheduling mode of Linux
  • Master the multi process programming technology of Linux and visualize the concept of process by tracking the running track of process; The corresponding data statistics are carried out on the basis of process trajectory tracking, so that the process scheduling algorithm can be quantitatively evaluated, the understanding of scheduling and scheduling algorithm can be further deepened, and the direct experience of comparing the experimental data of scheduling algorithm on the actual operating system can be obtained.

Tags: Linux

Posted by jmandas on Sat, 21 May 2022 11:10:40 +0300