<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 實驗步驟 (1)在ubuntu下,利用系統提供的進程控制函數fork、wait系統調用編寫多進程程序process.c,編譯運行,分析運行結果. 后面開始修改linux0.11內核: (2)在init/main.c中的main()中添加創建日志文件/var/process.log的語句. (3)在printk.c中添加日志打印功能。 (4)在fork.c、sched.c和exit.c中,找到正確的狀態轉換點,并添加合適的狀態信息 (5)用(4)中修改后的3個程序分別替換linux0.11中原有的程序,并編譯內核。 (6)運行虛擬機,編譯并運行process.c. (7)在虛擬機上運行ls -l /var”或“ll /var”查看process.log是否建立,及它的屬性和長度;運行“vi /var/process.log”或“more /var/process.log”查看整個log文件。檢查打印出的狀態轉換信息是否正確。 (8)閱讀0.11的[調度](http://cms.hit.edu.cn/mod/quiz/view.php?id=3410 "調度")函數schedule,分析linux的[調度](https://cms.hit.edu.cn/mod/quiz/view.php?id=3410 "調度")算法,思考下面兩個問題 a、進程counter是如何初始化的? b、當進程的時間片用完時,被重新賦成何值? (9)對現有的[調度](http://cms.hit.edu.cn/mod/quiz/view.php?id=3410 "調度")算法進行時間片大小的修改,并重新編譯、運行內核進行驗證。 (1)process.c ~~~ #include <stdio.h> #include <unistd.h> #include <time.h> #include <sys/times.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> #define HZ 100 void cpuio_bound(int last, int cpu_time, int io_time); int main(int argc, char * argv[]) { pid_t Pid1; pid_t Pid2; pid_t Pid3; Pid1 = fork(); if (Pid1 < 0) printf("error in fork!"); else if (Pid1 == 0) { printf("child process 1:\n"); cpuio_bound(5, 2, 2); } Pid2 = fork(); if (Pid2 < 0) printf("error in fork!"); else if (Pid2 == 0) { printf("child process 2:\n"); cpuio_bound(5, 4, 0); } Pid3 = fork(); if (Pid3 < 0) printf("error in fork!"); else if (Pid3 == 0) { printf("child process 3:\n"); cpuio_bound(5, 0, 4); } printf("This process's Pid is %d\n", getpid()); printf("Pid of child process 1 is %d\n", Pid1); printf("Pid of child process 2 is %d\n", Pid2); printf("Pid of child process 3 is %d\n", Pid3); wait(NULL); wait(NULL); wait(NULL); return 0; } /* * 此函數按照參數占用CPU和I/O時間 * last: 函數實際占用CPU和I/O的總時間,不含在就緒隊列中的時間,>=0是必須的 * cpu_time: 一次連續占用CPU的時間,>=0是必須的 * io_time: 一次I/O消耗的時間,>=0是必須的 * 如果last > cpu_time + io_time,則往復多次占用CPU和I/O * 所有時間的單位為秒 */ 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); /* 其實只有t.tms_utime才是真正的CPU時間。但我們是在模擬一個 * 只在用戶狀態運行的CPU大戶,就像“for(;;);”。所以把t.tms_stime * 加上很合理。*/ do { times(¤t_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; /* IO Burst */ /* 用sleep(1)模擬1秒鐘的I/O操作 */ sleep_time=0; while (sleep_time < io_time) { sleep(1); sleep_time++; } last -= sleep_time; } } ~~~ (2)init/main.c ### 打開log文件 為了能盡早開始記錄,應當在內核啟動時就打開log文件。內核的入口是init/main.c中的main()(Windows環境下是start()),其中一段代碼是: ~~~ …… move_to_user_mode(); if (!fork()) { /* we count on this going ok */ init(); } …… ~~~ 這段代碼在進程0中運行,先切換到用戶模式,然后全系統第一次調用fork()建立進程1。進程1調用init()。在init()中: ~~~ …… setup((void *) &drive_info); //加載文件系統 (void) open("/dev/tty0",O_RDWR,0); //打開/dev/tty0,建立文件描述符0和/dev/tty0的關聯 (void) dup(0); //讓文件描述符1也和/dev/tty0關聯 (void) dup(0); //讓文件描述符2也和/dev/tty0關聯 …… ~~~ 這段代碼建立了文件描述符0、1和2,它們分別就是stdin、stdout和stderr。這三者的值是系統標準(Windows也是如此),不可改變。可以把log文件的描述符關聯到3。文件系統初始化,描述符0、1和2關聯之后,才能打開log文件,開始記錄進程的運行軌跡。為了能盡早訪問log文件,我們要讓上述工作在進程0中就完成。所以把這一段代碼從init()**移動到**main()中,放在move_to_user_mode()之后(不能再靠前了),同時加上打開log文件的代碼。修改后的main()如下: ~~~ …… 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(); } …… ~~~ (3)kernel/printk.c 向printk.c中添加日志打印功能 因為和printk的功能近似,建議將此函數放入到kernel/printk.c中。fprintk()的使用方式類同與C標準庫函數fprintf(),唯一的區別是第一個參數是文件描述符,而不是文件指針。例如: ~~~ fprintk(1, "The ID of running process is %ld", current->pid); //向stdout打印正在運行的進程的ID fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'R', jiffies); //向log文件輸出 ~~~ 將下面代碼加入到printk.c中 ~~~ /* * 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]; extern int vsprintf(char * buf, const char * fmt, va_list args); int printk(const char *fmt, ...) { va_list args; int i; va_start(args, fmt); i=vsprintf(buf,fmt,args); va_end(args); __asm__("push %%fs\n\t" "push %%ds\n\t" "pop %%fs\n\t" "pushl %0\n\t" "pushl $buf\n\t" "pushl $0\n\t" "call tty_write\n\t" "addl $8,%%esp\n\t" "popl %0\n\t" "pop %%fs" ::"r" (i):"ax","cx","dx"); return i; } 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; } ~~~ (4)fork.c 修改下列三個文件參考進程狀態表,當狀態改變時,向日志輸出 | 內核表示 | 含義 | |-----|-----| | TASK_RUNNING | 可運行 | | TASK_INTERRUPTIBLE | 可中斷的等待狀態 | | TASK_UNINTERRUPTIBLE | 不可中斷的等待狀態 | | TASK_ZOMBIE | 僵死 | | TASK_STOPPED | 暫停 | | TASK_SWAPPING | 換入/換出 | ~~~ #include <errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/segment.h> #include <asm/system.h> extern void write_verify(unsigned long address); long last_pid=0; void verify_area(void * addr,int size) { ... } int copy_mem(int nr,struct task_struct * p) { ... } 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->start_time = jiffies; /*在上面一行初始化了進程的開始時間所以趕快輸出一條進程創建的Log*/ fprintk(3,"%ld\t%c\t%ld\n",last_pid,'N',jiffies); ... p->state = TASK_RUNNING; /* do this last, just in case */ /*在上面一行改變了進程的狀態這里輸出一個進入就緒隊列的Log*/ /*進程中Running表示的是可以運行,并不是正在運行*/ fprintk(3,"%ld\t%c\t%ld\n",last_pid,'J',jiffies); return last_pid; } int find_empty_process(void) { ... } ~~~ (5)exit.c ~~~ #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/tty.h> #include <asm/segment.h> int sys_pause(void); int sys_close(int fd); void release(struct task_struct * p) { ... } static inline int send_sig(long sig,struct task_struct * p,int priv) { ... } static void kill_session(void) { ... } int sys_kill(int pid,int sig) { ... } static void tell_father(int pid) { ... } int do_exit(long code) { ... } int sys_exit(int error_code) { ... } int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) { int flag, code; struct task_struct ** p; verify_area(stat_addr,4); repeat: flag=0; for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p || *p == current) continue; if ((*p)->father != current->pid) continue; if (pid>0) { if ((*p)->pid != pid) continue; } else if (!pid) { if ((*p)->pgrp != current->pgrp) continue; } else if (pid != -1) { if ((*p)->pgrp != -pid) continue; } switch ((*p)->state) { case TASK_STOPPED: if (!(options & WUNTRACED)) continue; put_fs_long(0x7f,stat_addr); return (*p)->pid; case TASK_ZOMBIE: current->cutime += (*p)->utime; current->cstime += (*p)->stime; flag = (*p)->pid; code = (*p)->exit_code; /*輸出一條進程退出的Log*/ /*TASK_STOPED狀態只是將當前進程轉入睡眠狀態,收到SIG_CONT信號時會被喚醒*/ /*TASK_ZOMBIE狀態則是當前進程被KILL,并發送信號給父進程*/ fprintk(3,"%ld\t%c\t%ld\n",flag,'E',jiffies); release(*p); put_fs_long(code,stat_addr); return flag; default: flag=1; continue; } } if (flag) { if (options & WNOHANG) return 0; current->state=TASK_INTERRUPTIBLE; /*輸出一條等待的Log*/ /*這里要注意一下輸出wait的時候要判斷一下 pid 是不是等于0 如果等于0 就不輸出Log*/ /*0號進程是守護進程,cpu空閑的時候一直在waiting,輸出它的話是不會通過腳本檢查的哦*/ if (current->pid!=0) 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; } ~~~ (6)sched.c ~~~ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/sys.h> #include <linux/fdreg.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> #include <signal.h> #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) void show_task(int nr,struct task_struct * p) { ... } void show_stat(void) { ... } #define LATCH (1193180/HZ) extern void mem_use(void); extern int timer_interrupt(void); extern int system_call(void); union task_union { struct task_struct task; char stack[PAGE_SIZE]; }; static union task_union init_task = {INIT_TASK,}; long volatile jiffies=0; long startup_time=0; struct task_struct *current = &(init_task.task); struct task_struct *last_task_used_math = NULL; struct task_struct * task[NR_TASKS] = {&(init_task.task), }; long user_stack [ PAGE_SIZE>>2 ] ; struct { long * a; short b; } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; void math_state_restore() { ... } 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 & ~(_BLOCKABLE & (*p)->blocked)) && (*p)->state==TASK_INTERRUPTIBLE) { (*p)->state=TASK_RUNNING; /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies); } } /* this is the scheduler proper: */ while (1) { 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; for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; } if(current->state == TASK_RUNNING && current != task[next]) /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",current->pid,'J',jiffies); if(current != task[next]) /*輸出可運行的Log*/ fprintk(3,"%ld\t%c\t%ld\n",task[next]->pid,'R',jiffies); switch_to(next); } int sys_pause(void) { current->state = TASK_INTERRUPTIBLE; /*檢查并輸出等待的Log*/ if (current->pid != 0) fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); schedule(); return 0; } void 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; current->state = TASK_UNINTERRUPTIBLE; /*檢查并輸出等待的Log*/ if (current->pid != 0) fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); schedule(); *p = tmp; if (tmp) { tmp->state=TASK_RUNNING; /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies); } } 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; /*檢查并輸出等待的Log*/ if (current->pid != 0) fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); schedule(); if (*p && *p != current) { (**p).state=TASK_RUNNING; /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies); goto repeat; } *p=tmp; if (tmp) { tmp->state=TASK_RUNNING; /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies); } } void wake_up(struct task_struct **p) { if (p && *p) { if((**p).state != TASK_RUNNING){ /*輸出就緒的Log*/ fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies); (**p).state=TASK_RUNNING; } } } ... ~~~ 附report 1.結合自己的體會,談談從程序設計者的角度看,單進程編程和多進程編程最大的區別是什么? 單進程編程較于多進程編程要更簡單,因為單進程是順序執行的,而多進程編程是同步執行的,所以情況要復雜得多。在設計多進程編程時,要考慮資源的分配,時間片的分配等達到系統調度的平衡。要綜合考慮所有進程的情況以達到最優的并行執行效果。且多進程編程的功能更為強大,且應用范圍較于單進程編程更加廣泛。 2.你是如何修改時間片的? 僅針對樣本程序建立的進程,在修改時間片前后,log文件的統計結果(不包括Graphic)都是什么樣? 結合你的修改分析一下為什么會這樣變化,或者為什么沒變化? 1)include/sched.h宏INIT_TASK中定義的: ~~~ #define INIT_TASK \ { 0,15,15, //分別對應state;counter;和priority; ~~~ 將priority值修改,即可實現對時間片大小的調整。 2)在修改時間片將priority由15改為150后: Process 9~20 中Turnaround, Waiting, CPU Burst, I/O Burst變化不大 3)原因可能是程序中I/O操作占用的時間對于總時間影響的權重過大,導致處理時間體現的并不明顯。 或者變化不大的原因是,子進程連續占用cpu的時間要比時間片大很多。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看