Packetbuffer Basics (包缓冲区基础知识)

本文详细介绍了ContikiOS中包缓冲区的结构及操作方法。包括包属性设置、地址配置、数据长度设定等核心功能,并深入探讨了用于清除、复制及调整包缓冲区状态的相关函数。

Introduction

This tutorial is a introduction to different operations on the packet buffer on ContikiOS 2.7. We will also cover brief details about each of the functions and how they work. A packet buffer is a structure that is used to create a outbound packet or store a inbound packet. A packet buffer is also used to operate on a packet and it can store only one packet at a time.

A packet buffer has two section: header section and data section.
Buf.jpg
For outbound packets, the header is stored in packetbuf header section and data is stored in packetbuf data section.
Out.jpg
For incoming packets, both the packet header and the packet data is stored in the data section of the packetbuf.
In.jpg

You will learn

Through this tutorial you will learn about the structure of packet buffer and how exactly it is maintained in ContikiOS. You will also learn about different functions and possible operations on packet buffer. We will discuss each of the function in details to help you understand the current implementations. This will help you in any projects since packet buffer is necessary component for any networking project in contikiOS.

Source Codes

~/contiki-2.7/core/net/packetbuf.c

~/contiki-2.7/core/net/packetbuf.h


Description of the Parameters and Initialization of Packet Buffer Structure

In this section, we will cover the implementations details of packet buffer in ContikiOS. We also talk about different parameters and variables that are used to store some information about packet buffer and also to operate on the packet buffer.

Packet Attributes

In this section we will discuss the details about different attributes of the packet in packet buffer.

struct packetbuf_attr packetbuf_attrs[PACKETBUF_NUM_ATTRS];
struct packetbuf_addr packetbuf_addrs[PACKETBUF_NUM_ADDRS];

These two structures are used to store the attributes of the packet stored in packet buffer and the addresses related to the packet in packet buffer i.e. source and destination address. Here the addresses are RIME addresses which is the address format used in the contiki RIME stack. The respective structures are defined as follows.

typedef uint16_t packetbuf_attr_t;

struct packetbuf_attr {
/*   uint8_t type; */
  packetbuf_attr_t val;
};
struct packetbuf_addr {
/*   uint8_t type; */
  rimeaddr_t addr;
};

Therefore packetbuf_attrs is an array of packets attributes. The array size is PACKETBUF_NUM_ATTRS. Similarly, packetbuf_addrs is an array of packet addresses. The array size is PACKETBUF_NUM_ADDRS.

The sizes are defined as follows where PACKETBUF_ATTR_MAX=28:

//  File:   ~/contiki-2.7/core/net/packetbuf.h
#define PACKETBUF_NUM_ADDRS 4
#define PACKETBUF_NUM_ATTRS (PACKETBUF_ATTR_MAX - PACKETBUF_NUM_ADDRS)

The reason behind such definition is that the PACKETBUF_ATTR_MAX also counts the packet's associated rime addresses as the packet's attribute.

Now, to set an attribute we have to call the following function. We have to supply the attribute "type" and the "value":

static inline int
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
{
/*   packetbuf_attrs[type].type = type; */
  packetbuf_attrs[type].val = val;
  return 1;
}

The attribute types are defined as follows.

enum {
  PACKETBUF_ATTR_NONE,//or  integer value=0

  /* Scope 0 attributes: used only on the local node. */
  PACKETBUF_ATTR_CHANNEL,//or  integer value=1
  PACKETBUF_ATTR_NETWORK_ID,//or  integer value=2
  PACKETBUF_ATTR_LINK_QUALITY,//or  integer value=3
  PACKETBUF_ATTR_RSSI,//or  integer value=4
  PACKETBUF_ATTR_TIMESTAMP,//or  integer value=5
  PACKETBUF_ATTR_RADIO_TXPOWER,//or  integer value=6
  PACKETBUF_ATTR_LISTEN_TIME,//or  integer value=7
  PACKETBUF_ATTR_TRANSMIT_TIME,//or  integer value=8
  PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS,//or  integer value=9
  PACKETBUF_ATTR_MAC_SEQNO,//or  integer value=10
  PACKETBUF_ATTR_MAC_ACK,//or  integer value=11

