理解進程控制的原理對於理解和修改fio project非常的重要。"fio is an I/O tool meant to be used both for benchmark and stress/hardware verification."
進程
unix提供了大量的從c程序中操作系統的系統調用(別的語言應該也是有的吧)。
創建進程
每一個進程都有一個正數的id,叫做pid。getpid函數返回調用進程的pid,getppid函數返回它的父進程的pid。
fork
exit
父進程和子進程是併發運行的獨立進程。內核能夠以任意方式交替執行他們的邏輯控制流中的指令。
如果能夠在fork函數在父進程和子進程中返回後立即暫停這兩個進程,我們會看到每個進程的地址空間都是相同的。(每個進程有相同的用戶棧,相同的本地變量值,相同的堆,相同的全局變量值以及相同的代碼。然而他們都有自己獨立的地址空間)
關於這個,fio這個程序利用到它的地方就是
while (todo) { struct thread_data *map[REAL_MAX_JOBS]; struct timeval this_start; int this_jobs = 0, left; for_each_td(td, i) { if (td->runstate != TD_NOT_CREATED) continue;
if (td->o.start_delay) { spent = utime_since_genesis();
if (td->o.start_delay > spent) continue; }
if (td->o.stonewall && (nr_started || nr_running)) { dprint(FD_PROCESS, "%s: stonewall wait\n", td->o.name); break; }
init_disk_util(td);
td->rusage_sem = fio_mutex_init(FIO_MUTEX_LOCKED); td->update_rusage = 0;
/* * Set state to created. Thread will transition * to TD_INITIALIZED when it's done setting up. */ td_set_runstate(td, TD_CREATED); map[this_jobs++] = td; nr_started++; ... if (td->o.use_thread) { int ret;
dprint(FD_PROCESS, "will pthread_create\n"); ret = pthread_create(&td->thread, NULL, thread_main, td); if (ret) { log_err("pthread_create: %s\n", strerror(ret)); nr_started--; break; } ret = pthread_detach(td->thread); if (ret) log_err("pthread_detach: %s", strerror(ret)); } else { pid_t pid; dprint(FD_PROCESS, "will fork\n"); pid = fork(); if (!pid) { int ret = fork_main(shm_id, i);
_exit(ret); } else if (i == fio_debug_jobno) *fio_debug_jobp = pid; } |
|
這就相當於這麼編程:
#include <stdio.h> int main() { int i=1; int pid; while((i--)>=0){ pid=fork(); if(pid==0){ i--; printf("in the child process.\n"); } else printf("in the parent process.\n"); } } |
編譯並執行上面那段程序的結果:
root@localhost ~]# ./a.out in the parent process. in the child process. in the parent process. in the child process. |
一共創建了兩個進程,只不過在Fio中的子進程的執行是由另外一個函數fork_main和thread_main來決定的。
note:thread main這個函數做了許多事情,會再分析。
回收子進程
當一個進程由於某種原因終止時,內核並不是立即把它從系統中清除。相反,進程被保持在一種已終止的狀態中,直到被它的父進程reap。當父進程回收已經終止的子進程時,內核將子進程的退出狀態(這是什麼樣的退出狀態呢,留個心)傳遞給父進程,然後拋棄已終止的進程,從此時開始,該進程就不存在了。
一個終止了但未被回收的進程稱爲殭屍zombie。
如果父進程沒有回收他的zombie就終止了,那麼內核就會安排init進程來回收他們。長時間運行的程序,比如shell或者服務器,總是應該回收他們的zombie(總是在消耗系統的存儲器資源)。
一個進程可以通過調用waitpid函數來等待它的子進程終止或者停止。
函數原型:
#include<sys/types.h> waitpid()會暫時停止目前進程的執行,直到有信號來到或子進程
|
fio使用waitpid的例子
* Run over the job map and reap the threads that have exited, if any. */ static void reap_threads(unsigned int *nr_running, unsigned int *t_rate, unsigned int *m_rate) { ... realthreads = pending = cputhreads = 0; for_each_td(td, i) { int flags = 0;
/* * ->io_ops is NULL for a thread that has closed its * io engine */ if (td->io_ops && !strcmp(td->io_ops->name, "cpuio")) cputhreads++; else realthreads++; ... flags = WNOHANG; if (td->runstate == TD_EXITED) flags = 0;
/* * check if someone quit or got killed in an unusual way */ ret = waitpid(td->pid, &status, flags); if (ret < 0) { if (errno == ECHILD) { log_err("fio: pid=%d disappeared %d\n", (int) td->pid, td->runstate); td->sig = ECHILD; td_set_runstate(td, TD_REAPED); goto reaped; } perror("waitpid"); } else if (ret == td->pid) { if (WIFSIGNALED(status)) { int sig = WTERMSIG(status);
if (sig != SIGTERM && sig != SIGUSR2) log_err("fio: pid=%d, got signal=%d\n", (int) td->pid, sig); td->sig = sig; td_set_runstate(td, TD_REAPED); goto reaped; } if (WIFEXITED(status)) { if (WEXITSTATUS(status) && !td->error) td->error = WEXITSTATUS(status);
td_set_runstate(td, TD_REAPED); goto reaped; } }
...
|
讓進程休眠
sleep函數將一個進程掛起一段指定的時間。sleep函數返回0,否則返回剩下的要休眠的秒數(是可能的,因爲可能被信號中斷而過早的返回)。
另外一個有用的函數是pause,該函數讓調用函數休眠,直到該進程收到一個信號。
另外,如果想要睡眠一段更加精確的時間,可以用usleep函數,usleep函數能把進程掛起一段時間, 單位是微秒(千分之一毫秒)。
進程間的信號傳遞
def launch (address='', port=8000, static=False):
httpd = SplitThreadedServer((address, int(port)), SplitterRequestHandler)
core.register("WebServer", httpd)
httpd.set_handler("/", CoreHandler, httpd, True)
#httpd.set_handler("/foo", StaticContentHandler, {'root':'.'}, True)
#httpd.set_handler("/f", StaticContentHandler, {'root':'pox'}, True)
#httpd.set_handler("/cgis", SplitCGIRequestHandler, "pox/web/www_root")
print "WebServer hello"
if static is True:
httpd.add_static_dir('static', 'www_root', relative=True)
elif static is False:
pass
else:
static = static.split(",")
for entry in static:
if entry.lower() == "":
httpd.add_static_dir('static', 'www_root', relative=True)
continue
if ':' not in entry:
directory = entry
prefix = os.path.split(directory)
if prefix[1] == '':
prefix = os.path.split(prefix[0])
prefix = prefix[1]
assert prefix != ''
else:
prefix,directory = entry.split(":")
for entry in static:
if entry.lower() == "":
httpd.add_static_dir('static', 'www_root', relative=True)
continue
if ':' not in entry:
directory = entry
prefix = os.path.split(directory)
if prefix[1] == '':
prefix = os.path.split(prefix[0])
prefix = prefix[1]
assert prefix != ''
else:
prefix,directory = entry.split(":")
directory = os.path.expanduser(directory)
httpd.add_static_dir(prefix, directory, relative=False)
def run ():
try:
log.debug("Listening on %s:%i" % httpd.socket.getsockname())
httpd.serve_forever()
print "the value of dropped is %d" %(dropped)
except:
pass
log.info("Server quit")
thread = threading.Thread(target=run)
thread.daemon = True
thread.start(的返回)。
參考文獻
Caapp:深入理解計算機系統
源碼fio-2.1.10
pox的webcore組件