【UVM學習筆記】更加靈活的UVM—通信

系列文章目錄

【UVM學習筆記】UVM基礎—一文告訴你UVM的組成部分
【UVM學習筆記】UVM中的“類”


文章目錄

  • 系列文章目錄
  • 前言
  • 一、TLM是什么?
  • 二、put操作
    • 2.1、建立PORT和EXPORT的連接
    • 2.2 IMP組件
  • 三、get操作
  • 四、transport端口
  • 五、nonblocking端口
  • 六、analysis端口
  • 七、monitor與scoreboard之間的通信
  • 八、使用FIFO通信
  • 總結


前言

該專題用于記錄學習UVM芯片驗證的過程,主要學習書籍為經典的《UVM實戰》,同時也會去進行一些UVM的項目聯系。


一、TLM是什么?

TLM是Transaction Level Modeling(事務級建模)的縮寫。所謂transaction level是相對DUT中各個模塊之間信號線級別的通信來說的。
TLM通常有三種模式:

  1. put操作,通信的發起者A把一個transaction發送給B。在這個過程中,A稱為“發起者”,而B稱為“目標”。A具有的端口(用方框表示)稱為PORT,而B的端口(用圓圈表示)稱為EXPORT。這個過程中,數據流是從A流向B的。
  2. get操作。在這個過程中,A依然是“發起者”,B依然是“目標”,A上的端口依然是PORT,而B上的端口依然是EXPORT。這個過程中,數據流是從B流向A的。PORT和EXPORT體現的是控制流而不是數據流。

在這里插入圖片描述

  1. transport操作,transport操作相當于一次put操作加一次get操作,這兩次操作的“發起者”都是A,目標都是B。在這個過程中,數據流先從A流向B,再從B流向A。在現實世界中, 相當于是A向B提交了一個請求(request),而B返回給A一個應答(response)。

在這里插入圖片描述

二、put操作

2.1、建立PORT和EXPORT的連接

UVM中使用connect函數來建立連接關系。如A要和B通信(A是發起者),那么可以這么寫:A.port.connect(B.export)。下面是A的代碼部分:

class A extends uvm_component;`uvm_component_utils(A)uvm_blocking_put_port#(my_transaction) A_port;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void A::build_phase(uvm_phase phase);super.build_phase(phase);A_port = new("A_port", this);
endfunctiontask A::main_phase(uvm_phase phase);
endtask

然后得到B的代碼:

class B extends uvm_component;`uvm_component_utils(B)uvm_blocking_put_export#(my_transaction) B_export;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void B::build_phase(uvm_phase phase);super.build_phase(phase);B_export = new("B_export", this);
endfunctiontask B::main_phase(uvm_phase phase);
endtask

然后在env將兩者進行鏈接