  /* Scope 1 attributes: used between two neighbors only. */
  PACKETBUF_ATTR_RELIABLE,//or  integer value=12
  PACKETBUF_ATTR_PACKET_ID,//or  integer value=13
  PACKETBUF_ATTR_PACKET_TYPE,//or  integer value=14
  PACKETBUF_ATTR_REXMIT,//or  integer value=15
  PACKETBUF_ATTR_MAX_REXMIT,//or  integer value=16
  PACKETBUF_ATTR_NUM_REXMIT,//or  integer value=17
  PACKETBUF_ATTR_PENDING,//or  integer value=18
  
  /* Scope 2 attributes: used between end-to-end nodes. */
  PACKETBUF_ATTR_HOPS,//or  integer value=19
  PACKETBUF_ATTR_TTL,//or  integer value=20
  PACKETBUF_ATTR_EPACKET_ID,//or  integer value=21
  PACKETBUF_ATTR_EPACKET_TYPE,//or  integer value=22
  PACKETBUF_ATTR_ERELIABLE,//or  integer value=23

  /* These must be last */
  PACKETBUF_ADDR_SENDER,//or  integer value=24
  PACKETBUF_ADDR_RECEIVER,//or  integer value=25
  PACKETBUF_ADDR_ESENDER,//or  integer value=26
  PACKETBUF_ADDR_ERECEIVER,//or  integer value=27
  
  PACKETBUF_ATTR_MAX //or  integer value=28
};

Therefore to choose an attribute you have to choose from the first 24 entries. For example, packetbuf_set_attr(PACKETBUF_ATTR_RSSI,8) will set the RSSIattribute packet of the packet to be 8.

Similarly to get the value of a certain packet attribute, we have to use following function.

static inline packetbuf_attr_t
packetbuf_attr(uint8_t type)
{
  return packetbuf_attrs[type].val;
}

For example, packetbuf_attr(PACKETBUF_ATTR_RSSI) will return the RSSI value associated with the packet.

Now, to set the packet addresses, we have to use the packetbuf_set_addr(type,*addr) function. Here type can be PACKETBUF_ADDR_SENDER, PACKETBUF_ADDR_RECEIVER, PACKETBUF_ADDR_ESENDER or PACKETBUF_ADDR_ERECEIVER i.e any value between 24-27. The value of PACKETBUF_ADDR_FIRST is 24. Here PACKETBUF_ADDR_ESENDER or PACKETBUF_ADDR_ERECEIVER are used for multihop routing. The data stored by a=these four types of fields are as follows.

  1. PACKETBUF_ADDR_SENDER: Immediate sender
  2. PACKETBUF_ADDR_RECEIVER: Immediate Reciever
  3. PACKETBUF_ADDR_ESENDER: Original Sender of a multihop packet
  4. PACKETBUF_ADDR_ERECEIVER: Original Receiver of a multihop packet

Say, node 4.0 sends a packet to node 1.0 through node 3.0 and node 2.0 . When node 3.0 forwards a packet from node 4.0 to node 2.0 the respective addresses will be PACKETBUF_ADDR_SENDER=3.0, PACKETBUF_ADDR_RECEIVER=2.0, PACKETBUF_ADDR_ESENDER =4.0, PACKETBUF_ADDR_ERECEIVER=1.0;

static inline int
packetbuf_set_addr(uint8_t type, const rimeaddr_t *addr)
{
/*   packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].type = type; */
  rimeaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr); 
//Since packetbuf_addrs is an array of size 4
  return 1;
}

Similarly, to get the address attributes we have to use the following function.

static inline const rimeaddr_t *
packetbuf_addr(uint8_t type)
{
  return &packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr;
}


There are two more function that copies the entire attribute array. They are as follows.

void
packetbuf_attr_copyto(struct packetbuf_attr *attrs,
		    struct packetbuf_addr *addrs)
{
  memcpy(attrs, packetbuf_attrs, sizeof(packetbuf_attrs));
  memcpy(addrs, packetbuf_addrs, sizeof(packetbuf_addrs));
}

