操作系統面試題練習,精品解析
選擇題
引入多道程序的目的在於()。
A.充分利用CPU,減少CPU等待時間
B.提高實時響應速度
C.有利於代碼共享,減少主、輔存信息交換量
D.充分利用存儲器
解析
多道程序設計,是在內存中同時存放幾道相互獨立的程序,無論何時都有程序在執行,充分利用CPU。
下列關於管道(Pipe)通信的敘述中,正確的是 。
A.一個管道可實現雙向數據傳輸
B.管道的容量僅受磁碟容量大小限制
C.進程對管道進行讀操作和寫操作都可能被阻塞
D.一個管道只能有一個讀進程或一個寫進程對其操作
解析
管道的用途
- 管道是最早出現的進程間通信的手段。在shell中執行命令,經常會將上一個命令的輸出作為下一個命令的輸入,由多個命令配合完成一件事情。而這就是通過管道來實現的。
- 在圖9-3中,進程who的標準輸出,通過管道傳遞給下游的wc進程作為標準輸入,從而通過相互配合完成了一件任務。
管道的操作
- 管道的作用是在具有親緣關係的進程之間傳遞消息,所謂有親緣關係,是指有同一個祖先。
所以管道並不是只可以用於父子進程通信,也可以在兄弟進程之間還可以用在祖孫之間等,反正只要共同的祖先調用了pipe函數,打開的管道文件就會在fork之後,被各個後代所共享
。
- 不過由於管道是位元組流通信,沒有消息邊界,多個進程同時發送的位元組流混在一起,則無法分辨消息,所有管道一般用於2個進程之間通信,另外管道的內容讀完後不會保存,管道是單向的,一邊要麼讀,一邊要麼寫,不可以又讀又寫,想要一邊讀一邊寫,那就創建2個管道,如下圖
- 管道是一種文件,可以調用read、write和close等操作文件的介面來操作管道。
另一方面管道又不是一種普通的文件,它屬於一種獨特的文件系統:pipefs。管道的本質是內核維護了一塊緩衝區與管道文件相關聯,對管道文件的操作,被內核轉換成對這塊緩衝區內存的操作
。下面我們來看一下如何使用管道。
#include<unistd.h>
int pipe(int fd[2])
如果成功,則返回值是0,如果失敗,則返回值是-1,並且設置errno。
成功調用pipe函數之後,會返回兩個打開的文件描述符,一個是管道的讀取端描述符pipefd[0],另一個是管道的寫入端描述符pipefd[1]。管道沒有文件名與之關聯,因此程序沒有選擇,只能通過文件描述符來訪問管道,只有那些能看到這兩個文件描述符的進程才能夠使用管道。那麼誰能看到進程打開的文件描述符呢?只有該進程及該進程的子孫進程才能看到。這就限制了管道的使用範圍。
- 成功調用pipe函數之後,可以對寫入端描述符pipefd[1]調用write,向管道裡面寫入數據,代碼如下所示:
write(pipefd[1],wbuf,count);
一旦向管道的寫入端寫入數據後,就可以對讀取端描述符pipefd[0]調用read,讀出管道裡面的內容。如下所示,管道上的read調用返回的位元組數等於請求位元組數和管道中當前存在的位元組數的最小值。如果當前管道為空,那麼read調用會阻塞(如果沒有設置O_NONBLOCK標誌位的話)。
管道非法read與write內核實現解析
調用pipe函數返回的兩個文件描述符中,讀取端pipefd[0]支持的文件操作定義在read_pipefifo_fops,寫入端pipefd[1]支持的文件操作定義在write_pipefifo_fops,其定義如下:
const struct file_operations read_pipefifo_fops = { //讀端相關操作
.llseek = no_llseek,
.read = do_sync_read,
.aio_read = pipe_read,
.write = bad_pipe_w, //一旦寫,將調用bad_pipe_w
.poll = pipe_poll,
.unlocked_ioctl = pipe_ioctl,
.open = pipe_read_open,
.release = pipe_read_release,
.fasync = pipe_read_fasync,
};
const struct file_operations write_pipefifo_fops = {//寫端相關操作
.llseek = no_llseek,
.read = bad_pipe_r, //一旦讀,將調用bad_pipe_r
.write = do_sync_write,
.aio_write = pipe_write,
.poll = pipe_poll,
.unlocked_ioctl = pipe_ioctl,
.open = pipe_write_open,
.release = pipe_write_release,
.fasync = pipe_write_fasync,
};
我們可以看到,對讀取端描述符執行write操作,內核就會執行bad_pipe_w函數;對寫入端描述符執行read操作,內核就會執行bad_pipe_r函數。這兩個函數比較簡單,都是直接返回-EBADF。因此對應的read和write調用都會失敗,返回-1,並置errno為EBADF。
static ssize_t
bad_pipe_r(struct file filp, char __user buf, size_t count, loff_t ppos)
{
return -EBADF; //返回錯誤
}
static ssize_t
bad_pipe_w(struct file filp, const char __user buf, size_t count,loff_t ppos)
{
return -EBADF;
}
管道通信原理及其親戚通信解析
父子進程通信解析
我們只介紹了pipe函數介面,至今尚看不出來該如何使用pipe函數進行進程間通信。調用pipe之後,進程發生了什麼呢?請看圖9-5。
可以看到,調用pipe函數之後,系統給進程分配了兩個文件描述符,即pipe函數返回的兩個描述符。該進程既可以往寫入端描述符寫入信息,也可以從讀取端描述符讀出信息。可是一個進程管道,起不到任何通信的作用。這不是通信,而是自言自語。
如果調用pipe函數的進程隨後調用fork函數,創建了子進程,情況就不一樣了。fork以後,子進程複製了父進程打開的文件描述符(如圖9-6所示),兩條通信的通道就建立起來了。此時,可以是父進程往管道里寫,子進程從管道裡面讀;也可以是子進程往管道里寫,父進程從管道裡面讀。這兩條通路都是可選的,但是不能都選。原因前面介紹過,管道裡面是位元組流,父子進程都寫、都讀,就會導致內容混在一起,對於讀管道的一方,解析起來就比較困難。常規的使用方法是父子進程一方只能寫入,另一方只能讀出,管道變成一個單向的通道,以方便使用。如圖9-7所示,父進程放棄讀,子進程放棄寫,變成父進程寫入,子進程讀出,成為一個通信的通道…
父進程如何放棄讀,子進程又如何放棄寫
?其實很簡單,父進程把讀埠pipefd[0]這個文件描述符關閉掉,子進程把寫埠pipefd[1]這個文件描述符關閉掉就可以了,示例代碼如下:
int pipefd[2];
pipe(pipefd);
switch(fork())
{
case -1:
/fork failed, error handler here/
case 0: /子進程/
close(pipefd[1]) ; /關閉掉寫入端對應的文件描述符/
/子進程可以對pipefd[0]調用read/
break;
default: /父進程/
close(pipefd[0]); /父進程關閉掉讀取端對應的文件描述符/
/父進程可以對pipefd[1]調用write, 寫入想告知子進程的內容/
break
}
親緣關係的進程管道通信解析
- 圖9-8也講述了如何在兄弟進程之間通過管道通信。如圖9-8所示,父進程再次創建一個子進程B,子進程B就持有管道寫入端,這時候兩個子進程之間就可以通過管道通信了。父進程為了不干擾兩個子進程通信,很自覺地關閉了自己的寫入端。從此管道成為了兩個子進程之間的單向的通信通道。在shell中執行管道命令就是這種情景,只是略有特殊之處,其特殊的地方是管道描述符佔用了標準輸入和標準輸出兩個文件描述符
有 9 個村莊,其坐標位置如下表所示:
現在要蓋一所郵局為這 9 個村莊服務,請問郵局應該蓋在( ) 才能使到郵局到這 9 個村莊的總距離和最短。
A.(4.5,0)
B.(4.5,4.5)
C.(5,5)
D.(5,0)
磁碟設備的I/O控制主要是採取( )方式。
A.位
B.位元組
C.幀
D.DMA
解析
I/O控制方式主要有程序查詢方式、中斷方式、DMA方式和通信方式。
1、程序查詢方式
程序查詢方式也稱為程序輪詢方式,該方式採用用戶程序直接控制主機與外部設備之間輸入/輸出操作。CPU必須不停地循環測試I/O設備的狀態埠,當發現設備處於準備好(Ready)狀態時,CPU就可以與I/O設備進行數據存取操作。這種方式下的CPU與I/O設備是串列工作的,輸入/輸出一般以位元組或字為單位進行。這個方式頻繁地測試I/O設備,I/O設備的速度相對來說又很慢,極大地降低了CPU的處理效率,並且僅僅依靠測試設備狀態位來進行數據傳送,不能及時發現傳輸中的硬體錯誤。但是這種方式的過程很簡單,易理解,並且不需要額外硬體。
2、中斷方式
當I/O設備結束(完成、特殊或異常)時,就會向CPU發出中斷請求信號,CPU收到信號就可以採取相應措施。當某個進程要啟動某個設備時,CPU就向相應的設備控制器發出一條設備I/O啟動指令,然後CPU又返回做原來的工作。CPU與I/O設備可以並行工作,與程序查詢方式相比,大大提高了CPU的利用率。但是在中斷方式下,同程序查詢方式一樣,也是以位元組或字為單位進行。但是該方法大大降低了CPU的效率,因為當中斷髮生的非常頻繁的時候,系統需要進行頻繁的中斷源識別、保護現場、中斷處理、恢復現場。這種方法對於以「塊」為存取單位的塊設備,效率是低下的。
3、DMA(直接內存存取)方式
DMA方式也稱為直接主存存取方式,其思想是:允許主存儲器和I/O設備之間通過「DMA控制器(DMAC)」直接進行批量數據交換,除了在數據傳輸開始和結束時,整個過程無須CPU的干預。每傳輸一個「塊」數據只需要佔用一個主存周期。
DMA方式下,一個完整的數據傳輸過程:
1)DMA初始化
當進程需要I/O設備進行數據輸入輸出時,CPU對DMA控制器初始化,並向I/O埠發出操作命令,提供準備傳輸的數據起始地址,需要傳送的數據長度等信息送入到DMA控制器中的主存地址寄存器和傳送位元組計數器中。
2)DMA傳輸
DMA控制器獲得匯流排控制權後,進行輸出讀寫命令,直接控制主存與I/O設備之間的傳輸。在DMA控制器的控制下,數據傳輸過程中不需要CPU的參與。
3)DMA結束
當完成本次數據傳輸後,DMA控制器釋放匯流排控制權,並向I/O設備埠發出結束信號。
4、I/O通道控制方式
通道(Channel)也稱為外圍設備處理器、輸入輸出處理機,是相對於CPU而言的。是一個處理器。也能執行指令和由指令的程序,只不過通道執行的指令是與外部設備相關的指令。是一種實現主存與I/O設備進行直接數據交換的控制方式,與DMA控制方式相比,通道所需要的CPU控制更少,一個通道可以控制多個設備,並且能夠一次進行多個不連續的數據塊的存取交換,從而大大提高了計算機系統效率
通道的類型
1)位元組多路通道
2)數組選擇通道
3)數組多路通道
TAG:青峰科技 |