class my_env extends uvm_env;A   A_inst;B   B_inst;function new(string name = "my_env", uvm_component parent);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);A_inst = A::type_id::create("A_inst", this);B_inst = B::type_id::create("B_inst", this);endfunctionextern virtual function void connect_phase(uvm_phase phase);`uvm_component_utils(my_env)
endclassfunction void my_env::connect_phase(uvm_phase phase);super.connect_phase(phase);A_inst.A_port.connect(B_inst.B_export);
endfunction

2.2 IMP組件

除了TLM中定義的PORT與EXPORT外,UVM中加入了第三種端口:IMP,起作用相當于在EXPORT后進行接受操作。
添加IMP后,A的代碼變為:

task A::main_phase(uvm_phase phase);my_transaction tr;repeat(10) begin#10;tr = new("tr");assert(tr.randomize());A_port.put(tr);end
endtask

在B中需要改動的要多一點:

class B extends uvm_component;`uvm_component_utils(B)uvm_blocking_put_export#(my_transaction) B_export;uvm_blocking_put_imp#(my_transaction, B) B_imp;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern function void connect_phase(uvm_phase phase);extern function void put(my_transaction tr);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void B::build_phase(uvm_phase phase);super.build_phase(phase);B_export = new("B_export", this);B_imp = new("B_imp", this);
endfunctionfunction void B::connect_phase(uvm_phase phase);super.connect_phase(phase);B_export.connect(B_imp);
endfunctionfunction void B::put(my_transaction tr);`uvm_info("B", "receive a transaction", UVM_LOW) tr.print();
endfunction

在上述連接關系中,IMP是作為連接的終點。在UVM中,只有IMP才能作為連接關系的終點。如果是PORT或者EXPORT作為終點,則會報錯。

三、get操作

get系列端口與put系列端口在某些方面完全相反。在這種連接關系中,數據流依然是從A到B,但是A由動作發起者變成了動作接收者,而B由動作接收者變成了動作發起者。
在這里插入圖片描述
B_port的類型為uvm_blocking_get_port,A_export的類型為uvm_blocking_get_export,A_imp的類型為uvm_blocking_get_imp。A的代碼為:

class A extends uvm_component;`uvm_component_utils(A)uvm_blocking_get_export#(my_transaction) A_export;uvm_blocking_get_imp#(my_transaction, A) A_imp;my_transaction tr_q[$];function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern function void connect_phase(uvm_phase phase);extern virtual  task get(output my_transaction tr);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void A::build_phase(uvm_phase phase);super.build_phase(phase);A_export = new("A_export", this);A_imp = new("A_imp", this);
endfunctionfunction void A::connect_phase(uvm_phase phase);super.connect_phase(phase);A_export.connect(A_imp); 
endfunctiontask A::get(output my_transaction tr);while(tr_q.size() == 0) #2;tr = tr_q.pop_front();
endtasktask A::main_phase(uvm_phase phase);my_transaction tr;repeat(10) begin#10;tr = new("tr");tr_q.push_back(tr); end
endtask

在A的get任務中,每隔2個時間單位檢查tr_q中是否有數據,如果有則發送出去。當B在其main_phase調用get任務時,會最終執行A的get任務。在A的connect_phase,需要把A_export和A_imp連接起來。下面是B的部分:

class B extends uvm_component;`uvm_component_utils(B)uvm_blocking_get_port#(my_transaction) B_port;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void B::build_phase(uvm_phase phase);super.build_phase(phase);B_port = new("B_port", this);
endfunctiontask B::main_phase(uvm_phase phase);my_transaction tr;while(1) beginB_port.get(tr);`uvm_info("B", "get a transaction", UVM_LOW) tr.print();end
endtask

在這些連接關系中,需要謹記的是連接的終點必須是一個IMP。

四、transport端口

在這里插入圖片描述
A代碼如下所示:

task A::main_phase(uvm_phase phase);my_transaction tr;my_transaction rsp;repeat(10) begin#10;tr = new("tr");assert(tr.randomize());A_transport.transport(tr, rsp);`uvm_info("A", "received rsp", UVM_MEDIUM)rsp.print();end
endtask

B中需要定義一個類型為uvm_blocking_transport_imp的IMP:

class B extends uvm_component;`uvm_component_utils(B)uvm_blocking_transport_imp#(my_transaction, my_transaction, B) B_imp;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern task transport(my_transaction req, output my_transaction rsp);
endclassfunction void B::build_phase(uvm_phase phase);super.build_phase(phase);B_imp = new("B_imp", this);
endfunctiontask B::transport(my_transaction req, output my_transaction rsp);`uvm_info("B", "receive a transaction", UVM_LOW) req.print();//do something according to req#5;rsp = new("rsp");
endtask

env中的代碼是:

function void my_env::connect_phase(uvm_phase phase);super.connect_phase(phase);A_inst.A_transport.connect(B_inst.B_imp);
endfunction

在A中調用transport任務,并把生成的transaction作為第一個參數。B中的transaport任務接收到這筆transaction,根據這筆transaction做某些操作,并把操作的結果作為transport的第二個參數發送出去。A根據接收到的rsp來決定后面的行為。