void
packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
		      struct packetbuf_addr *addrs)
{
  memcpy(packetbuf_attrs, attrs, sizeof(packetbuf_attrs));
  memcpy(packetbuf_addrs, addrs, sizeof(packetbuf_addrs));
}

Some Important variables

In this section we will talk about the structure which actually implements the packet buffer. We will also talk about some important variables and pointers used to modify and access the actual data in the packet buffer.

The packet buffer is implemented by using a array structure as follows.

/* The declarations below ensure that the packet buffer is aligned on an even 16-bit boundary.
 On some platforms (most notably the msp430), having a potentially misaligned packet buffer
 may lead to problems when accessing 16-bit values. */
static uint16_t packetbuf_aligned[(PACKETBUF_SIZE + PACKETBUF_HDR_SIZE) / 2 + 1]; //Array of 16 bit elements
static uint8_t *packetbuf = (uint8_t *)packetbuf_aligned;
//Array of 8 bit elements. So each entry of the array is a character

where PACKETBUF_SIZE is the maximum size of a packet including header and PACKETBUF_HDR_SIZE is the size of the header of a packet. packetbuf points to the array that stores the packet data. Note the conversion from uint16_t array structure to a uint8_t array structure. The reason behind using uint8_t format is that it can store a character(8 bit), therefore each entry of the array can be used to store a character. The size of the buffer is (PACKETBUF_SIZE + PACKETBUF_HDR_SIZE + 2).

The reason to consider the buffer size to be (PACKETBUF_SIZE + PACKETBUF_HDR_SIZE + 2) is that the incoming packets are stored entirely in the data section. Therefore the data section of the buffer should have enough space to accommodate an entire packet which can be equal to PACKETBUF_SIZE number of bytes/characters.


By default PACKETBUF_SIZE is 128 and PACKETBUF_HDR_SIZE is 48 (Defined in ~/contiki-2.7/core/net/packetbuf.h)

static uint8_t *packetbufptr;
//This is a pointer used to point to the memory location which stores the packet's data/payload part.

static uint16_t buflen, bufptr;
static uint8_t hdrptr;
packetbufptr is used to point to the memory location which stores the packet's data/payload part.

The data section may be in the packet buffer structure or may be in some external location. In either cases, packetbufptr points to the actual address of the packet's payload/data which may be part of packetbuf or maybe some external memory address.

hdrptr stores the index of the packetbuf array where the first character of the header of a packet is stored. 

For outgoing packets, the header is stored in the slots with number starting from hdrptr to PACKETBUF_HDR_SIZE-1. Keep in mind that the first location of an array is numbered as 0. Therefore, the header is stored in packetbuf[hdrptr] to packetbuf[PACKETBUF_HDR_SIZE-1]. For example say i.e say thePACKETBUF_HDR_SIZE is 9 and hdrptr=5 then packetbuf[5:8] contains the header.

Header.jpg


buflen stores the size of the packet's payload/data that is stored in the data section of the packetbuf. 

For outgoing packets, it is the length of the packet's payload or data part. For incoming packet, since the whole packet is stored in the data section of the buffer, buflen referes to the total size of the packet.

bufptr is used to locate the first character of the packet's data part/payload within the packetbuf array. 

The value stored in the bufptr is a offset value. When we add bufptr value with PACKETBUF_HDR_SIZE, we get the index of the array which stores the first character of the packet's payload/data part. Here is an example for bufptr=4.

Bufptr.jpg

Description of the different Functions that delete data of packetbuf

void packetbuf_clear(void)

This function clears the packetbuf and resets all internal state pointers (header size, header pointer, external data pointer). It is used before preparing a packet in the packetbuf or copying a packet to the packetbuf.

However this function doesn't delete the previous data. It just resets the pointers and variables discussed in the previous section. The source code for this function is as follows.

buflen = bufptr = 0;
hdrptr = PACKETBUF_HDR_SIZE;
packetbufptr = &packetbuf[PACKETBUF_HDR_SIZE];
packetbuf_attr_clear();

The following code resets the variables that points to the payload section. Setting buflen to be zero means that the buffer doesn't contain a payload/data part of a packet. Setting bufptr to be zero implies that the new packet's data must be stores from the beginning of the buffer's data section.

buflen = bufptr = 0;


