【UEFI基础】EDK网络框架(通用函数和数据)

通用函数和数据

DPC

DPC全称Deferred Procedure Call。Deferred的意思是“延迟”,这个DPC的作用就是注册函数,然后在之后的某个时刻调用,所以确实是有“延迟”的意思。DPC在UEFI的实现中包括两个部分。一部分是库函数DxeDpcLib,对应代码NetworkPkg\Library\DxeDpcLib\DxeDpcLib.inf;另一部分是EFI_DPC_PROTOCOL,对应代码NetworkPkg\DpcDxe\DpcDxe.inf。

DxeDpcLib

库中只实现了两个函数:

/**Add a Deferred Procedure Call to the end of the DPC queue.@param[in]  DpcTpl        The EFI_TPL that the DPC should be invoked.@param[in]  DpcProcedure  Pointer to the DPC's function.@param[in]  DpcContext    Pointer to the DPC's context.  Passed to DpcProcedurewhen DpcProcedure is invoked.@retval EFI_SUCCESS            The DPC was queued.@retval EFI_INVALID_PARAMETER  DpcTpl is not a valid EFI_TPL.@retval EFI_INVALID_PARAMETER  DpcProcedure is NULL.@retval EFI_OUT_OF_RESOURCES   There are not enough resources available toadd the DPC to the queue.
**/
EFI_STATUS
EFIAPI
QueueDpc (IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL);/**Dispatch the queue of DPCs.  ALL DPCs that have been queued with a DpcTplvalue greater than or equal to the current TPL are invoked in the order thatthey were queued.  DPCs with higher DpcTpl values are invoked before DPCs withlower DpcTpl values.@retval EFI_SUCCESS    One or more DPCs were invoked.@retval EFI_NOT_FOUND  No DPCs were invoked.
**/
EFI_STATUS
EFIAPI
DispatchDpc (VOID);

库函数的实现只是简单调用了EFI_DPC_PROTOCOL的接口函数:

EFI_STATUS
EFIAPI
QueueDpc (IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL)
{//// Call the EFI_DPC_PROTOCOL to queue the DPC//return mDpc->QueueDpc (mDpc, DpcTpl, DpcProcedure, DpcContext);
}

我们在使用DPC的时候直接调用库函数更方便,不过在了解DPC的实现时还是要重点关注EFI_DPC_PROTOCOL

EFI_DPC_PROTOCOL

接口初始化

EFI_DPC_PROTOCOL在一个DXE模块中安装的,安装的过程也很简单,查看具体的模块入口代码(位于NetworkPkg\DpcDxe\Dpc.c,只包含重点代码,下同):