五、nonblocking端口

task A::main_phase(uvm_phase phase);my_transaction tr;repeat(10) begintr = new("tr");assert(tr.randomize());while(!A_port.can_put()) #10;void'(A_port.try_put(tr));end
endtask

由于端口變為了非阻塞的,所以在送出transaction之前需要調用can_put函數來確認是否能夠執行put操作。can_put最終會調用B中的can_put:

六、analysis端口

UVM中還有兩種特殊的端口:analysis_port和analysis_export。該端口有兩點需要注意的地方:

  • 一個analysis_port(analysis_export)可以連接多個IMP,analysis_port(analysis_export)與IMP 之間的通信是一對多的通信。analysis_port(analysis_export)更像是一個廣播。
  • put與get系列端口都有阻塞和非阻塞的區分。但是對于analysis_port和analysis_export來說,沒有阻塞和非阻塞的概念。

一個analysis_port可以和多個IMP相連接進行通信,但是IMP的類型必須是uvm_analysis_imp,否則會報錯。
在這里插入圖片描述
下面是A的代碼:

class A extends uvm_component;`uvm_component_utils(A)uvm_analysis_port#(my_transaction) A_ap;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern function void build_phase(uvm_phase phase);extern virtual  task main_phase(uvm_phase phase);
endclassfunction void A::build_phase(uvm_phase phase);super.build_phase(phase);A_ap = new("A_ap", this);
endfunctiontask A::main_phase(uvm_phase phase);my_transaction tr;repeat(10) begin#10;tr = new("tr");assert(tr.randomize());A_ap.write(tr);end
endtask

A的代碼很簡單,只是簡單地定義一個analysis_port,并在main_phase中每隔10個時間單位寫入一個transaction。
B的代碼為:

function void B::write(my_transaction tr);`uvm_info("B", "receive a transaction", UVM_LOW) tr.print();
endfunction

在env中通過下面方式進行連接:

function void my_env::connect_phase(uvm_phase phase);super.connect_phase(phase);A_inst.A_ap.connect(B_inst.B_imp);A_inst.A_ap.connect(C_inst.C_imp);
endfunction

上面只是一個analysis_port與IMP相連的例子。analysis_export和IMP也可以這樣相連接,只需將上面例子中的uvm_analysis_port改為uvm_analysis_export就可以。

七、monitor與scoreboard之間的通信

和上一個一樣,在兩段分別進行定義,monitor的代碼為:

task my_monitor::main_phase(uvm_phase phase);my_transaction tr;while(1) begintr = new("tr");collect_one_pkt(tr);ap.write(tr);end
endtask

scoreboard的代碼為:

function void my_scoreboard::write_monitor(my_transaction tr);my_transaction  tmp_tran;bit result;if(expect_queue.size() > 0) begintmp_tran = expect_queue.pop_front();result = tr.compare(tmp_tran);if(result) begin `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);endelse begin`uvm_error("my_scoreboard", "Compare FAILED");$display("the expect pkt is");tmp_tran.print();$display("the actual pkt is");tr.print();endendelse begin`uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");$display("the unexpected pkt is");tr.print();end
endfunction

之后在env中可以使用connect連接。
由于monitor與scoreboard在UVM樹中并不是平等的兄妹關系,這里選擇下面的連接方式:
在agent中聲明一個ap,但是不實例化它,讓其指向monitor中的ap。在env中可以直接連接agent的ap到scoreboard的imp:

agent:
class my_agent extends uvm_agent ; uvm_analysis_port #(my_transaction) ap; ...  function void my_agent::connect_phase(uvm_phase phase); ap = mon.ap; ...  endfunction 
endclass 
env:
function void my_env::connect_phase(uvm_phase phase); o_agt.ap.connect(scb.scb_imp); ...  
endfunction

在上面的例子中,scoreboard只接收一路數據。但在現實情況中,scoreboard除了接收monitor的數據之外,還要接收reference model的數據。相應的scoreboard就要再添加一個 uvm_analysis_imp的IMP。此時問題就出現了,由于接收到的兩路數據應該做不同的處理,所以這個新的IMP也要有一個write任務與其對應。但是write只有一個,怎么辦?
可以使用宏定義的方法:

`uvm_analysis_imp_decl(_monitor)
`uvm_analysis_imp_decl(_model)
class my_scoreboard extends uvm_scoreboard;my_transaction  expect_queue[$];uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp; uvm_analysis_imp_model#(my_transaction, my_scoreboard) model_imp;`uvm_component_utils(my_scoreboard)extern function new(string name, uvm_component parent = null);extern virtual function void build_phase(uvm_phase phase);extern virtual task main_phase(uvm_phase phase);
endclass 