The following lines reset the header pointer to the end of the header section of the buffer which implies that the there is no header. It also sets the packetbufptr pointer to the first location of the array after the the header section which is also the beginning of the payload section.

hdrptr = PACKETBUF_HDR_SIZE;
packetbufptr = &packetbuf[PACKETBUF_HDR_SIZE];


Note that this function doesn't delete the content of the memory. It just resets the pointers. You should be careful about that while using this function in your code.

packetbuf_attr_clear();

Now, this function clears all the attributes including the address fields of the buffer. The code is as follows

void
packetbuf_attr_clear(void)
{
  int i;
  for(i = 0; i < PACKETBUF_NUM_ATTRS; ++i) {
    packetbuf_attrs[i].val = 0;  // Reset the attribute value to 0 i.e NULL
  }
  for(i = 0; i < PACKETBUF_NUM_ADDRS; ++i) {
    rimeaddr_copy(&packetbuf_addrs[i].addr, &rimeaddr_null); // Reset the rime address values to 0 i.e NULL
  }
}

void packetbuf_clear_hdr(void)

This function only resets the header field of the packet buffer.This function resets all the internal state pointers pertaining to the header (header size, header pointer, but not external data pointer). It is used after sending a packet in the packetbuf, to be able to reuse the packet buffer for a later retransmission.

hdrptr = PACKETBUF_HDR_SIZE;

Similar to the packet buffer clear operation, this line just resets the header field pointer to the end of the header section of the buffer which thereby implies that there is no header. It doesn't delete the content of the memory. You should be careful about that while coding. Here is a example with PACKETBUF_HDR_SIZE=10.

After clear.jpg

void packetbuf_hdr_remove(int size)

This function is used to remove the first part of the header in the packetbuf, when processing outgoing packets. It removes the first "size" number of entries of the header which is nothing but a pointer operation where the hdrptr pointer has to be set to point to the new beginning. The following code does exactly the same thing.

hdrptr += size;

It moves the header pointer upwards by "size" number of slots.

Note that, this is a operation for outgoing packets only.

Here is an example with size=4, hdrptr=1 and PACKETBUF_HDR_SIZE=13.

Example remove.jpg

void packetbuf_hdrreduce(int size)

This function is used to remove the first part of the header in the packetbuf, when processing incoming packets. If the function is unable to remove the requested amount of header space, the function returns zero and does nothing.

if(buflen < size) {
  return 0;
}

bufptr += size;
buflen -= size;
return 1;

Since the incoming packets are stored in the data section of the buffer, bufptr is actually the offset to get the location of the packet's first byte which is also the first byte of the packet's header. Therefore, to remove the first size number of bytes of the packet header, we just need to increase the bufptr value i.e the offset value.

bufptr += size;

Since the size of the packer is now lesser than actual and since for incoming packet buflen is the total packet length, the function reduces buflen by "size".

buflen -= size;
Note that, this is a operation for inbound/incoming packets only.

Here is an example with PACKETBUF_HDR_SIZE =6 and "size"=2.

Packetbuf hdrreduce.jpg

Description of the Functions related to different length parameters

void packetbuf_set_datalen(uint16_t len)

For outbound packets, the packetbuf consists of two parts: header and data. This function is used to set the length of the data in the packetbuf. The only thing that we need to do is to set the "buflen" value to be "len" since "buflen" value is used to refer to the data section of the packet. The data section will be located in the array entries with indexes starting from (PACKETBUF_HDR_SIZE + bufptr) to (PACKETBUF_HDR_SIZE + bufptr + buflen - 1).

buflen = len;

Here is an example for outbound packet, with len=8 and bufptr=0.

Data len.jpg

uint16_t packetbuf_datalen(void)

This function returns the length/amount of data stored in the data section of the packet buffer.

For outbound packets, the packetbuf consists of two parts: header and data. 
This function is used to get the length of the data in the packetbuf. 
For incoming packets, both the packet header and the packet data is stored in the data portion of the packetbuf. 
This function is then used to get the total length of the packet - both header and data.

Since the value is already stored in buflen, the only operation of this function is to return the value of buflen. This emphasizes on the importance of the variable buflen. If it is not set properly, the all the operations on the current packet will be erroneous.