EFI_STATUS
EFIAPI
DpcDriverEntryPoint (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{//// Initialize the DPC queue for all possible TPL values//for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {InitializeListHead (&mDpcQueue[Index]);}//// Install the EFI_DPC_PROTOCOL instance onto a new handle//Status = gBS->InstallMultipleProtocolInterfaces (&mDpcHandle,&gEfiDpcProtocolGuid, &mDpc,NULL);
}

这里唯一需要关注的是mDpcQueue这个全局变量。它是一个链表数组:

//
// An array of DPC queues.  A DPC queue is allocated for every level EFI_TPL value.
// As DPCs are queued, they are added to the end of the linked list.
// As DPCs are dispatched, they are removed from the beginning of the linked list.
//
LIST_ENTRY  mDpcQueue[TPL_HIGH_LEVEL + 1];	// TPL_HIGH_LEVEL的值是31,所以数组大小是32个元素

每一个数组元素对应一种优先级。这里创建了所有可能的优先级,但实际上常用的也就下面几种:

//
// Task priority level
//
#define TPL_APPLICATION       4
#define TPL_CALLBACK          8
#define TPL_NOTIFY            16
#define TPL_HIGH_LEVEL        31

这个链表真正的有效元素是DpcEntry,其结构如下:

//
// Internal data structure for managing DPCs.  A DPC entry is either on the free
// list or on a DPC queue at a specific EFI_TPL.
//
typedef struct {LIST_ENTRY           ListEntry;EFI_DPC_PROCEDURE    DpcProcedure;VOID                 *DpcContext;
} DPC_ENTRY;

第一个参数ListEntry用来处理链表,可以不关注;后面的两个参数,一个是DPC函数指针,一个是DPC函数的入参。EFI_DPC_PROCEDURE定义如下:

/**Invoke a Deferred Procedure Call.@param  DpcContext           The pointer to the Deferred Procedure Call's context,which is implementation dependent.
**/
typedef
VOID
(EFIAPI *EFI_DPC_PROCEDURE)(IN VOID  *DpcContext);

这个函数就是我们的主角:Deferred Procedure Call,简称DPC

接口实现

EFI_DPC_PROTOCOL包含两个接口函数,下面具体介绍。

  • 首先介绍DpcQueueDpc()
EFI_STATUS
EFIAPI
DpcQueueDpc (IN EFI_DPC_PROTOCOL   *This,IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL)

该函数的操作流程如下:

  1. 判断优先级DpcTpl是否满足要求:
  //// Make sure DpcTpl is valid//if ((DpcTpl < TPL_APPLICATION) || (DpcTpl > TPL_HIGH_LEVEL)) {return EFI_INVALID_PARAMETER;}
  1. 判断mDpcEntryFreeList的状态。这个mDpcEntryFreeList是另外的一个链表:
//
// Free list of DPC entries.  As DPCs are queued, entries are removed from this
// free list.  As DPC entries are dispatched, DPC entries are added to the free list.
// If the free list is empty and a DPC is queued, the free list is grown by allocating
// an additional set of DPC entries.
//
LIST_ENTRY  mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE (mDpcEntryFreeList);

一开始它是空的,在首次使用时会创建64个空的DPC_ENTRY,其基本代码:

  //// Check to see if there are any entries in the DPC free list//if (IsListEmpty (&mDpcEntryFreeList)) {//// Add 64 DPC entries to the free list//for (Index = 0; Index < 64; Index++) {//// Allocate a new DPC entry//DpcEntry = AllocatePool (sizeof (DPC_ENTRY));//// Add the newly allocated DPC entry to the DPC free list//InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);}}

这里去掉了一系列的条件判断,只保留基本的操作,就是一个分配内存给DPC_ENTRY,然后放到mDpcEntryFreeList的过程,表示的是没有使用到的DPC。

  1. mDpcEntryFreeList中拿出一个DpcEntry,为它赋值DpcProcedureDpcContext,这么做之后DPC才真正生效了:
  //// Retrieve the first node from the free list of DPCs//DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));//// Remove the first node from the free list of DPCs//RemoveEntryList (&DpcEntry->ListEntry);//// Fill in the DPC entry with the DpcProcedure and DpcContext//DpcEntry->DpcProcedure = DpcProcedure;DpcEntry->DpcContext   = DpcContext;
  1. 将初始化好的DpcEntry插入到mDpcQueue中,mDpcQueueDepth全局变量的值加1,表示又多了一个DPC:
  //// Add the DPC entry to the end of the list for the specified DplTpl.//InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);//// Increment the measured DPC queue depth across all TPLs//mDpcQueueDepth++;

mDpcQueuemDpcEntryFreeList的关系大致如下:

在这里插入图片描述

这里预分配资源给mDpcEntryFreeList,以及mDpcQueuemDpcEntryFreeList之间的DPC转换使用,其目的主要是能够减少资源的反复分配导致的内存碎片化,也能够实现名字中的“延迟”一说,而且执行速度上也有保证。

  • 然后介绍DpcDispatchDpc()
EFI_STATUS
EFIAPI
DpcDispatchDpc (IN EFI_DPC_PROTOCOL  *This)

该函数的操作流程如下:

  1. 判断mDpcQueueDepth值是否大于0,如果大于0,表示有DPC可以被调用,才会有后续的操作。
  //// Check to see if there are 1 or more DPCs currently queued//if (mDpcQueueDepth > 0) {
  1. 遍历mDpcQueue,找到非空的链表节点,将它从mDpcQueue中删去,mDpcQueueDepth的值相应的减1。
    //// Loop from TPL_HIGH_LEVEL down to the current TPL value//for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {//// Check to see if the DPC queue is empty//while (!IsListEmpty (&mDpcQueue[Tpl])) {//// Retrieve the first DPC entry from the DPC queue specified by Tpl//DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));//// Remove the first DPC entry from the DPC queue specified by Tpl//RemoveEntryList (&DpcEntry->ListEntry);//// Decrement the measured DPC Queue Depth across all TPLs//mDpcQueueDepth--;

注意这里的优先级是从高到低来执行对应的DPC的,这也是符合UEFI规范的。

  1. 执行那个从mDpcQueue去除下来的DpcEntry对应的DpcProcedure
        //// Invoke the DPC passing in its context//(DpcEntry->DpcProcedure)(DpcEntry->DpcContext);
  1. 将从mDpcQueue去除下来的DpcEntry在放回到mDpcEntryFreeList中,之后就可以重复利用:
        //// Add the invoked DPC entry to the DPC free list//InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);

以上就是DpcDispatchDpc()的流程。注意一次DpcDispatchDpc会将mDpcQueue中的所有DPC都执行一遍。

DPC代码示例

DpcLib的使用示例大致如下:

  1. 创建事件(位于NetworkPkg\ArpDxe\ArpDriver.c):
  //// Create the event used in the RxToken.//Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,ArpOnFrameRcvd,ArpService,&ArpService->RxToken.Event);if (EFI_ERROR (Status)) {goto ERROR_EXIT;}
  1. 事件的实现(位于NetworkPkg\ArpDxe\ArpImpl.c):
VOID
EFIAPI
ArpOnFrameRcvd (IN EFI_EVENT  Event,IN VOID       *Context)
{//// Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK//QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context);
}

这里就会调用QueueDpc()。这实际上很像是Linux中断处理中的上半部和下半部,上述的代码实际上是上半部,下半部就是执行DispatchDpc(),下面是一个示例(位于NetworkPkg\ArpDxe\ArpImpl.c):

UINTN
ArpAddressResolved (IN ARP_CACHE_ENTRY    *CacheEntry,IN ARP_INSTANCE_DATA  *Instance OPTIONAL,IN EFI_EVENT          UserEvent OPTIONAL)
{//// Dispatch the DPCs queued by the NotifyFunction of the Context->UserRequestEvent.//DispatchDpc ();return Count;
}

执行DispatchDpc()的位置需要根据实际的代码逻辑来确定,这个将在后续实际使用模块中进一步说明。

下面是自己写的一个使用DpcLib的简单示例,代码可以在BeniPkg\DynamicCommand\TestDynamicCommand\TestDpc.c中找到:

/**DPC function.@param  NA@retval  NA**/
VOID
EFIAPI
DpcCallback (IN  VOID                          *Context)
{Print (L"DPC callback function\r\n");
}/**Test DpcLib code.@param  NA@return  NA**/
VOID
TestDpc (VOID)
{QueueDpc (TPL_CALLBACK, DpcCallback, NULL);
}

这里执行TestDpc()之后就会注册一个DPC函数,该函数也只是简单的打印信息而已,执行结果如下:

在这里插入图片描述

有几点需要注意:

  1. 首先代码中并没有直接调用DispatchDpc(),但是我们注册的DPC函数DpcCallback()还是被执行了,这是因为当前BIOS已经包含了UEFI网络协议栈,所以会在某个网络驱动的执行代码中调用DispatchDpc(),而前面也已经说过DispatchDpc()会调用所有的DPC函数,所以这里注册的DPC也会被调用。
  2. 其次是多次执行的结果稍有不同,主要是输出信息顺序的变化,这跟上面说明的原因也类似,网络驱动事件调用的时间点有随机的成分,这也导致了DpcCallback()执行时间的变化。

Token

BIOS网络协议中的数据基本都是通过Token的形式来处理的,它有很多种不同的描述方式,整体描述如下:

在这里插入图片描述

需要注意的是Token实际上的使用者并不是提供者本身,而是提供者的上层协议,比如MNP的Token使用者是ARP、IP4等使用MNP的上层协议。

Token中比较通用的一种是:

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;// 其它结构体union {XXX_DATA     *RxData;XXX_DATA    *TxData;// 其它结构体} Packet;
} YYY;

