一、實(shí)驗(yàn)內(nèi)容
- 基于模板?
process.c
?編寫多進(jìn)程的樣本程序,實(shí)現(xiàn)如下功能: + 所有子進(jìn)程都并行運(yùn)行,每個(gè)子進(jìn)程的實(shí)際運(yùn)行時(shí)間一般不超過 30 秒; + 父進(jìn)程向標(biāo)準(zhǔn)輸出打印所有子進(jìn)程的 id,并在所有子進(jìn)程都退出后才退出; - 在?
Linux0.11
?上實(shí)現(xiàn)進(jìn)程運(yùn)行軌跡的跟蹤。 + 基本任務(wù)是在內(nèi)核中維護(hù)一個(gè)日志文件?/var/process.log
,把從操作系統(tǒng)啟動(dòng)到系統(tǒng)關(guān)機(jī)過程中所有進(jìn)程的運(yùn)行軌跡都記錄在這一 log 文件中。 - 在修改過的 0.11 上運(yùn)行樣本程序,通過分析 log 文件,統(tǒng)計(jì)該程序建立的所有進(jìn)程的等待時(shí)間、完成時(shí)間(周轉(zhuǎn)時(shí)間)和運(yùn)行時(shí)間,然后計(jì)算平均等待時(shí)間,平均完成時(shí)間和吞吐量??梢宰约壕帉懡y(tǒng)計(jì)程序,也可以使用 python 腳本程序——?
stat_log.py
(在?/home/teacher/
?目錄下) ——進(jìn)行統(tǒng)計(jì)。 - 修改 0.11 進(jìn)程調(diào)度的時(shí)間片,然后再運(yùn)行同樣的樣本程序,統(tǒng)計(jì)同樣的時(shí)間數(shù)據(jù),和原有的情況對(duì)比,體會(huì)不同時(shí)間片帶來的差異。
/var/process.log
?文件的格式必須為:
PID X Time
其中:
- pid 是進(jìn)程的 ID;
- X 可以是 N、J、R、W 和 E 中的任意一個(gè),分別表示進(jìn)程新建(N)、進(jìn)入就緒態(tài)(J)、進(jìn)入運(yùn)行態(tài)(R)、進(jìn)入阻塞態(tài)(W) 和退出(E);
- time 表示 X 發(fā)生的時(shí)間。這個(gè)時(shí)間不是物理時(shí)間,而是系統(tǒng)的滴答時(shí)間(tick);
三個(gè)字段之間用制表符分隔。例如:
12 N 1056
12 J 1057
4 W 1057
12 R 1057
13 N 1058
13 J 1059
14 N 1059
14 J 1060
15 N 1060
15 J 1061
12 W 1061
15 R 1061
15 J 1076
14 R 1076
14 E 1076
......
二、實(shí)驗(yàn)環(huán)境搭建(Ubuntu虛擬機(jī))
ubuntu下 Linux 0.11 編譯內(nèi)核 - 實(shí)驗(yàn)環(huán)境搭建_~橘子~的博客-CSDN博客如果想學(xué)習(xí)Linux 0.11,實(shí)驗(yàn)環(huán)境是必不可少的。最好是能編譯,能運(yùn)行,能調(diào)試。本文用的是Mooc網(wǎng)上的哈工大李治軍老師操作系統(tǒng)!李老師的講課風(fēng)格淺入深出。1.下載Linux 0.11用github下載https://github.com/Wangzhike/HIT-Linux-0.11 下載命令行下載 如果沒有g(shù)it,那么先安裝gitsudo apt-get instal...https://blog.csdn.net/qq_39557240/article/details/85336730
三、先備知識(shí)
在init/目錄下有一個(gè)main.c文件,系統(tǒng)在執(zhí)行完boot/目錄下的head.s程序后就會(huì)將執(zhí)行權(quán)交給main.c,這個(gè)程序包含了內(nèi)核初始化的所有工作。
- 多進(jìn)程如何啟動(dòng)?
?main中的fork()創(chuàng)建了操作系統(tǒng)中的0進(jìn)程的第一個(gè)子進(jìn)程,在這個(gè)子進(jìn)程中,執(zhí)行了init(),init中又執(zhí)行了Shell(Windows桌面),Shell這個(gè)進(jìn)程再啟動(dòng)其他進(jìn)程,這樣就形成了多進(jìn)程圖像。
這里簡單介紹一下fork()函數(shù),fork()系統(tǒng)調(diào)用用于創(chuàng)建子進(jìn)程,它的返回值有兩個(gè),父進(jìn)程返回創(chuàng)建子進(jìn)程的pid,子進(jìn)程返回0。這里if(!fork())就表示在子進(jìn)程中調(diào)用init()函數(shù)。
- 多進(jìn)程如何組織?
多進(jìn)程的組織依賴于一些重要的數(shù)據(jù)結(jié)構(gòu),比如PCB(進(jìn)程控制塊)、隊(duì)列和狀態(tài)。
進(jìn)程控制塊(PCB):是用來記錄進(jìn)程信息的一種數(shù)據(jù)結(jié)構(gòu),它在linux-0.11內(nèi)核中的include/linux/sched.h中,結(jié)構(gòu)如下:
狀態(tài):這里值進(jìn)程的狀態(tài),進(jìn)程的狀態(tài)有五種:新建態(tài)、就緒態(tài)、運(yùn)行態(tài)、阻塞態(tài)、終止態(tài)。
?注:上面的箭頭表示狀態(tài)的轉(zhuǎn)換方向
隊(duì)列:因?yàn)镃PU在同一時(shí)刻只能運(yùn)行一個(gè)進(jìn)程。除了處于運(yùn)行態(tài)的進(jìn)程,其他的進(jìn)程都排隊(duì)等著執(zhí)行。
????????但是每個(gè)進(jìn)程的狀態(tài)又是不一樣的,如果進(jìn)程已經(jīng)準(zhǔn)備就緒,只是還沒有被分配CPU,這個(gè)進(jìn)程的PCB就處于就緒隊(duì)列上,等著被CPU調(diào)度。如果進(jìn)程正在等待一個(gè)事件發(fā)生,從而發(fā)生了阻塞,需要這個(gè)事件發(fā)生來喚醒,那么這個(gè)進(jìn)程PCB處于阻塞隊(duì)列上,等著被喚醒,喚醒后則進(jìn)入就緒態(tài)。
因此每個(gè)隊(duì)列上的元素都是進(jìn)程的PCB,隨著進(jìn)程狀態(tài)的改變,這些PCB也會(huì)隨之出隊(duì)入隊(duì)。
這樣操作系統(tǒng)就把多個(gè)進(jìn)程有序的組織起來了。
- 多進(jìn)程如何交替?
進(jìn)程交替需要三個(gè)步驟:隊(duì)列操作+調(diào)度+切換
?隊(duì)列操作:就是將進(jìn)程PCB放入對(duì)應(yīng)狀態(tài)的等待隊(duì)列中,隨著進(jìn)程狀態(tài)轉(zhuǎn)換出隊(duì)入隊(duì)的操作。
調(diào)度:當(dāng)一個(gè)進(jìn)程因?yàn)橐却硞€(gè)事件的發(fā)生(比如讀寫磁盤的IO操作)或者時(shí)間片用完,擦偶作系統(tǒng)會(huì)使用剝奪當(dāng)前進(jìn)程的CPU使用權(quán),使用調(diào)度算法(FIFO、短作業(yè)優(yōu)先等策略)在就緒隊(duì)列中選出下一個(gè)進(jìn)程使用CPU。
切換:當(dāng)進(jìn)程切換時(shí),需要保存運(yùn)行時(shí)的一些狀態(tài)和數(shù)據(jù),而這些都保存在CPU的寄存器中,這時(shí),被剝奪CPU的進(jìn)程需要把CPU這些寄存器的值保存在自己的PCB中,而獲得CPU使用權(quán)的進(jìn)程,為了從上次運(yùn)行的狀態(tài)繼續(xù)運(yùn)行,則需要將自己PCB中的寄存器的值賦值給CPU。
- 多進(jìn)程如何影響?
?
?實(shí)驗(yàn)相關(guān)源碼分析:
1.實(shí)驗(yàn)?zāi)康氖且獙⑦M(jìn)程的每一次狀態(tài)變換和該狀態(tài)持續(xù)的時(shí)間都記錄在log文件中,因此要求操作系統(tǒng)啟動(dòng)后先打開/var/process.log,然后就可以在每個(gè)進(jìn)程發(fā)生狀態(tài)切換的時(shí)候?qū)懭胗涗?,為了能盡早開始記錄,應(yīng)該在內(nèi)核啟動(dòng)時(shí)就打開log文件,內(nèi)核入口是init/main.c的main函數(shù):
將下面紅色區(qū)域的代碼剪切到上面 move_to_user_mode();后面,然后其后跟著添加打開log文件的代碼:
(void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);
?這樣,文件描述符0,1,2,3就在進(jìn)程0中創(chuàng)建了。根據(jù)fork()原理,進(jìn)程0創(chuàng)建的任意子進(jìn)程都會(huì)繼承這些文件描述符,因此在init()中就不必再open()了。此后,init()的后續(xù)代碼和/bin/sh都會(huì)重新初始化它們。所以只有進(jìn)程0和進(jìn)程1的文件描述符關(guān)聯(lián)著log文件。
2.編寫fprintk()函數(shù),所有的狀態(tài)轉(zhuǎn)移都是在內(nèi)核環(huán)境下發(fā)生的,而想要把每一次狀態(tài)切換的記錄寫入log文件,內(nèi)核不能使用write()函數(shù),就像內(nèi)核狀態(tài)下只能調(diào)用printk()而不能調(diào)用printf()函數(shù)一樣。因此這個(gè)fprintk()函數(shù)實(shí)現(xiàn)的功能就是在內(nèi)核中寫log文件。下面是代碼:
#include "linux/sched.h"
#include "sys/stat.h"
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);
/* 如果輸出到stdout或stderr,直接調(diào)用sys_write即可 */
if (fd < 3)
{
__asm__("push %%fs\n\t"
"push %%ds\n\t"
"pop %%fs\n\t"
"pushl %0\n\t"
/* 注意對(duì)于Windows環(huán)境來說,是_logbuf,下同 */
"pushl $logbuf\n\t"
"pushl %1\n\t"
/* 注意對(duì)于Windows環(huán)境來說,是_sys_write,下同 */
"call sys_write\n\t"
"addl $8,%%esp\n\t"
"popl %0\n\t"
"pop %%fs"
::"r" (count),"r" (fd):"ax","cx","dx");
}
else
/* 假定>=3的描述符都與文件關(guān)聯(lián)。事實(shí)上,還存在很多其它情況,這里并沒有考慮。*/
{
/* 從進(jìn)程0的文件描述符表中得到文件句柄 */
if (!(file=task[0]->filp[fd]))
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.在進(jìn)程狀態(tài)切換處,將進(jìn)程的聲明周期寫入日志。涉及到的文件有fork.c、sched.c、exit.c
新建態(tài):用戶使用fork()函數(shù),新建的進(jìn)程處于新建態(tài)
就緒態(tài):進(jìn)程新建之后進(jìn)入就緒態(tài)、進(jìn)程阻塞事件發(fā)生進(jìn)入就緒態(tài)、進(jìn)程時(shí)間片用完進(jìn)入就緒態(tài)等等
阻塞態(tài):進(jìn)程等待資源或事件會(huì)進(jìn)入阻塞態(tài)
運(yùn)行態(tài):進(jìn)程被調(diào)度占用CPU進(jìn)入運(yùn)行態(tài)
?四、實(shí)驗(yàn)步驟
?1.修改init.c文件。
將init.c文件中的init()的以下代碼移動(dòng)到main()函數(shù)中,并添加代碼打開process.log文件。
2.在printf.c里面添加上面fprintk的代碼:
3.將進(jìn)程狀態(tài)切換信息寫入process.log文件:
修改fork文件:
?
修改sched.c文件:
?
?
?
?4.在linux-0.11目錄下執(zhí)行make all重新編譯,若沒報(bào)錯(cuò)則執(zhí)行下一步。
5.編寫process.c文件,這個(gè)文件可以放在oslab下,為了把這個(gè)文件拷貝到linux-0.11系統(tǒng)中,需要掛載系統(tǒng)硬盤,最后啟動(dòng)linux-0.11系統(tǒng),在這個(gè)系統(tǒng)里編譯運(yùn)行process.c文件。
#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[])
{
pid_t n_proc[10]; /*10個(gè)子進(jìn)程 PID*/
int i;
for(i=0;i<10;i++)
{
n_proc[i] = fork();
/*子進(jìn)程*/
if(n_proc[i] == 0)
{
cpuio_bound(20,2*i,20-2*i); /*每個(gè)子進(jìn)程都占用20s*/
return 0; /*執(zhí)行完cpuio_bound 以后,結(jié)束該子進(jìn)程*/
}
/*fork 失敗*/
else if(n_proc[i] < 0 )
{
printf("Failed to fork child process %d!\n",i+1);
return -1;
}
/*父進(jìn)程繼續(xù)fork*/
}
/*打印所有子進(jìn)程PID*/
for(i=0;i<10;i++)
printf("Child PID: %d\n",n_proc[i]);
/*等待所有子進(jìn)程完成*/
wait(&i); /*Linux 0.11 上 gcc要求必須有一個(gè)參數(shù), gcc3.4+則不需要*/
return 0;
}
/*
* 此函數(shù)按照參數(shù)占用CPU和I/O時(shí)間
* last: 函數(shù)實(shí)際占用CPU和I/O的總時(shí)間,不含在就緒隊(duì)列中的時(shí)間,>=0是必須的
* cpu_time: 一次連續(xù)占用CPU的時(shí)間,>=0是必須的
* io_time: 一次I/O消耗的時(shí)間,>=0是必須的
* 如果last > cpu_time + io_time,則往復(fù)多次占用CPU和I/O
* 所有時(shí)間的單位為秒
*/
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);
/* 其實(shí)只有t.tms_utime才是真正的CPU時(shí)間。但我們是在模擬一個(gè)
* 只在用戶狀態(tài)運(yùn)行的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;
}
}
掛載代碼:sudo ./mount-hdc
將process文件用cp指令復(fù)制到hdc/usr/root目錄下,然后在oslab目錄執(zhí)行./run,啟動(dòng)系統(tǒng)。
?執(zhí)行process之后,會(huì)生成log文件(這個(gè)文件在hdc/var目錄下,退出linux-0.11,hdc會(huì)自動(dòng)卸載,再掛載一次就行了),但注意一定要刷新,然后將log文件拷貝到oslab目錄下。
?6.編寫python文件stat_log.py(這個(gè)文件放在oslab下),統(tǒng)計(jì)進(jìn)程的運(yùn)行信息。
#!/usr/share/python
import sys
import copy
P_NULL = 0
P_NEW = 1
P_READY = 2
P_RUNNING = 4
P_WAITING = 8
P_EXIT = 16
S_STATE = 0
S_TIME = 1
HZ = 100
graph_title = r"""
-----===< COOL GRAPHIC OF SCHEDULER >===-----
[Symbol] [Meaning]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
number PID or tick
"-" New or Exit
"#" Running
"|" Ready
":" Waiting
/ Running with
"+" -| Ready
\and/or Waiting
-----===< !!!!!!!!!!!!!!!!!!!!!!!!! >===-----
"""
usage = """
Usage:
%s /path/to/process.log [PID1] [PID2] ... [-x PID1 [PID2] ... ] [-m] [-g]
Example:
# Include process 6, 7, 8 and 9 in statistics only. (Unit: tick)
%s /path/to/process.log 6 7 8 9
# Exclude process 0 and 1 from statistics. (Unit: tick)
%s /path/to/process.log -x 0 1
# Include process 6 and 7 only and print a COOL "graphic"! (Unit: millisecond)
%s /path/to/process.log 6 7 -m -g
# Include all processes and print a COOL "graphic"! (Unit: tick)
%s /path/to/process.log -g
"""
class MyError(Exception):
pass
class DuplicateNew(MyError):
def __init__(self, pid):
args = "More than one 'N' for process %d." % pid
MyError.__init__(self, args)
class UnknownState(MyError):
def __init__(self, state):
args = "Unknown state '%s' found." % state
MyError.__init__(self, args)
class BadTime(MyError):
def __init__(self, time):
args = "The time '%d' is bad. It should >= previous line's time." % time
MyError.__init__(self, args)
class TaskHasExited(MyError):
def __init__(self, state):
args = "The process has exited. Why it enter '%s' state again?" % state
MyError.__init__(self, args)
class BadFormat(MyError):
def __init__(self):
args = "Bad log format"
MyError.__init__(self, args)
class RepeatState(MyError):
def __init__(self, pid):
args = "Previous state of process %d is identical with this line." % (pid)
MyError.__init__(self, args)
class SameLine(MyError):
def __init__(self):
args = "It is a clone of previous line."
MyError.__init__(self, args)
class NoNew(MyError):
def __init__(self, pid, state):
args = "The first state of process %d is '%s'. Why not 'N'?" % (pid, state)
MyError.__init__(self, args)
class statistics:
def __init__(self, pool, include, exclude):
if include:
self.pool = process_pool()
for process in pool:
if process.getpid() in include:
self.pool.add(process)
else:
self.pool = copy.copy(pool)
if exclude:
for pid in exclude:
if self.pool.get_process(pid):
self.pool.remove(pid)
def list_pid(self):
l = []
for process in self.pool:
l.append(process.getpid())
return l
def average_turnaround(self):
if len(self.pool) == 0:
return 0
sum = 0
for process in self.pool:
sum += process.turnaround_time()
return float(sum) / len(self.pool)
def average_waiting(self):
if len(self.pool) == 0:
return 0
sum = 0
for process in self.pool:
sum += process.waiting_time()
return float(sum) / len(self.pool)
def begin_time(self):
begin = 0xEFFFFF
for p in self.pool:
if p.begin_time() < begin:
begin = p.begin_time()
return begin
def end_time(self):
end = 0
for p in self.pool:
if p.end_time() > end:
end = p.end_time()
return end
def throughput(self):
return len(self.pool) * HZ / float(self.end_time() - self.begin_time())
def print_graphic(self):
begin = self.begin_time()
end = self.end_time()
print graph_title
for i in range(begin, end+1):
line = "%5d " % i
for p in self.pool:
state = p.get_state(i)
if state & P_NEW:
line += "-"
elif state == P_READY or state == P_READY | P_WAITING:
line += "|"
elif state == P_RUNNING:
line += "#"
elif state == P_WAITING:
line += ":"
elif state & P_EXIT:
line += "-"
elif state == P_NULL:
line += " "
elif state & P_RUNNING:
line += "+"
else:
assert False
if p.get_state(i-1) != state and state != P_NULL:
line += "%-3d" % p.getpid()
else:
line += " "
print line
class process_pool:
def __init__(self):
self.list = []
def get_process(self, pid):
for process in self.list:
if process.getpid() == pid:
return process
return None
def remove(self, pid):
for process in self.list:
if process.getpid() == pid:
self.list.remove(process)
def new(self, pid, time):
p = self.get_process(pid)
if p:
if pid != 0:
raise DuplicateNew(pid)
else:
p.states=[(P_NEW, time)]
else:
p = process(pid, time)
self.list.append(p)
return p
def add(self, p):
self.list.append(p)
def __len__(self):
return len(self.list)
def __iter__(self):
return iter(self.list)
class process:
def __init__(self, pid, time):
self.pid = pid
self.states = [(P_NEW, time)]
def getpid(self):
return self.pid
def change_state(self, state, time):
last_state, last_time = self.states[-1]
if state == P_NEW:
raise DuplicateNew(pid)
if time < last_time:
raise BadTime(time)
if last_state == P_EXIT:
raise TaskHasExited(state)
if last_state == state and self.pid != 0: # task 0 can have duplicate state
raise RepeatState(self.pid)
self.states.append((state, time))
def get_state(self, time):
rval = P_NULL
combo = P_NULL
if self.begin_time() <= time <= self.end_time():
for state, s_time in self.states:
if s_time < time:
rval = state
elif s_time == time:
combo |= state
else:
break
if combo:
rval = combo
return rval
def turnaround_time(self):
return self.states[-1][S_TIME] - self.states[0][S_TIME]
def waiting_time(self):
return self.state_last_time(P_READY)
def cpu_time(self):
return self.state_last_time(P_RUNNING)
def io_time(self):
return self.state_last_time(P_WAITING)
def state_last_time(self, state):
time = 0
state_begin = 0
for s,t in self.states:
if s == state:
state_begin = t
elif state_begin != 0:
assert state_begin <= t
time += t - state_begin
state_begin = 0
return time
def begin_time(self):
return self.states[0][S_TIME]
def end_time(self):
return self.states[-1][S_TIME]
# Enter point
if len(sys.argv) < 2:
print usage.replace("%s", sys.argv[0])
sys.exit(0)
# parse arguments
include = []
exclude = []
unit_ms = False
graphic = False
ex_mark = False
try:
for arg in sys.argv[2:]:
if arg == '-m':
unit_ms = True
continue
if arg == '-g':
graphic = True
continue
if not ex_mark:
if arg == '-x':
ex_mark = True
else:
include.append(int(arg))
else:
exclude.append(int(arg))
except ValueError:
print "Bad argument '%s'" % arg
sys.exit(-1)
# parse log file and construct processes
processes = process_pool()
f = open(sys.argv[1], "r")
# Patch process 0's New & Run state
processes.new(0, 40).change_state(P_RUNNING, 40)
try:
prev_time = 0
prev_line = ""
for lineno, line in enumerate(f):
if line == prev_line:
raise SameLine
prev_line = line
fields = line.split("\t")
if len(fields) != 3:
raise BadFormat
pid = int(fields[0])
s = fields[1].upper()
time = int(fields[2])
if time < prev_time:
raise BadTime(time)
prev_time = time
p = processes.get_process(pid)
state = P_NULL
if s == 'N':
processes.new(pid, time)
elif s == 'J':
state = P_READY
elif s == 'R':
state = P_RUNNING
elif s == 'W':
state = P_WAITING
elif s == 'E':
state = P_EXIT
else:
raise UnknownState(s)
if state != P_NULL:
if not p:
raise NoNew(pid, s)
p.change_state(state, time)
except MyError, err:
print "Error at line %d: %s" % (lineno+1, err)
sys.exit(0)
# Stats
stats = statistics(processes, include, exclude)
att = stats.average_turnaround()
awt = stats.average_waiting()
if unit_ms:
unit = "ms"
att *= 1000/HZ
awt *= 1000/HZ
else:
unit = "tick"
print "(Unit: %s)" % unit
print "Process Turnaround Waiting CPU Burst I/O Burst"
for pid in stats.list_pid():
p = processes.get_process(pid)
tt = p.turnaround_time()
wt = p.waiting_time()
cpu = p.cpu_time()
io = p.io_time()
if unit_ms:
print "%7d %10d %7d %9d %9d" % (pid, tt*1000/HZ, wt*1000/HZ, cpu*1000/HZ, io*1000/HZ)
else:
print "%7d %10d %7d %9d %9d" % (pid, tt, wt, cpu, io)
print "Average: %10.2f %7.2f" % (att, awt)
print "Throughout: %.2f/s" % (stats.throughput())
if graphic:
stats.print_graphic()
運(yùn)行這個(gè)文件:python2 stat_log.py process.log,得到實(shí)驗(yàn)結(jié)果。
?參考博客:文章來源:http://www.zghlxwxcb.cn/news/detail-503044.html
(112條消息) 操作系統(tǒng)實(shí)驗(yàn)四 進(jìn)程運(yùn)行軌跡的跟蹤與統(tǒng)計(jì)(哈工大李治軍)_Casten-Wang的博客-CSDN博客https://blog.csdn.net/leoabcd12/article/details/120107478文章來源地址http://www.zghlxwxcb.cn/news/detail-503044.html
到了這里,關(guān)于哈工大操作系統(tǒng)實(shí)驗(yàn)三(整理自用)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!