The ContikiOS code for this function is as follows.

return buflen;

uint8_t packetbuf_hdrlen(void)

This function is used to get the length of the header in the packetbuf. The header is stored in the packetbuf and accessed via the packetbuf_hdrptr() function.

This is useful only for the outgoing packets since incoming packets are stored entirely in the data section.
Therefore, for incoming packets it will return 0.

Since the header is located at the array locations starting from hdrptr (inclusive) to PACKETBUF_HDR_SIZE-1 (inclusive) the size of the header is nothing but the number of slots between them. The value is same as PACKETBUF_HDR_SIZE - hdrptr. Say, PACKETBUF_HDR_SIZE =8, hdrptr=3, then the header is stored in locations 3,4,5,6,7 i.e header size=5 = 8-3 = PACKETBUF_HDR_SIZE-hdrptr.

return PACKETBUF_HDR_SIZE - hdrptr;

uint16_t packetbuf_totlen(void)

This function is used to get the total length of the header and data in the packetbuf.

As discussed already, for an outbound packet, packetbuf_hdrlen() returns the header length and packetbuf_datalen() return the length of the data section. For an inbound/incoming packet, packetbuf_hdrlen() returns "0" and packetbuf_datalen() returns the total length of the packet.

Thereby the code to get the total length is as follows.

return packetbuf_hdrlen() + packetbuf_datalen();

Description of the Functions related to different pointers

void *packetbuf_dataptr(void)

This function is used to get a pointer to the data in the packetbuf. The data is either stored in the packetbuf, or referenced to an external location. For outbound packets, the packetbuf consists of two parts: header and data. The header is accessed with the packetbuf_hdrptr() function.

So, for outbound packets this function is used to get a pointer to the data/payload of the packets.

For incoming packets, both the packet header and the packet data is stored in the data portion of the packetbuf.

So, for incoming packets this function is used to get a pointer to the header of the incoming packets.

return (void *)(&packetbuf[bufptr + PACKETBUF_HDR_SIZE]);

In this case bufptr value is the offset from the buffers data section beginning to point to the location that contains the first character of the packet.

For outgoing packets, if bufptr=0, the packet's data/payload are stored from the beginning of the buffer's data section i.e at buffer index = PACKETBUF_HDR_SIZE.