注意结构体中写的XXXYYY并不是真实的名称,在不同协议层下有不同的表示。当然也有其它的形式(图中深色部分),后面也会说明。下面将例举出所有的Token的具体格式。

EFI_MANAGED_NETWORK_COMPLETION_TOKEN

typedef struct {////// This Event will be signaled after the Status field is updated/// by the MNP. The type of Event must be/// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT     Event;////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS    Status;union {////// When this token is used for receiving, RxData is a pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.///EFI_MANAGED_NETWORK_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to the EFI_MANAGED_NETWORK_TRANSMIT_DATA.///EFI_MANAGED_NETWORK_TRANSMIT_DATA    *TxData;} Packet;
} EFI_MANAGED_NETWORK_COMPLETION_TOKEN;

IP_IO_IP_COMPLETION_TOKEN

typedef struct {////// This Event will be signaled after the Status field is updated/// by the EFI IPv4 Protocol driver. The type of Event must be/// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT     Event;////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS    Status;union {////// When this token is used for receiving, RxData is a pointer to the EFI_IP4_RECEIVE_DATA.///EFI_IP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to the EFI_IP4_TRANSMIT_DATA.///EFI_IP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_IP4_COMPLETION_TOKEN;typedef union {EFI_IP4_COMPLETION_TOKEN    Ip4Token;EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;

TCP_IO_CONNECTION_TOKEN

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;
} EFI_TCP4_COMPLETION_TOKEN;typedef struct {////// The Status in the CompletionToken will be set to one of/// the following values if the active open succeeds or an unexpected/// error happens:/// EFI_SUCCESS:              The active open succeeds and the instance's///                           state is Tcp4StateEstablished./// EFI_CONNECTION_RESET:     The connect fails because the connection is reset///                           either by instance itself or the communication peer./// EFI_CONNECTION_REFUSED:   The connect fails because this connection is initiated with///                           an active open and the connection is refused./// EFI_ABORTED:              The active open is aborted./// EFI_TIMEOUT:              The connection establishment timer expires and///                           no more specific information is available./// EFI_NETWORK_UNREACHABLE:  The active open fails because///                           an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The active open fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The active open fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The connection establishment///                           timer times out and an ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The connection establishment timer timeout and some other ICMP///                           error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurred./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
} EFI_TCP4_CONNECTION_TOKEN;typedef union {EFI_TCP4_CONNECTION_TOKEN    Tcp4Token;EFI_TCP6_CONNECTION_TOKEN    Tcp6Token;
} TCP_IO_CONNECTION_TOKEN;

注意这个结构体不涉及到具体的数据(即Packet成员)。

TCP_IO_IO_TOKEN

typedef struct {////// When transmission finishes or meets any unexpected error it will/// be set to one of the following values:/// EFI_SUCCESS:              The receiving or transmission operation///                           completes successfully./// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer///                           has closed the connection and there is no more data in the///                           receive buffer of the instance./// EFI_CONNECTION_RESET:     The receiving or transmission operation fails///                           because this connection is reset either by instance///                           itself or the communication peer./// EFI_ABORTED:              The receiving or transmission is aborted./// EFI_TIMEOUT:              The transmission timer expires and no more///                           specific information is available./// EFI_NETWORK_UNREACHABLE:  The transmission fails///                           because an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The transmission fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The transmission fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The transmission fails and an///                           ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The transmission fails and some other///                           ICMP error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurs./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;union {////// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.///EFI_TCP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.///EFI_TCP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_TCP4_IO_TOKEN;typedef union {EFI_TCP4_IO_TOKEN    Tcp4Token;EFI_TCP6_IO_TOKEN    Tcp6Token;
} TCP_IO_IO_TOKEN;

TCP_IO_LISTEN_TOKEN

typedef struct {EFI_TCP4_COMPLETION_TOKEN    CompletionToken;EFI_HANDLE                   NewChildHandle;
} EFI_TCP4_LISTEN_TOKEN;typedef union {EFI_TCP4_LISTEN_TOKEN    Tcp4Token;EFI_TCP6_LISTEN_TOKEN    Tcp6Token;
} TCP_IO_LISTEN_TOKEN;

这里的数据不再是Packet,而是一个EFI_HANDLE,它表示的是一个TCP Socket对应的虚拟Handle:

///
/// The socket structure representing a network service access point.
///
struct _TCP_SOCKET {EFI_HANDLE                  SockHandle;    ///< The virtual handle of the socket

TCP_IO_CLOSE_TOKEN

typedef struct {////// When transmission finishes or meets any unexpected error it will/// be set to one of the following values:/// EFI_SUCCESS:              The receiving or transmission operation///                           completes successfully./// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer///                           has closed the connection and there is no more data in the///                           receive buffer of the instance./// EFI_CONNECTION_RESET:     The receiving or transmission operation fails///                           because this connection is reset either by instance///                           itself or the communication peer./// EFI_ABORTED:              The receiving or transmission is aborted./// EFI_TIMEOUT:              The transmission timer expires and no more///                           specific information is available./// EFI_NETWORK_UNREACHABLE:  The transmission fails///                           because an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The transmission fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The transmission fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The transmission fails and an///                           ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The transmission fails and some other///                           ICMP error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurs./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;union {////// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.///EFI_TCP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.///EFI_TCP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_TCP4_IO_TOKEN;typedef union {EFI_TCP4_CLOSE_TOKEN    Tcp4Token;EFI_TCP6_CLOSE_TOKEN    Tcp6Token;
} TCP_IO_CLOSE_TOKEN;

UDP_COMPLETION_TOKEN

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;union {EFI_UDP4_RECEIVE_DATA     *RxData;EFI_UDP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_UDP4_COMPLETION_TOKEN;typedef union {EFI_UDP4_COMPLETION_TOKEN    Udp4;EFI_UDP6_COMPLETION_TOKEN    Udp6;
} UDP_COMPLETION_TOKEN;

EFI_DNS4_COMPLETION_TOKEN

///
/// EFI_DNS4_COMPLETION_TOKEN
///
typedef struct {////// This Event will be signaled after the Status field is updated by the EFI DNS/// protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL.///EFI_EVENT    Event;////// Will be set to one of the following values:///   EFI_SUCCESS:      The host name to address translation completed successfully.///   EFI_NOT_FOUND:    No matching Resource Record (RR) is found.///   EFI_TIMEOUT:      No DNS server reachable, or RetryCount was exhausted without///                     response from all specified DNS servers.///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.///   EFI_NO_MEDIA:     There was a media error.///EFI_STATUS    Status;////// Retry number if no response received after RetryInterval. If zero, use the/// parameter configured through Dns.Configure() interface.///UINT32        RetryCount;////// Minimum interval of retry is 2 second. If the retry interval is less than 2/// seconds, then use the 2 seconds. If zero, use the parameter configured through/// Dns.Configure() interface.UINT32        RetryInterval;////// DNSv4 completion token data///union {////// When the Token is used for host name to address translation, H2AData is a pointer/// to the DNS_HOST_TO_ADDR_DATA.///DNS_HOST_TO_ADDR_DATA      *H2AData;////// When the Token is used for host address to host name translation, A2HData is a/// pointer to the DNS_ADDR_TO_HOST_DATA.///DNS_ADDR_TO_HOST_DATA      *A2HData;////// When the Token is used for a general lookup function, GLookupDATA is a pointer to/// the DNS_GENERAL_LOOKUP_DATA.///DNS_GENERAL_LOOKUP_DATA    *GLookupData;} RspData;
} EFI_DNS4_COMPLETION_TOKEN;

这个Token中的数据相比其它的Token多了很多内容,不过形式并没有太大的变化,后面还有一些类似的Token。

EFI_MTFTP4_TOKEN

struct _EFI_MTFTP4_TOKEN {////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS                     Status;////// The event that will be signaled when the operation completes. If/// set to NULL, the corresponding function will wait until the read or/// write operation finishes. The type of Event must be/// EVT_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT                      Event;////// If not NULL, the data that will be used to override the existing configure data.///EFI_MTFTP4_OVERRIDE_DATA       *OverrideData;////// The pointer to the null-terminated ASCII file name string.///UINT8                          *Filename;////// The pointer to the null-terminated ASCII mode string. If NULL, "octet" is used.///UINT8                          *ModeStr;////// Number of option/value string pairs.///UINT32                         OptionCount;////// The pointer to an array of option/value string pairs. Ignored if OptionCount is zero.///EFI_MTFTP4_OPTION              *OptionList;////// The size of the data buffer.///UINT64                         BufferSize;////// The pointer to the data buffer. Data that is downloaded from the/// MTFTPv4 server is stored here. Data that is uploaded to the/// MTFTPv4 server is read from here. Ignored if BufferSize is zero.///VOID                           *Buffer;////// The pointer to the context that will be used by CheckPacket,/// TimeoutCallback and PacketNeeded.///VOID                           *Context;////// The pointer to the callback function to check the contents of the received packet.///EFI_MTFTP4_CHECK_PACKET        CheckPacket;////// The pointer to the function to be called when a timeout occurs.///EFI_MTFTP4_TIMEOUT_CALLBACK    TimeoutCallback;////// The pointer to the function to provide the needed packet contents.///EFI_MTFTP4_PACKET_NEEDED       PacketNeeded;
};

SOCK_IO_TOKEN

typedef struct _SOCK_COMPLETION_TOKEN {EFI_EVENT     Event;          ///< The event to be issuedEFI_STATUS    Status;         ///< The status to be issued
} SOCK_COMPLETION_TOKEN;typedef struct _SOCK_IO_TOKEN {SOCK_COMPLETION_TOKEN    Token;SOCK_IO_DATA             Packet;
} SOCK_IO_TOKEN;

EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN

typedef struct {////// The completion status of transmitting and receiving.///EFI_STATUS                Status;////// If not NULL, the event that will be signaled when the collection process/// completes. If NULL, this function will busy-wait until the collection process competes.///EFI_EVENT                 CompletionEvent;////// The pointer to the server IP address. This address may be a unicast, multicast, or broadcast address.///EFI_IPv4_ADDRESS          RemoteAddress;////// The server listening port number. If zero, the default server listening port number (67) will be used.///UINT16                    RemotePort;////// The pointer to the gateway address to override the existing setting.///EFI_IPv4_ADDRESS          GatewayAddress;////// The number of entries in ListenPoints. If zero, the default station address and port number 68 are used.///UINT32                    ListenPointCount;////// An array of station address and port number pairs that are used as receiving filters./// The first entry is also used as the source address and source port of the outgoing packet.///EFI_DHCP4_LISTEN_POINT    *ListenPoints;////// The number of seconds to collect responses. Zero is invalid.///UINT32                    TimeoutValue;////// The pointer to the packet to be transmitted.///EFI_DHCP4_PACKET          *Packet;////// Number of received packets.///UINT32                    ResponseCount;////// The pointer to the allocated list of received packets.///EFI_DHCP4_PACKET          *ResponseList;
} EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN;

EFI_HTTP_TOKEN

///
/// EFI_HTTP_TOKEN
///
typedef struct {////// This Event will be signaled after the Status field is updated by the EFI HTTP/// Protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL. The Task Priority/// Level (TPL) of Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT    Event;////// Status will be set to one of the following value if the HTTP request is/// successfully sent or if an unexpected error occurs:///   EFI_SUCCESS:      The HTTP request was successfully sent to the remote host.///   EFI_HTTP_ERROR:   The response message was successfully received but contains a///                     HTTP error. The response status code is returned in token.///   EFI_ABORTED:      The HTTP request was cancelled by the caller and removed from///                     the transmit queue.///   EFI_TIMEOUT:      The HTTP request timed out before reaching the remote host.///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.///EFI_STATUS          Status;////// Pointer to storage containing HTTP message data.///EFI_HTTP_MESSAGE    *Message;
} EFI_HTTP_TOKEN;

UDP_RX_TOKEN

这里开始的Token的结构相比前面的Token已经有很大的差别:

typedef struct {UINT32                  Signature;UDP_IO                  *UdpIo;UDP_IO_CALLBACK         CallBack;VOID                    *Context;UINT32                  HeadLen;UDP_COMPLETION_TOKEN    Token;
} UDP_RX_TOKEN;

UDP_TX_TOKEN

typedef struct {UINT32                  Signature;LIST_ENTRY              Link;UDP_IO                  *UdpIo;UDP_IO_CALLBACK         CallBack;NET_BUF                 *Packet;VOID                    *Context;EFI_IPv4_ADDRESS        Gateway;UDP_SESSION_DATA        Session;UDP_COMPLETION_TOKEN    Token;UDP_TRANSMIT_DATA       Data;
} UDP_TX_TOKEN;

IP4_LINK_TX_TOKEN

typedef struct {UINT32                                  Signature;LIST_ENTRY                              Link;IP4_INTERFACE                           *Interface;IP4_SERVICE                             *IpSb;IP4_PROTOCOL                            *IpInstance;IP4_FRAME_CALLBACK                      CallBack;NET_BUF                                 *Packet;VOID                                    *Context;EFI_MAC_ADDRESS                         DstMac;EFI_MAC_ADDRESS                         SrcMac;EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;EFI_MANAGED_NETWORK_TRANSMIT_DATA       MnpTxData;
} IP4_LINK_TX_TOKEN;

IP4_LINK_RX_TOKEN

typedef struct {UINT32                                  Signature;IP4_INTERFACE                           *Interface;IP4_PROTOCOL                            *IpInstance;IP4_FRAME_CALLBACK                      CallBack;VOID                                    *Context;EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;
} IP4_LINK_RX_TOKEN;

SOCK_TOKEN

typedef struct _SOCK_TOKEN {LIST_ENTRY               TokenList;     ///< The entry to add in the token listSOCK_COMPLETION_TOKEN    *Token;        ///< The application's tokenUINT32                   RemainDataLen; ///< Unprocessed data lengthSOCKET                   *Sock;         ///< The pointer to the socket this token///< belongs to
} SOCK_TOKEN;

IpIoLib

UDP4和TCP4中,有不少操作实际上是放在IpIoLib库中完成的,它实际上是上层网络驱动对IP层调用的包装接口。

IpIoLib首先对IPv4和IPv6中会使用到的数据进行了包装,这样就可以用统一的接口来处理IP层,比如:

typedef union {EFI_IP4_COMPLETION_TOKEN    Ip4Token;EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;typedef union {EFI_IP4_TRANSMIT_DATA    Ip4TxData;EFI_IP6_TRANSMIT_DATA    Ip6TxData;
} IP_IO_IP_TX_DATA;typedef union {EFI_IP4_RECEIVE_DATA    Ip4RxData;EFI_IP6_RECEIVE_DATA    Ip6RxData;
} IP_IO_IP_RX_DATA;typedef union {EFI_IP4_OVERRIDE_DATA    Ip4OverrideData;EFI_IP6_OVERRIDE_DATA    Ip6OverrideData;
} IP_IO_OVERRIDE;typedef union {EFI_IP4_CONFIG_DATA    Ip4CfgData;EFI_IP6_CONFIG_DATA    Ip6CfgData;
} IP_IO_IP_CONFIG_DATA;typedef union {EFI_IP4_HEADER    *Ip4Hdr;EFI_IP6_HEADER    *Ip6Hdr;
} IP_IO_IP_HEADER;typedef union {EFI_IP4_PROTOCOL    *Ip4;EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;

这里包含了收发和处理数据的结构体,配置IP的结构体等内容,另外还有一个重要的结构体IP_IO,会在后续进一步介绍。

IP_IO

IP_IO是对IP4和IP6接口的包装,这样上层的TCP和UDP就可以直接使用它而不需要分别对待IP4和IP6。该结构体位于NetworkPkg\Include\Library\IpIoLib.h:

///
/// This data structure wraps Ip4/Ip6 instances. The IpIo Library uses it for all
/// Ip4/Ip6 operations.
///
typedef struct _IP_IO {////// The node used to link this IpIo to the active IpIo list.///LIST_ENTRY                   Entry;////// The list used to maintain the IP instance for different sending purpose.///LIST_ENTRY                   IpList;EFI_HANDLE                   Controller;EFI_HANDLE                   Image;EFI_HANDLE                   ChildHandle;//// The IP instance consumed by this IP_IO//IP_IO_IP_PROTOCOL            Ip;BOOLEAN                      IsConfigured;////// Some ip configuration data can be changed.///UINT8                        Protocol;////// Token and event used to get data from IP.///IP_IO_IP_COMPLETION_TOKEN    RcvToken;////// List entry used to link the token passed to IP_IO.///LIST_ENTRY                   PendingSndList;//// User interface used to get notify from IP_IO//VOID                         *RcvdContext;     ///< See IP_IO_OPEN_DATA::RcvdContext.VOID                         *SndContext;      ///< See IP_IO_OPEN_DATA::SndContext.PKT_RCVD_NOTIFY              PktRcvdNotify;    ///< See IP_IO_OPEN_DATA::PktRcvdNotify.PKT_SENT_NOTIFY              PktSentNotify;    ///< See IP_IO_OPEN_DATA::PktSentNotify.UINT8                        IpVersion;IP4_ADDR                     StationIp;IP4_ADDR                     SubnetMask;
} IP_IO;

该结构体通过IpIoCreate()函数创建,它们在UDP和TCP模块中使用,比如TCP中:

EFI_STATUS
TcpCreateService (IN EFI_HANDLE  Controller,IN EFI_HANDLE  Image,IN UINT8       IpVersion)
{TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);

还有UDP中:

EFI_STATUS
Udp4CreateService (IN OUT UDP4_SERVICE_DATA  *Udp4Service,IN     EFI_HANDLE         ImageHandle,IN     EFI_HANDLE         ControllerHandle)
{Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_4);

下面介绍IP_IO中比较重要的成员:

  • IpList:IP实例链表,它在IpIoCreate()中初始化,并在IpIoAddIp()中添加链表成员:
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo)
{InsertTailList (&IpIo->IpList, &IpInfo->Entry);

这里涉及到另外一个结构体IP_IO_IP_INFO

typedef struct _IP_IO_IP_INFO {EFI_IP_ADDRESS               Addr;IP_IO_IP_MASK                PreMask;LIST_ENTRY                   Entry;EFI_HANDLE                   ChildHandle;IP_IO_IP_PROTOCOL            Ip;IP_IO_IP_COMPLETION_TOKEN    DummyRcvToken;INTN                         RefCnt;UINT8                        IpVersion;
} IP_IO_IP_INFO;

这个结构体也是在IpIoAddIp()中创建的,这个函数虽然说是增加IP_IO_IP_INFO,但是其中只是对IP_IO_IP_INFO做了初始化而已,其中最重要的代码是:

IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo)
{Status = IpIoCreateIpChildOpenProtocol (IpIo->Controller,IpIo->Image,&IpInfo->ChildHandle,IpInfo->IpVersion,(VOID **)&IpInfo->Ip);// 中间略InsertTailList (&IpIo->IpList, &IpInfo->Entry);

IpIoCreateIpChildOpenProtocol()打开了gEfiIp4ProtocolGuid对应的EFI_IP4_PROTOCOL(当然实际代码中是IP_IO_IP_PROTOCOL)。所以IP_IO_IP_INFO这个结构体的重点是对IP通信接口EFI_IP4_PROTOCOL的包装。最终IpIoAddIp()函数会被上层的TCP和UDP使用,来创建它们的IP通信接口:

// TCP模块中:
EFI_STATUS
TcpAttachPcb (IN SOCKET  *Sk)
{Tcb->IpInfo = IpIoAddIp (IpIo);// UDP模块中:
EFI_STATUS
EFIAPI
Udp4ServiceBindingCreateChild (IN EFI_SERVICE_BINDING_PROTOCOL  *This,IN EFI_HANDLE                    *ChildHandle)
{Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo);

通过上述的方式,UPD和TCP就与其下层的IP联系到了一起:

在这里插入图片描述

  • Ip:对IP4和IP6的Protocol的包装,我们主要关注IP4就可以了:
typedef union {EFI_IP4_PROTOCOL    *Ip4;EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;
  • RcvToken:如注释描述,是从IP4获取数据的Token。
  • PendingSndList:需要传递给IP4的Token的列表。
  • RcvdContextSndContextPktRcvdNotifyPktSentNotify:对应数据处理的函数,它们来自另外的一个结构体IP_IO_OPEN_DATA
///
/// The struct is for the user to pass IP configuration and callbacks to IP_IO.
/// It is used by IpIoOpen().
///
typedef struct _IP_IO_OPEN_DATA {IP_IO_IP_CONFIG_DATA    IpConfigData;  ///< Configuration of the IP instance.VOID                    *RcvdContext;  ///< Context data used by receive callback.VOID                    *SndContext;   ///< Context data used by send callback.PKT_RCVD_NOTIFY         PktRcvdNotify; ///< Receive callback.PKT_SENT_NOTIFY         PktSentNotify; ///< Send callback.
} IP_IO_OPEN_DATA;

IP_IO_OPEN_DATA其实是一个临时参数,会在打开IP_IO的时候使用,比如UDP模块中:

EFI_STATUS
Udp4CreateService (IN OUT UDP4_SERVICE_DATA  *Udp4Service,IN     EFI_HANDLE         ImageHandle,IN     EFI_HANDLE         ControllerHandle)
{OpenData.RcvdContext           = (VOID *)Udp4Service;OpenData.SndContext            = NULL;OpenData.PktRcvdNotify         = Udp4DgramRcvd;OpenData.PktSentNotify         = Udp4DgramSent;Status = IpIoOpen (Udp4Service->IpIo, &OpenData);

还有TCP中:

EFI_STATUS
TcpCreateService (IN EFI_HANDLE  Controller,IN EFI_HANDLE  Image,IN UINT8       IpVersion)
{ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));if (IpVersion == IP_VERSION_4) {CopyMem (&OpenData.IpConfigData.Ip4CfgData,&mIp4IoDefaultIpConfigData,sizeof (EFI_IP4_CONFIG_DATA));OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;} else {CopyMem (&OpenData.IpConfigData.Ip6CfgData,&mIp6IoDefaultIpConfigData,sizeof (EFI_IP6_CONFIG_DATA));OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;}OpenData.PktRcvdNotify = TcpRxCallback;Status                 = IpIoOpen (TcpServiceData->IpIo, &OpenData);

都是在创建TCP或者UDP服务时作为参数使用。这个数据中最重要的是两个回调函数,分别对应IP数据收发的处理:

/**The prototype is called back when an IP packet is received.@param[in] Status        The result of the receive request.@param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.@param[in] NetSession    The IP session for the received packet.@param[in] Pkt           The packet received.@param[in] Context       The data provided by the user for the received packet whenthe callback is registered in IP_IO_OPEN_DATA::RcvdContext.**/
typedef
VOID
(EFIAPI *PKT_RCVD_NOTIFY)(IN EFI_STATUS           Status,IN UINT8                IcmpErr,IN EFI_NET_SESSION_DATA *NetSession,IN NET_BUF              *Pkt,IN VOID                 *Context);/**The prototype is called back when an IP packet is sent.@param[in] Status        Result of the IP packet being sent.@param[in] Context       The data provided by user for the received packet whenthe callback is registered in IP_IO_OPEN_DATA::SndContext.@param[in] Sender        A Union type to specify a pointer of EFI_IP4_PROTOCOLor EFI_IP6_PROTOCOL.@param[in] NotifyData    The Context data specified when calling IpIoSend()**/
typedef
VOID
(EFIAPI *PKT_SENT_NOTIFY)(IN EFI_STATUS        Status,IN VOID              *Context,IN IP_IO_IP_PROTOCOL Sender,IN VOID              *NotifyData);
  • IpVersion:对应IP_VERSION_4或者IP_VERSION_6,值分别是4和6,表示IPv4和IPv6。
  • StationIpSubnetMask:IP使用的地址和掩码。

函数

比较重要的函数:

/**Create a new IP_IO instance.If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().This function uses IP4/IP6 service binding protocol in Controller to createan IP4/IP6 child (aka IP4/IP6 instance).@param[in]  Image             The image handle of the driver or application thatconsumes IP_IO.@param[in]  Controller        The controller handle that has IP4 or IP6 servicebinding protocol installed.@param[in]  IpVersion         The version of the IP protocol to use, eitherIPv4 or IPv6.@return The pointer to a newly created IP_IO instance, or NULL if failed.**/
IP_IO *
EFIAPI
IpIoCreate (IN EFI_HANDLE  Image,IN EFI_HANDLE  Controller,IN UINT8       IpVersion);/**Open an IP_IO instance for use.If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().This function is called after IpIoCreate(). It is used for configuring the IPinstance and register the callbacks and their context data for sending andreceiving IP packets.@param[in, out]  IpIo               The pointer to an IP_IO instance that needsto open.@param[in]       OpenData           The configuration data and callbacks forthe IP_IO instance.@retval          EFI_SUCCESS            The IP_IO instance opened with OpenDatasuccessfully.@retval          EFI_ACCESS_DENIED      The IP_IO instance is configured, avoid toreopen it.@retval          EFI_UNSUPPORTED        IPv4 RawData mode is no supported.@retval          EFI_INVALID_PARAMETER  Invalid input parameter.@retval          Others                 Error condition occurred.**/
EFI_STATUS
EFIAPI
IpIoOpen (IN OUT IP_IO            *IpIo,IN     IP_IO_OPEN_DATA  *OpenData);/**Send out an IP packet.This function is called after IpIoOpen(). The data to be sent is wrapped inPkt. The IP instance wrapped in IpIo is used for sending by default but can beoverridden by Sender. Other sending configs, like source address and gatewayaddress etc., are specified in OverrideData.@param[in, out]  IpIo                  Pointer to an IP_IO instance used for sending IPpacket.@param[in, out]  Pkt                   Pointer to the IP packet to be sent.@param[in]       Sender                The IP protocol instance used for sending.@param[in]       Context               Optional context data.@param[in]       NotifyData            Optional notify data.@param[in]       Dest                  The destination IP address to send this packet to.This parameter is optional when using IPv6.@param[in]       OverrideData          The data to override some configuration of the IPinstance used for sending.@retval          EFI_SUCCESS           The operation is completed successfully.@retval          EFI_INVALID_PARAMETER The input parameter is not correct.@retval          EFI_NOT_STARTED       The IpIo is not configured.@retval          EFI_OUT_OF_RESOURCES  Failed due to resource limit.@retval          Others                Error condition occurred.**/
EFI_STATUS
EFIAPI
IpIoSend (IN OUT IP_IO           *IpIo,IN OUT NET_BUF         *Pkt,IN     IP_IO_IP_INFO   *Sender        OPTIONAL,IN     VOID            *Context       OPTIONAL,IN     VOID            *NotifyData    OPTIONAL,IN     EFI_IP_ADDRESS  *Dest          OPTIONAL,IN     IP_IO_OVERRIDE  *OverrideData  OPTIONAL);/**Add a new IP instance for sending data.If IpIo is NULL, then ASSERT().If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().The function is used to add the IP_IO to the IP_IO sending list. The callercan later use IpIoFindSender() to get the IP_IO and call IpIoSend() to senddata.@param[in, out]  IpIo               The pointer to an IP_IO instance to add a new IPinstance for sending purposes.@return The pointer to the created IP_IO_IP_INFO structure; NULL if failed.**/
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo);/**Configure the IP instance of this IpInfo and start the receiving if IpConfigDatais not NULL.If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().@param[in, out]  IpInfo          The pointer to the IP_IO_IP_INFO instance.@param[in, out]  IpConfigData    The IP4 or IP6 configure data used to configurethe IP instance. If NULL, the IP instance is reset.If UseDefaultAddress is set to TRUE, and the configureoperation succeeds, the default address informationis written back in this IpConfigData.@retval          EFI_SUCCESS     The IP instance of this IpInfo was configured successfully,or there is no need to reconfigure it.@retval          Others          The configuration failed.**/
EFI_STATUS
EFIAPI
IpIoConfigIp (IN OUT IP_IO_IP_INFO  *IpInfo,IN OUT VOID           *IpConfigData OPTIONAL);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/594757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字IC设计——数字电路基本元器件

现代数字集成电路基本由CMOS晶体管构成&#xff0c;而CMOS门电路由PMOS场效应管和NMOS场效应管以对称互补的形式组成&#xff0c;所谓“互补”&#xff0c;即利用互补型MOSFET&#xff0c;即pMOS和nMOS&#xff0c;二者成对出现构成互补电路。 这种电路具有高的电路可靠性和抗干…

Mysql show Profiles详解

1.简介 show profile 和 show profiles 命令用于展示SQL语句的资源使用情况&#xff0c;包括CPU的使用&#xff0c;CPU上下文切换&#xff0c;IO等待&#xff0c;内存使用等&#xff0c;这个命令对于分析某个SQL的性能瓶颈非常有帮助&#xff0c;借助于show profile的输出信息&…

力扣hot100 二叉树的直径

&#x1f468;‍&#x1f3eb; 题目地址 一个节点的最大直径 它左树的深度 它右树的深度 &#x1f60b; AC code /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* Tr…

[Android]RadioButton控件

RadioButton控件 RadioButton控件是单选按钮控件&#xff0c;它继承自Button控件&#xff0c;可以直接使用Button控件支持的各种属性和方法。 与普通按钮不同的是&#xff0c;RadioButton控件多了一个可以选中的功能&#xff0c;能额外指定一个android&#xff1a;checked属性…

手机视频监控客户端APP如何实现跨安卓、苹果和windows平台,并满足不同人的使用习惯

目 录 一、手机视频监控客户端的应用和发展 二、手机视频监控客户端存在的问题 三、HTML5视频监控客户端在手机上实现的方案 &#xff08;一&#xff09;HTML5及其优点 &#xff08;二&#xff09;HTML5在手机上实现视频应用功能的优势 四、手机HTML5…

【数据结构】循环队列(数组实现)

目录 一、循环队列定义 怎么使一个数组在逻辑上呈“环状”呢&#xff1f; 二、循环队列与顺序队列的差异 1、存储方式: 2、操作方式: 3、空间利用率&#xff1a; 4、循环队列判断队空的方式&#xff1a; 5、循环队列判断队满的方式 完整测试代码及注释&#xff1a; 总…

axure RP9.0安装字体图标库fontawesome

字体图库地址: Font AwesomeThe internets icon library toolkit. Used by millions of designers, devs, & content creators. Open-source. Always free. Always awesome.https://fontawesome.com/v6/download进入后下载想要的版本如我是6.3 下载后得到压缩包,解压之后…

机器学习笔记 - 从2D数据合成3D数据

一、3D 数据简介 人们一致认为,从单一角度合成 3D 数据是人类视觉的一项基本功能,这对计算机视觉算法来说极具挑战性。但随着 LiDAR、RGB-D 相机(RealSense、Kinect)和 3D 扫描仪等 3D 传感器的可用性和价格的提高,3D 采集技术的最新进展取得了巨大飞跃。 与广泛使用的 2D…

Mybatis-Plus乐观锁配置使用流程【OptimisticLockerInnerInterceptor】

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家:人工智能学习网站 1.乐观锁实现 1.配置插件 1.XML方式 <bean class"com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerI…

C# .Net 开发设计多用户网上商城源码_OctShop

随着C#在TIOBE编程语言排行不断上升&#xff0c;这也标志着越来越多的程序员开始使用C#来开发项目了。在TIOBE2023年10月公布的排行中&#xff0c;C#和Java之间的差距越来越小了&#xff0c;仅为1.2%&#xff0c;随着C# .NetCore的免费开源&#xff0c;这一上升的趋势越来越明显…

MySQL视图特性

目录 视图概念基本使用创建视图修改视图会影响基表修改基表会影响视图删除视图 视图规则和限制 视图概念 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表&#xff0c;基表的数据变…

Python 编写代码的工具-交互式环境

交互式环境意思就是我们输入一行代码&#xff0c;按回车&#xff0c;代码就会立马执行并产生结果和显示在窗口中。 要打开Python交互式环境&#xff0c;具体操作如下&#xff08;win系统&#xff09;&#xff1a; 键盘WINR&#xff0c;再输入指令“cmd”,就可以打开命令提示符…

win下持续观察nvidia-smi

简介&#xff1a;在Windows操作系统中&#xff0c;没有与Linux中watch命令直接对应的内置工具&#xff0c;但有1种方法快速简单的方法可以实现类似的效果&#xff0c;尤其是用于监控类似于nvidia-smi的命令输出。 历史攻略&#xff1a; Python&#xff1a;查看windows下GPU的…

数据库攻防学习之MySQL

MySQL 0x01mysql学习 MySQL 是瑞典的MySQL AB公司开发的一个可用于各种流行操作系统平台的关系数据库系统&#xff0c;它具有客户机/服务器体系结构的分布式数据库管理系统。可以免费使用使用&#xff0c;用的人数很多。 0x02环境搭建 这里演示用&#xff0c;phpstudy搭建的…

华为端口隔离高级用法经典案例

最终效果&#xff1a; pc4不能ping通pc5&#xff0c;pc5能ping通pc4 pc1不能和pc2、pc3通&#xff0c;但pc2和pc3能互通 vlan batch 2 interface Vlanif1 ip address 10.0.0.254 255.255.255.0 interface Vlanif2 ip address 192.168.2.1 255.255.255.0 interface MEth0/0/1 i…

基于SSM的校园快递管理系统

目录 前言 开发环境以及工具 项目功能介绍 学生&#xff1a; 管理员&#xff1a; 详细设计 获取源码 前言 本项目是一个基于IDEA和Java语言开发的基于SSM的校园快递管理系统应用。应用包含学生端和管理员端等多个功能模块。 欢迎使用我们的校园快递管理系统&#xff01;我…

厦门大学OpenHarmony技术俱乐部开创“1+N”新模式,加速推动产学研融合

12月29日,OpenHarmony技术俱乐部再添重将——在多方见证下,厦门大学OpenHarmony技术俱乐部在翔安校区益海嘉里楼报告厅正式揭牌成立,现场出席领导及师生代表近千人。 成立仪式现场 OpenHarmony技术俱乐部 携手厦门大学共绘开源生态新图景 OpenHarmony是由开放原子开源基金…

揭示AUTOSAR中隐藏的漏洞

AUTOSAR是一个普遍采用的软件框架&#xff0c;用于各种汽车零部件&#xff0c;如ABS, ECU,自动照明、环境控制、充电控制器、信息娱乐系统等。AUTOSAR的创建目的是促进汽车零部件之间形成标准接口&#xff0c;可以在不同制造商之间互通。 因此&#xff0c;任何配备微控制器(MC…

CGAL的无限制的Delaunay图

本章描述了构建L∞距离下线段Delaunay图的算法和几何特征。这些特征还包括绘制L∞距离下线段Delaunay图对偶&#xff08;即L∞距离下线段Voronoi图&#xff09;边缘的方法。L∞算法和特征依赖于欧几里得&#xff08;或L2&#xff09;距离下的线段Delaunay图算法和特征。L∞度量…

月报总结|Moonbeam 12月份大事一览

一转眼已经到年底啦。本月&#xff0c;Moonbeam基金会发布四个最新战略重点&#xff1a;跨链解决方案、游戏、真实世界资产&#xff08;RWA&#xff09;、新兴市场。其中在新兴市场方面&#xff0c;紧锣密鼓地推出与巴西公司Grupo RO的战略合作。 用户教育方面&#xff0c;为了…