上述代碼通過宏uvm_analysis_imp_decl聲明了兩個后綴_monitor和_model。
當與monitor_imp相連接的analysis_port執行write函數時,會自動調用write_monitor函數,而與model_imp相連接的analysis_port執行write 函數時,會自動調用write_model函數。

八、使用FIFO通信

使用fifo的方法能夠讓兩個端口都能實現主動的接收,因此下面的例子便是利用FIFO來實現monitor和scoreboard的通信。
FIFO的本質是一塊緩存加兩個IMP。在monitor與FIFO的連接關系中,monitor中依然是analysis_port,FIFO中是uvm_analysis_imp,數據流和控制流的方向相同。在scoreboard與FIFO的連接關系中,scoreboard中使用blocking_get_port端口:

class my_scoreboard extends uvm_scoreboard;my_transaction  expect_queue[$];uvm_blocking_get_port #(my_transaction)  exp_port[16];uvm_blocking_get_port #(my_transaction)  act_port;`uvm_component_utils(my_scoreboard)extern function new(string name, uvm_component parent = null);extern virtual function void build_phase(uvm_phase phase);extern virtual task main_phase(uvm_phase phase);
endclass 

而FIFO中使用的是一個get端口的IMP。在這種連接關系中,控制流是從scoreboard到FIFO,而數據流是從FIFO到scoreboard。

在env中連接方式如下:

function void my_env::connect_phase(uvm_phase phase);super.connect_phase(phase);i_agt.ap.connect(agt_mdl_fifo.analysis_export);mdl.port.connect(agt_mdl_fifo.blocking_get_export);for(int i = 0; i < 16; i++) beginmdl.ap[i].connect(mdl_scb_fifo[i].analysis_export);scb.exp_port[i].connect(mdl_scb_fifo[i].blocking_get_export);endo_agt.ap.connect(agt_scb_fifo.analysis_export);scb.act_port.connect(agt_scb_fifo.blocking_get_export); 
endfunction

FIFO中有兩個IMP,但是在上面的連接關系中,FIFO中卻是EXPORT,這是為什么呢?實際上,FIFO中的analysis_export和blocking_get_export雖然名字中有關鍵字export,但是其類型卻是IMP。UVM為了掩飾IMP的存在,在它們的命名中加入了export關鍵字。
但事實上,FIFO上的端口并不局限于上述兩個,一個FIFO中有眾多的端口。端口列表如下:

總結

總結來說,這一章主要講了數據在UVM中的傳遞方式,學習這一章可以更好的編寫靈活性更高的UVM代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75416.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75416.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75416.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uni-app項目上傳至gitee方法詳細教程

1. 準備工作 1.1 安裝 Git 下載并安裝 Git&#xff1a;前往 Git 官網&#xff0c;根據操作系統下載安裝包。 配置用戶名和郵箱&#xff08;需與 Gitee 賬號一致&#xff09;&#xff1a; git config --global user.name "你的Gitee用戶名" git config --global use…

走向多模態AI之路(三):多模態 AI 的挑戰與未來