However, for incoming packet if bufptr=0, first character of data is at buffer index = (PACKETBUF_HDR_SIZE + packet's header size), since both the packet header and the packet data are stored in the data portion of the packetbuf.

Here is an example scenario.

Dataptr.jpg

void *packetbuf_hdrptr(void)

For outbound packets, the packetbuf consists of two parts: header and data. This function is used to get a pointer to the header in the packetbuf. The header is stored in the packetbuf. "hdrptr" provides the index of the first character's location in the packetbuf array. Therefore this function just returns the address of the packetbuf array entry pointed by hdrptr.

For inbound packets, hdrptr= PACKETBUF_HDR_SIZE.

This function is useful only for outbound/outgoing packets. 

The code for this function is as follows.

return (void *)(&packetbuf[hdrptr]);

void packetbuf_reference(void *ptr, uint16_t len)

This function is used to make the packetbuf point to external data. The function also specifies the length of the external data that the packetbuf references. The "ptr" argument points to the external data and the "len" is the length of the external data. Since the packet buffer doesn't store the data section anymore, the buffer should be cleared.

The code for this function is as follows.

packetbuf_clear();//clears the buffer
packetbufptr = ptr;//points to the external data
buflen = len;//Set the data length to be equal to the external data length

int packetbuf_is_reference(void)

For outbound packets, the packetbuf consists of two parts: header and data. This function is used to check if the packetbuf points to external data that has previously been referenced with packetbuf_reference().

return packetbufptr != &packetbuf[PACKETBUF_HDR_SIZE];

This function checks whether the packetbufptr points to the data section of the packetbuf. If yes, then the function returns 0 since the external data has not been previously referenced. Otherwise it return TRUE or 1. Therefore this function is a boolean function.

void *packetbuf_reference_ptr(void)

For outbound packets, the packetbuf consists of two parts: header and data. The data may point to external data that has previously been referenced with packetbuf_reference(). This function is used to get a pointer to the external data.

If there is no external data, it will return a pointer to the beginning of the data section in the packetbuf.

return packetbufptr;

Description of the Functions related to Copying

int packetbuf_copyfrom(const void *from, uint16_t len)

This function copies data from the memory location pointed by "from" into the packetbuf. If the data that is to be copied is larger than the packetbuf, only the data that fits in the packetbuf is copied. The number of bytes that could be copied into the packetbuf is returned.


{
  uint16_t l;

  packetbuf_clear(); //Clears the buffer before storing the new data 
  /* check if the size of the data is greater than the size of a packet i.e PACKETBUF_SIZE. 
  If yes, then copy only the first PACKETBUF_SIZE bytes of the data. Otherwise copy everything.*/
  l = len > PACKETBUF_SIZE? PACKETBUF_SIZE: len; 
  // the variable l stores the amount of data to be copied to packetbuf
  memcpy(packetbufptr, from, l);
  //packetbufptr points to the beginning of the packet buffer data section i.e where the data need to be stored.
  buflen = l;
  return l;
}

void packetbuf_compact(void)

This function compacts the packetbuf by copying the data portion of the packetbuf so that becomes consecutive to the header. It also copies external data that has previously been referenced with packetbuf_reference() into the packetbuf.

This function is called by the Rime code before a packet is to be sent by a device driver. This assures that the entire packet is consecutive in memory.

Note that, this code assumes the lengths and other variables to be set properly.

The source code of this function is as follows.

{
  int i, len;

  if(packetbuf_is_reference()) {
    memcpy(&packetbuf[PACKETBUF_HDR_SIZE], packetbuf_reference_ptr(),
	   packetbuf_datalen());
  } else if(bufptr > 0) {
    len = packetbuf_datalen() + PACKETBUF_HDR_SIZE;
    for(i = PACKETBUF_HDR_SIZE; i < len; i++) {
      packetbuf[i] = packetbuf[bufptr + i];
    }

    bufptr = 0;
  }
}

Now, we discuss the code in more details.

The first step is to check whether the data is external or not. If yes then we can simply copy the data into the buffer. The following code does exactly that. As discussed before packetbuf_is_reference() is a Boolean function to check whether the packet's data section refer to a external data, packetbuf_reference_ptr() return the pointer to the external data and packetbuf_datalen() return the number of bytes to be copied i.e. the length of the data section.

if(packetbuf_is_reference()) {
    memcpy(&packetbuf[PACKETBUF_HDR_SIZE], packetbuf_reference_ptr(),packetbuf_datalen());
}

Here is an example illustrating the above mentioned concept.

Packetbuf compact2.jpg

If the data is not external, but the data is not compactly stored, this function makes it compact. If bufptr > 0, then there is a gap or offset between the actual beginning location of the data section and the supposed beginning location of the data section i.e the location with id = PACKETBUF_HDR_SIZE. This function shift the data to the supposed location within the buffer. The following code does that.

len = packetbuf_datalen() + PACKETBUF_HDR_SIZE; //This gives us the supposed location for the last byte of the packet
for(i = PACKETBUF_HDR_SIZE; i < len; i++) { 
      packetbuf[i] = packetbuf[bufptr + i]; //This line shift the data to be the actual location
}

bufptr = 0; //Since the data is shifted to the beginning of the buffer's data section, the offset is now 0 again.

Here is an example illustrating the above mentioned situation.

Packetbuf compact1.jpg

int packetbuf_copyto_hdr(uint8_t *to)

This function copies the header portion of the packetbuf to an external buffer. The external buffer to which the packetbuf is to be copied must be able to accomodate at least PACKETBUF_HDR_SIZE bytes. The number of bytes that was copied to the external buffer is returned.

The source code this function is as follows.

{
#if DEBUG_LEVEL > 0
  {
    int i;
    PRINTF("packetbuf_write_hdr: header:\n");
    for(i = hdrptr; i < PACKETBUF_HDR_SIZE; ++i) {
      PRINTF("0x%02x, ", packetbuf[i]);
    }
    PRINTF("\n");
  }
#endif /* DEBUG_LEVEL */
  memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);//packetbuf points to the beginning of the buffer array.
  return PACKETBUF_HDR_SIZE - hdrptr;
}


