Ⅰ 求教,linux下網口虛擬串口驅動程序
開發虛擬串口驅動程序
虛擬串口就是當本地並沒有對應的串口硬體設備,而為應用層提供串口設備一樣的系統調用介面,以兼容原本使用本地串口的應用軟體的「虛」設備。本文作者給出了一種在Windows平台上實現虛擬串口的方法,由此實現的「串口」具有真實串口完全相同的系統調用介面。
在很多應用中需要用到虛擬串口,如在Modem卡出現之前,已經有了接在計算機串口上的外部Modem,而且各種拔號程序也是通過串口與外部Modem通信的。為了讓已有的拔號程序不做修改,像使用外部Modem一樣使用內置卡,就需要內置卡的驅動程序虛擬一個串口設備。又如當前工業界使用的一些串口伺服器,往往有8個或16個甚至更多的串口,以連接多個串口設備,再通過一個網卡直接連入乙太網。與它在同一網路上的計算機就通過乙太網與串口伺服器上掛接的串口設備通信。為了讓計算機中原來使用本地串口的軟體兼容,就需要在計算機上提供虛擬串口驅動。
虛擬串口的設計關鍵在於,該「串口」實現後必須具有與真實串口完全相同的系統調用介面。要做到這點,從已有的串口設備驅動程序上做修改是最佳捷徑。下文就介紹以Windows NT上的串口驅動程序為基礎,開發可運行於Windows NT、Windows 2000、Windows XP的各個版本虛擬串口驅動程序。
串口驅動中使用的幾個鏈表
由於串口是雙工設備,在一個讀請求發出來還沒有完成之前,同時可以發出寫請求,加上在驅動程序層所有I/O請求都要求非同步完成,即前一個請求尚沒有完成,下一個相同的請求可能又來了。為此,串口驅動程序需要使用多個雙向鏈表數據結構來處理各種IRP(I/O Request Packet,I/O請求包)。當收到一個IRP,先判斷是否可立即完成,可以馬上處理並返回,如果不允許則將IRP插在相應鏈表尾,在適當的時候如設備有空閑時處理,這時往往會產生一個硬體中斷,激發DPC(Deferred Procere Call,暫緩過程調用)過程,由DPC處理函數逐個從鏈表頭取出IRP並試著完成它。串口驅動中有以下幾個鏈表和DPC(在serial.h中有定義):
ReadQueue 和 CompleteReadDpc
用於保存Read IRP的鏈表和用於調度的DPC,與DPC對應的處理函數是SerialCompleteRead,它在read.c文件中,該函數的主要任務就是從ReadQueue中提取下一個IRP,並試著完成它。
WriteQueue 和 CompleteWriteDpc
用於保存Write IRP的鏈表和對應的DPC,與DPC對應的函數是SeriaCompleteWrite,它的實現在write.c中,該函數負責從WriteQueue中提取IRP,並試著完成它。
MaskQueue 和 CommWaitDpc
這一對鏈表用於處理Windows串口驅動的一個特性:事件驅動機制。它允許應用程序預設一個事件標志,而後等待與標志對應事件發生。DPC所調用的函數是SerialCompleteWait,它實現在Waitmask.c文件中,該函數也是試著從MaskQueue中提取IRP並完成它。
PurgeQueue
該鏈表與前面幾個稍有不同,它沒有與之相對應的DPC機制,而是在每次收到Purge請求時從PurgeQueue中逐個提取IRP並試著完成,因某種原因不能完成時則插入鏈表。相應的函數是purge.c文件中的SerialStartPurge。
以上機制是串口驅動程序的重要實現方法,在虛擬串口驅動中需要保留,但不同的是,硬體串口驅動中是ISR(中斷服務程序)根據收、發或MODEM中斷來激發相應的DPC,而在虛擬串口驅動中將因實際情況不同會有不同的激發機制。
DriverEntry的實現
DriverEntry是驅動程序的入口函數,相當於應用程序C語言中的main函數,開發一個虛擬串口驅動首先要修改的就是它。它的函數實體在initunlo.c文件中。只是在虛擬串口驅動中由於不與具體的硬體打交道,就不存在硬體資源分析、硬體初始化、判斷其工作狀態等處理,只需要為虛擬串建立設備對象、符號鏈接和初始化數據結構。一個典型函數實現大體如下:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
/*填寫DriverObject->MajorFunction[]數組*/
/*建立設備對象*/
/*初始化SERIAL_DEVCIE_EXETENSION數據結構*/
Status = IoCreateDevice(DriverObject, sizeof(SERIAL_DEVICE_EXTENSION), &uniNameString, FILE_DEVICE_SERIAL_PORT, 0,TRUE,&deviceObject);
//初始化所有鏈表
InitializeListHead(&extension->ReadQueue);
InitializeListHead(…);
…;
//初始化所有DPC
KeInitializeDpc(&extension->CompleteReadDpc,SerailCompleteRead,extension);
KeInitializeDpc(…);
/*建立符號鏈接*/
SerialSetupExternalNaming(extension);
return Status;
}
SerialRead和SerialCompleteRead的實現
函數SerailRead和SerialCompleteRead決定了對Read IRP的響應策略,它們都存於read.c中。以串口伺服器要用的虛擬串口為例,當串口伺服器收到來自外部數據時將通過網路發至計算機,計算機則產生相應的網路中斷並進行協議數據處理。網路接收線程緩存新收到的數據並激活CompleteReadDpc,從而SerialCompleteReadIrp得到調用,它再調用CompleteReadIrp對每個IRP進行處理。它們的實現大體如下:
NTSTATUS SerialRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
/*此處略去變數聲明和初始化*/
/*提取IRP中相關的數據*/
stack = IoGetCurrentIrpStackLocation(Irp);
ReadLen = stack->Parameters.Read.Length;
/*先看本地緩沖有數據否?有的話先讀取*/
if(Extension->InCounter > 0 )
{ //注意這里要加鎖,以防數據訪問沖突
KeAcquireSpinLock(&Extension->
ReadBufferLock,&lIrql);
FirstRead = (ReadLen>Extension->
InCounter)? Extension->InCounter: ReadLen;
RtlCopyMemory(Irp->AssociatedIrp.
SystemBuffer,Extension->pInBuffer,FirstRead);
Extension->InCounter -= FirstRead;
ReadLen -= FirstRead;
KeReleaseSpinLock(&Extension->
ReadBufferLock,lIrql);//釋放鎖
}
/*是否已讀到足夠數據?是的話則完成該IRP*/
if( 0 == ReadLen)
{
status=STATUS_SUCCESS;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = FirstRead;
IoCompleteRequest(Irp,0);
return status;
}
/*沒有則將IRP插入隊列中,通過網路向串口伺服器發出讀數據請求*/
IoMarkIrpPending(Irp);
InsertWaitList(Extension->ReadQueue,Irp);
status = TdiSendAsync(Extension->ComChannel,pAckPacket,PacketLen(pAckPacket),(PVOID)ReadAckComplete,Irp);
/*返回PENDING,表示該IRP尚沒有完成*/
return STATUS_PENDING;
}
Void CompleteReadIrp(IN PSERIAL_DEVICE_EXTENSION extension,IN PIRP Irp,IN PUCHAR pInData,IN ULONG Length )
{
/*此處略去變數聲明和初始化*/
/*讀取新數據*/
ReadLen = (ReadLen > Length)? Length : ReadLen;
if(ReadLen != 0)
{
RtlCopyMemory(pReadAsync->
pReadBuffer,pInData,ReadLen);
pReadAsync->pReadBuffer += ReadLen;
pReadAsync->ReadAlready += ReadLen;
extension->PerfStats.ReceivedCount +=
ReadLen;
}
else
{
/*因為串口伺服器端只有在已經有了相應的數據或超過時間(此時,Length=0)才會發來應答並激活本DPC過程,所以此時已經超時,為了便於結束本IRP,這里有意改變TotalNeedRead,造成接收完畢的假象*/
pReadAsync->TotalNeedRead =
pReadAsync->ReadAlready;
}
if(pReadAsync->TotalNeedRead == pReadAsync->ReadAlready)
{
/*該IRP是否已經接收完畢,是的話則結束該
IRP*/
EndReadIrp(Irp);
/*從ReadQueue中取下一個IRP*/
}
/*本IRP沒有完成也沒有超時,則繼續等待本DPC下次被激活,注意此時要判斷IRP是否被要求取消*/
}
SerialWrite和SerailCompleteWrite的實現
SerialWrite和SerailCompleteWrite決定了Write IRP的實現。在SerialWrite中調用了網路發送函數TdiSendAsync,當該發送完成後將激活CompleteWriteDpc,調度SerialCompleteWrite函數,而它主要就是取出當前的WriteIRP,設置已經發送的數據數量,調用CompleteWriteIrp做該IRP的進一步處理。它們大體如下:
NTSTATUS SerialWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
/*此處略去變數聲明和初始化*/
/*從IRP中提取有關數據*/
stack=IoGetCurrentIrpStackLocation(Irp);
SendLen = stack->Parameters.Write.Length;
/*為網路發送和非同步操作分配緩沖,在CompleteWrite中全部數據發送完後釋放*/
pWriteAsync = ExAllocatePool(NonPagedPool,
SendLen+PACKET_HEADER_LEN+sizeof(WRITE_ASYNC));
if(pWriteAsync == NULL)
{
//錯誤處理
}
//保存非同步數據
…
//設置網路發送數據包
BuildDataPacket(pPacket,WRITE,(USHORT)SendLen,pWriteAsync->pWriteBuffer);
/*先將IRP暫時阻塞並插入隊列,在CompleteWrite中完成*/
IoMarkIrpPending(Irp);
InsertWaitList(extension->WriteQueue, Irp);
/*將寫請求和相關數據通過網路發向串口伺服器,由它負責將數據傳到具體串口設備*/
status = TdiSendAsync(Extension->ComChannel,pPacket,PacketLen(pPacket),(PVOID)CompleteWriteIrp,Irp);
//統計數據累加
Extension->PerfStats.TransmittedCount += SendLen;
return STATUS_PENDING;
}
NTSTATUS CompleteWriteIrp(IN PDEVICE_OBJECT deviceobject,IN PIRP pIrp,IN PVOID context)
{
/*此處略去變數聲明和初始化*/
SendLen=pWriteAsync->TotalNeedWrite - pWriteAsync->WroteAlready;
if(SendLen == 0)//全部數據發送完畢
{
EndWaitWriteIrp(pWriteIrp,STATUS_SUCCESS,
pWriteAsync->WroteAlready,pWriteAsync);
//從WriteQueue中取下一個IRP;
}
else //發送剩餘數據
{
if(pWriteIrp->Cancel)
{
//IRP被要求取消,完成WriteIrp
EndWaitWriteIrp(pWriteIrp,STATUS_CANCELLED,
pWriteAsync->WroteAlready,pWriteAsync);
return STATUS_CANCELED;
}
else
{
//再次設置網路數據包並發送
BuildDataPacket(…);
status = TdiSendAsync(…);
//統計數據累加
Extension->PerfStats.TransmittedCount +=
SendLen;
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
}
其他幾個介面函數的實現
除Read/Write外,SerialUnload、SerialCreateOpen、 SerialClose、SerialCleanup、SerailFlush等調用介面是硬體相關性比較弱的介面函數,基本不要修改,直接刪除原來操作硬體的部分即可。復雜一點就是SerialIoControl,該介面函數包含有大量設置、讀取串口硬體狀態的處理,可建立一個本地數據結構隨時保存虛擬串口的當前硬體狀態。同時為了保證串口伺服器端的真實串口狀態和上層軟體要求的一致,需要將所有設置請求通過網路發送到伺服器端,由它負責改變真實硬體的狀態。
Ⅱ 如何在虛擬機上運行linux系統
下載VMWare解壓後根據提示正觸安裝VMWare到硬碟中
(1) 建立虛擬機
A.用滑鼠左建雙擊桌面中的"VMware workstation"圖標,運行虛擬機
B.建立一台虛擬機。點擊「FILE(文件)」-「NEW(新建)」--「NewVirtual Machine(
新建虛擬機)」,彈出虛擬機創建菜單。
C.根據向導一步一步地創建虛擬機,首先選擇安裝方式是「TYPICAL(典型)」還是
「CUSTOM(自定義)」安裝。 我這里選擇典型。
D.因為這里是用於安裝REDHAT,所以在Guest operating system(客戶操作系統)「
中選擇」LINUX「,點擊下一步。
E.在Virtual machine name(虛擬機名字)中輸入你想建立的虛擬機的名字
F.在Location(位置)中選擇虛擬機的安裝位置。因為會在虛擬機中安裝操作系統
和應用軟體,所以建議將虛擬機安裝在一個有較大空間的磁碟分區中
G.如果你的電腦連接在網路中,那麼選擇一個合適的網路環境。我這里選擇
Use bridged net-working(使用路由網路)
H.點擊finish,返回VMWARE主界面,LINUX虛擬機就建好了。
2. 安裝操作系統
A. 選中LINUX虛擬機,點擊VMWARE工具欄中的Power ON按鈕,啟動LINUX虛擬機
B.然後插入REDHAT7.3光碟,虛擬系統根據你選擇的安裝方式開始安裝。
3.從硬碟安裝REDHAT7.3
如果你認為從光碟機中安裝比較費時間,又不方便,那你可以將光碟文件轉換成ISO文件拷
貝在硬碟中,然後從硬碟安裝。
A.點擊Settings(設置)--Configuration Editor(編輯配置)進入設置界面對虛擬機進行
配置。
B.在Hardware(硬體)選項中,選擇DVD/CD--ROM[IDE 1:0]項,在左邊的選項中進行設置。
C.在Connection(連接)選項選中Use ISO image(使用ISO鏡像包),然後點擊Browse(預覽)
按鈕,找到放置ISO文件的目錄。
D.在打開對話框中選擇RedHat.ISO文件,然後點擊打開,將ISO文件打開(如果第一個ISO
文件安裝完後,計算機提示你插入第二張光碟,則在此選擇RedHat.ISO,如此類推)
E.在Virtual device mode(虛擬設備模式)選擇虛擬設備的介面方式,選擇IDEO:0項
然後點擊OK返回到虛擬機界面下,點擊Power ON就可以直接從硬碟安裝操作系統了
Ⅲ 求救!colinux安裝之後虛擬網卡的本地連接總是網路電纜沒有插好,怎麼解決
上網的時候提示網路電纜沒有插好,通常是由於網卡沒有插好或網卡驅動程序存在問題等情況導致,建議重新插入網卡或重新安裝驅動程序再試,同時留意網線和網卡介面是否正常,可換一根網線嘗試。
另外,如果是網卡工作模式存在問題,也會影響使用,可找到電腦「我的電腦」圖標,右鍵點擊,選擇「管理-設備管理器-點到網路適配器」,找到網卡,然後右鍵點擊網卡,在「屬性---高級--mediatype或者鏈接速度」,把網卡的工作模式設定外10m半雙工即可。
Ⅳ 虛擬網路介面怎麼設置 我在一台計算機上面安裝了linux操作系統 然後安裝了 虛擬機但是虛擬機不能上網
虛擬機選擇NAT或者BRIGE,然後在linux中定義一下網卡,NAT選擇dhcp獲取,BRIGE選擇固定IP-------以上要根據你實際上網方式選擇。
然後重啟linux下的network服務或重啟網卡,一般就能上網了
Ⅳ 如何將linux虛擬機和主機之間連接 詳細�0�3
將Linux虛擬機連接到主機的方法如下:
1、打開計算機中的虛擬機軟體,然後打開Linux系統。
Ⅵ linux系統中,有關網路服務介面,是什麼定義的
(1)網路介面的命名
這里並不存在一定的命名規范,但網路介面名字的定義一般都是要有意義的。例如:
eth0:
ethernet的簡寫,一般用於乙太網介面。
wifi0:wifi是無線區域網,因此wifi0一般指無線網路介面。
ath0:
Atheros的簡寫,一般指Atheros晶元所包含的無線網路介面。
lo:
local的簡寫,一般指本地環回介面。
(2)網路介面如何工作
網路介面是用來發送和接受數據包的基本設備。
系統中的所有網路介面組成一個鏈狀結構,應用層程序使用時按名稱調用。
每個網路介面在linux系統中對應於一個struct
net_device結構體,包含name,mac,mask,mtu…信息。
每個硬體網卡(一個MAC)對應一個網路介面,其工作完全由相應的驅動程序控制。
(3)虛擬網路介面
虛擬網路介面的應用范圍非常廣泛。最著名的當屬「lo」了,基本上每個linux系統都有這個介面。
虛擬網路介面並不真實地從外界接收和發送數據包,而是在系統內部接收和發送數據包,因此虛擬網路介面不需要驅動程序。
虛擬網路介面和真實存在的網路介面在使用上是一致的。
(4)網路介面的創建
硬體網卡的網路介面由驅動程序創建。而虛擬的網路介面由系統創建或通過應用層程序創建。
驅動中創建網路介面的函數是:register_netdev(struct
net_device
*)或者register_netdevice(struct
net_device
*)。
這兩個函數的區別是:register_netdev(…)會自動生成以」eth」作為打頭名稱的介面,而register_netdevice(…)需要提前指定介面名稱.事實上,register_netdev(…)也是通過調用register_netdevice(…)實現的。
2、LINUX中的lo(回環介面)
1)
什麼是LO介面?
在LINUX系統中,除了網路介面eth0,還可以有別的介面,比如lo(本地環路介面)。
2)
LO介面的作用是什麼?
假如包是由一個本地進程為另一個本地進程產生的,
它們將通過外出鏈的』lo』介面,然後返回進入鏈的』lo』介面.具體參考包過濾器的相關內容。
PART2
實驗:
本地一個進程發起連接,到一個本地的daemon監聽的內網IP地址(eth1:
10.1.1.1)的埠(8085),此時在eth1上是抓不到包的,在
lo
上抓到,說明使用的是本地回環介面lo,而網路層的IP地址則是內網IP地址.
原
Ⅶ VirtualBox Linux虛擬機 怎麼配置vlan
virtualBox虛擬機上安裝linux系統,如何在兩個虛擬介面上配置網路
環境:
1.VirtualBox虛擬機,Ubuntu 64位系統,虛擬機網卡設置為:
網1用於管理linux系統,與本地主機為橋接關系,網卡2用於與另一個虛擬pc機相連,網卡3用橋接模式與本地主機連接
2.其配置方法為:
進入目錄:cd etc/sysconfig/network-scripts
其下創建ifcfg-eth0,ifcfg-eth1,ifcfg-eth2
ifcfg-eth0文件內容為:
DEVICE="eth0"
HWADDR="08:00:27:FA:85:8B"
NM_CONTROLLED="yes"
ONBOOT="yes"
#BOOTPROTO="dhcp"
IPADDR=10.60.52.42 //我的本地主機ip為10.60.52.52,需在同一個區域網中;
PREFIX=16
GATEWAY=10.60.10.252//這是我的網關
DNS1=8.8.8.8
DEFROUTE=yes
ifcfg-eth1文件內容為:
DEVICE="eth1"
HWADDR="08:00:27:71:DC:D5"
NM_CONTROLLED="yes"
ONBOOT="yes"
ifcfg-eth2文件內容為:
DEVICE="eth2"
HWADDR="08:00:27:62:F3:88"
NM_CONTROLLED="yes"
ONBOOT="yes"
3.創建另一個虛擬pc機,系統為win7系統,創建成功後,為其分配ip地址
10.60.52.49,,默認網關為:10.60.10.252
win7pc的網卡設置信息為:上圖網卡2所示
4.搭建虛擬防火牆
配置兩個介面,將兩個介面放入vlan 1中,然後兩個介面就可以通信了
Ⅷ Linux中,創建一個網卡的子介面和配置一個虛擬網卡是一個意思嗎
對的
第一塊網卡的 子介面 可以是eht0:1 可以將第一塊網卡的配置文件復制一下 重命名 但是 文件里的設備名 不能一樣 要不然 會出錯的
Ⅸ linux虛擬機 xshell埠怎麼設置
1 打開虛擬機,設置linux虛擬機為 僅主機 模式。
2 編輯linux的網卡配置文件 /etc/sysconfig/network-scripts/ifcfg-eth0(redhat6和7版本配置文件不一樣,以6為例)
3 為虛擬機配置一個ip(ip不做固定要求)(注意重啟網卡服務:service network restart)
1 打開真實機的網路配置
2 找到vmnet1的連接,右鍵-屬性
3 雙擊 版本協議4 (tcp/ipv4)
4 設置靜態ip地址 ,地址要和虛擬機的ip地址為同一網段
1 打開xshell客戶端
2 右上角 文件-新建
3 主機欄里填入linux虛擬機的ip地址,其他按需求填寫
4 右上角 文件-打開-選擇我們創建的會話。點擊 連接 。
1 等待彈出對話框,用戶名中填入linux虛擬機中創建的用戶(可以選保存用戶名,方便下次使用) 點擊 確定
2 在密碼欄里輸入你選擇用戶的密碼(同樣可以選擇保存)
3 點擊確定,等待連接。
5
也可以通過命令連接:ssh (要連接的虛擬機ip地址)