目錄 前言一、多模態 AI 真的成熟了嗎&#xff1f;二、多模態 AI 的主要挑戰2.1 計算資源消耗&#xff1a;模型復雜度帶來的成本問題2.2 數據標注困難&#xff1a;跨模態數據集的挑戰2.3 對齊和融合的難點2.4 泛化能力與魯棒性2.5 倫理與隱私問題 三、研究方向與未來發展3.1 輕…

STM32單片機入門學習——第12節: [5-2]對射式紅外傳感器計次旋轉編碼器計次

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難&#xff0c;但我還是想去做&#xff01; 本文寫于&#xff1a;2025.04.03 STM32開發板學習——第12節: [5-2]對射式紅外傳感器計次&旋轉編碼器計次 前言…

匯編學習之《jcc指令》

JCC&#xff08;Jump on Condition Code&#xff09;指的是條件跳轉指令&#xff0c;c中的就是if-else, while, for 等分支循環條件判斷的邏輯。它包括很多指令集&#xff0c;各自都不太一樣&#xff0c;接下來我盡量將每一個指令的c 源碼和匯編代碼結合起來看&#xff0c;加深…

深度解析算法之滑動窗口

12滑動窗口—將 x 減到 0 的最小操作數 題目傳送門 題目描述&#xff1a; 給你一個整數數組 nums 和一個整數 x 。每一次操作時&#xff0c;你應當移除數組 nums 最左邊或最右邊的元素&#xff0c;然后從 x 中減去該元素的值。請注意&#xff0c;需要 修改 數組以供接下來的操…

[MySQL初階]MySQL表的操作

MySQL表的操作 1. 創建表2. 查看表結構3. 修改表&#xff08;修改表的屬性而非表的數據&#xff09;4. 刪除表 1. 創建表 語法&#xff1a; CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校驗規則 engine 存儲…

sqlalchemy詳細介紹以及使用方法

SQLAlchemy是一個Python的ORM&#xff08;對象關系映射&#xff09;工具&#xff0c;它允許開發者使用Python代碼來操作數據庫而不必直接編寫SQL語句。SQLAlchemy提供了一種抽象層&#xff0c;使開發者可以通過簡單的Python對象來表示數據庫表和記錄&#xff0c;從而實現對數據…

圖解AUTOSAR_SWS_LINDriver

AUTOSAR LIN驅動詳解文檔 基于AUTOSAR標準的本地互聯網絡(LIN)驅動程序技術規范解析 目錄 1. 概述 1.1 AUTOSAR LIN驅動簡介1.2 LIN協議基礎2. LIN驅動架構 2.1 類圖結構2.2 狀態機設計3. LIN幀結構 3.1 基本幀組成3.2 PID結構4. LIN驅動配置 4.1 主要配置參數4.2 配置結構5. L…

《網絡管理》實踐環節03:snmp服務器上對網絡設備和服務器進行初步監控

蘭生幽谷&#xff0c;不為莫服而不芳&#xff1b; 君子行義&#xff0c;不為莫知而止休。 應用拓撲圖 3.0準備工作 所有Linux服務器上&#xff08;服務器和Agent端&#xff09;安裝下列工具 yum -y install net-snmp net-snmp-utils 保證所有的HCL網絡設備和服務器相互間能…

2025年內外網文件交換系統排名分析

在時代&#xff0c;企業的日常運營離不開內外網文件的交換。然而&#xff0c;傳統的文件傳輸方式難以滿足企業對多方面的要求。以下是一些備受關注的內外網文件交換系統及其排名分析。 第一名&#xff1a;陽途內外網文件交換系統 陽途內外網文件交換系統是一款專為解決內外網…

【Centos】centos7內核升級-親測有效

相關資源 通過網盤分享的文件&#xff1a;腳本升級 鏈接: https://pan.baidu.com/s/1yrCnflT-xWhAPVQRx8_YUg?pwd52xy 提取碼: 52xy –來自百度網盤超級會員v5的分享 使用教程 將腳本文件上傳到服務器的一個目錄 執行更新命令 yum install -y linux-firmware執行腳本即可 …