In the above potion of code, the main line is the following

memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);

where "to" is the destination address. (packetbuf + hdrptr) is the address of the buffer entry which stores the first byte of the packet's header.(PACKETBUF_HDR_SIZE - hdrptr) is the actual size of the header.

Note that, this function should be used only for outgoing packet. Since the incoming packet header is stored in the data section of the buffer, this approach will not work (PACKETBUF_HDR_SIZE - hdrptr=0) for that.


int packetbuf_copyto(void *to)

This function copies the packetbuf to an external buffer. Both the data portion and the header portion of the packetbuf is copied. If the packetbuf referenced external data (referenced with packetbuf_reference()) the external data is copied.

The external buffer to which the packetbuf is to be copied must be able to accommodate at least PACKETBUF_SIZE bytes. The number of bytes that was copied to the external buffer is returned.


{
#if DEBUG_LEVEL > 0
  {
         ...
  }
#endif /* DEBUG_LEVEL */
  
  if(PACKETBUF_HDR_SIZE - hdrptr + buflen > PACKETBUF_SIZE) {
    /* Too large packet */
    return 0;
  }
  memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);
  memcpy((uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr, packetbufptr + bufptr, buflen);
  return PACKETBUF_HDR_SIZE - hdrptr + buflen;
}


This function first copies from the header section of the buffer and then copy from the data section of the buffer. The following lines copies the data from the header section of the packetbuf. The destination address is "to" and the source address is the address of the entry packetbuf[hdrptr] which is(packetbuf+hdrptr)(PACKETBUF_HDR_SIZE - hdrptr) is the actual size of the header i.e the number of bytes to be copied for the header only.

memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);

The following lines copies the data from the data/payload section of the packetbuf. The destination address is "(uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr". The reason is that "PACKETBUF_HDR_SIZE - hdrptr" bytes of header data is already copied. This gives us the next available memory location. The source address is (packetbufptr + bufptr)"packetbufptr" points to the packet's data part beginning. If the data is external, "packetbufptr" points to the external source, otherwise "packetbufptr" points to packetbuf[PACKETBUF_HDR_SIZE]"buflen" is the actual size of the data i.e the number of bytes to be copied for the packet's data part only.

memcpy((uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr, packetbufptr + bufptr, buflen);



int packetbuf_hdralloc(int size)

This function is used to allocate extra space in the header portion in the packetbuf, when preparing outbound packets for transmission. If the function is unable to allocate sufficient header space, the function returns zero and does not allocate anything.

if(hdrptr >= size && packetbuf_totlen() + size <= PACKETBUF_SIZE) 
  {
    hdrptr -= size;
    return 1;
  }
  return 0;

if hdrptr >= size then, there is a possibility. But if the total length of the packet after adding extra space is greater PACKETBUF_SIZE then we can allocate more space.

if hdrptr <= size or packet total length + size > PACKETBUF_SIZE, it is not possible to allocate "size" amount of extra bytes or space.

hdrptr -= size;

This increases the header size. Since the end location of the header is fixed at PACKETBUF_HDR_SIZE-1, only way to assign more space is to move the beginning location pointer.

<think>好的,用户想了解Packet Buffer的概念和使用方法,特别是在网络或编程中的情况。我需要先明确Packet Buffer的定义和作用。根据之前的引用,比如引用[3]提到链路层处理数据,可能涉及到Packet Buffer的应用。首先,Packet Buffer应该是用于存储和管理网络数据的内存区域,这在网络协议栈中很常见。 接下来要分两部分回答:概念和使用方法。概念部分需要解释Packet Buffer的基本功能,比如临时存储数据,处理数据的组装、分片、排队等。可能需要提到它在OSI模型中的位置,比如在数据链路层或网络层的使用,引用[3]中的链路层相关内容可以作为支持。 然后是使用方法,这部分需要涵盖编程中的具体应用,比如在内核驱动、用户空间程序中的应用。可能需要涉及数据结构,如sk_buff在Linux内核中的使用,或者用户态库如DPDK的rte_mbuf。引用[1]中的socket创建示例可能相关,因为处理UDP数据时会用到Packet Buffer。此外,引用[2]提到的流处理和数据平面处理也涉及Packet Buffer的管理,比如流表查找和处理策略。 还要生成相关问题,确保问题与Packet Buffer相关,比如结构设计、优化技巧、与Socket Buffer的区别、在DPDK中的应用等。需要检查是否有相关的引用可以作为支持,但用户提供的引用可能有限,所以主要依赖通用知识。 最后,确保使用正确的中文表达,避免技术术语错误,并按照系统指令使用合适的LaTeX格式。例如,提到数据结构时不需要数学公式,但如果有涉及性能分析可能需要使用行内公式,比如缓冲区大小与延迟的关系$L = \frac{B}{\lambda}$。需要检查是否有必要加入这样的公式,但用户的问题可能不需要,所以可能不需要。 总结回答结构:先介绍概念,再分编程和网络中的使用方法,最后相关问题和引用。确保引用标记正确,例如在提到数据处理流程时引用[2],或者在链路层时引用[3]。需要确认引用内容是否恰当,比如引用[1]是关于socket创建的,可能与缓冲区分配有关,可以关联到内核中的Packet Buffer管理。</think>### Packet Buffer的概念及使用方法 #### 一、Packet Buffer的概念 Packet Buffer(数据缓冲区)是网络协议栈或应用程序中用于**临时存储网络数据**的内存区域。它主要用于以下场景: 1. **数据接收与发送**:网卡驱动将接收到的原始数据帧存入缓冲区,协议栈从中提取并处理;发送时则将封装好的数据暂存至缓冲区,等待传输[^3]。 2. **流量控制**:通过缓冲区队列管理网络拥塞,例如TCP滑动窗口机制依赖缓冲区实现流量控制。 3. **协议处理**:支持数据的分片(如IP分片)、重组(如TCP分段)和协议头解析。 在网络协议栈中,Packet Buffer通常与**分层处理**紧密相关。例如: - **链路层**:处理以太网帧的缓冲区管理[^3] - **网络层**:存储IP并进行路由决策 - **传输层**:管理TCP/UDP段的重排序和重组 #### 二、Packet Buffer的使用方法 ##### 1. 网络编程中的实现 在Linux内核中,`sk_buff`结构体是最核心的Packet Buffer实现: ```c struct sk_buff { struct sk_buff *next; struct sk_buff *prev; ktime_t tstamp; struct sock *sk; unsigned int len; char cb[48]; // ...其他元数据和数据区指针 }; ``` 其关键操作括: - **分配/释放**:通过`alloc_skb()`和`kfree_skb()`管理缓冲区生命周期 - **数据操作**:使用`skb_push()`(添加协议头)、`skb_pull()`(剥离协议头)调整数据指针 ##### 2. 用户态网络编程 在DPDK等高性能框架中,使用`rte_mbuf`结构实现零拷贝缓冲区: ```c struct rte_mbuf { void *buf_addr; // 数据缓冲区地址 uint16_t data_off; // 数据起始偏移 uint32_t pkt_len; // 总长度 uint16_t data_len; // 当前分段长度 // ...其他元数据 }; ``` 典型工作流程: 1. 从内存池预分配mbuf对象 2. 网卡DMA直接写入mbuf数据区 3. 应用程序通过`rte_eth_rx_burst()`获取数据 4. 处理后通过`rte_eth_tx_burst()`发送 ##### 3. 关键设计考量 - **内存对齐**:通常要求缓存行对齐(如64字节)以避免伪共享 - **DMA兼容性**:物理地址连续的内存区域可提升网卡DMA效率 - **批处理**:如引用[2]所述,流处理应尽量批量操作数据以提高缓存命中率 #### 三、性能优化技巧 1. **环形缓冲区(Ring Buffer)**:通过生产-消费者模型减少锁竞争 2. **内存池预分配**:避免运行时动态内存分配开销 3. **NUMA感知**:确保缓冲区内存与处理它的CPU核心位于同一NUMA节点 4. **巨型帧(Jumbo Frame)支持**:调整缓冲区大小以适应不同MTU需求 $$ \text{理论吞吐量} = \frac{\text{Buffer Size}}{\text{Latency}} \times \text{Packet Rate} $$
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值