Qt進階開發:QDirModel的使用

文章目錄 一、QDirModel的基本介紹二、QDirModel的基本使用2.1 在 QTreeView 中顯示文件系統2.2 在 QListView 顯示當前目錄2.3 在 QTableView 中使用 三、QDirModel的常用API1. 構造 & 目錄操作1.1 創建 QDirModel1.2 設置根目錄 2. 過濾 & 排序2.1 過濾文件類型2.2 設…

牛客 除2問題

除2&#xff01; 貪心堆 讓偶數入堆 注意點&#xff1a; 1.判斷堆是否為空再進行操作 2. 為了防止超時&#xff0c;我們采取先求和的方式&#xff0c;后面調整之后再減掉&#xff0c;可以節省一次遍歷的時間。 3.注意數據范圍&#xff0c;要用long long #include<iost…

#MySQL 語句大全(完整實用教程)

&#x1f4cc; MySQL 語句大全&#xff08;完整實用教程&#xff09; &#x1f4cc; 1. 數據庫操作 ? 創建數據庫 CREATE DATABASE mydb; -- 創建名為 mydb 的數據庫? 使用數據庫 USE mydb; -- 選擇數據庫? 刪除數據庫 DROP DATABASE mydb; -- 刪除數據庫&#xff08…

萬字重談C++——類和對象篇

什么是類&#xff1f; 在編程中&#xff0c;類是用來創建對象的模板。可以把類看作一個藍圖&#xff0c;它定義了對象的屬性&#xff08;特征&#xff09;和方法&#xff08;行為&#xff09;。例如&#xff0c;如果我們有一個“學生”的類&#xff0c;它可能包含學生的名字、…

18認識Qt坐標系

平面直角坐標系(笛卡爾坐標系) 數學上的坐標系 右手坐標系 計算機中的坐標系 左手坐標系 坐標系的原點(0,0) 就是屏幕的左上角 /窗口的左上角 給 Qt 的某個控件,設置位置,就需要指定坐標.對于這個控件來說, 坐標系原點就是相對于父窗口/控件的. QPushButton 的父元素/父控件/父…

量子計算與人工智能的結合:未來科技的雙重革命

引言 在過去幾十年里&#xff0c;人工智能&#xff08;AI&#xff09;和計算能力的提升一直是推動科技進步的重要力量。然而&#xff0c;隨著深度學習和大規模數據處理的發展&#xff0c;傳統計算架構的算力瓶頸逐漸顯現&#xff0c;人工智能的訓練和推理效率受到了限制。在此背…

SEO長尾詞優化策略精要

內容概要 長尾關鍵詞優化是SEO策略中實現精準流量捕獲的核心環節。本文從定位方法、搜索意圖分析、詞庫構建三個維度切入&#xff0c;系統闡述如何通過數據化工具篩選高轉化潛力詞&#xff0c;并結合用戶行為路徑優化內容架構。具體而言&#xff0c;內容將覆蓋關鍵詞挖掘工具的…

基于大模型的主動脈瓣病變預測及治療方案研究報告

目錄 一、引言 1.1 研究背景 1.2 研究目的 1.3 研究意義 二、大模型預測主動脈瓣病變原理 2.1 大模型介紹 2.2 數據收集與處理 2.3 模型訓練與優化 三、術前預測與評估 3.1 主動脈瓣病變類型及程度預測 3.2 患者整體狀況評估 3.3 手術風險預測 四、術中應用與監測…

進程和內存管理

目錄 一.進程的基本信息 1.1進程的定義 1.2進程的特征 1.3進程的組成 1.4線程產生的背景 1.5線程的定義 1.6進程與線程的區別 1.7進程的類別 1.8進程的優先級 1.8.1進程優先級的概念 1.8.2PRI和NI 1.9僵尸進程 1.9.1僵尸進程的定義 1.9.2僵尸進程產生的原因 1.9…