【LwIP】移植(FreeRTOS)

基於操作系統FreeRTOS的移植又比我想象的複雜一點,雖然前面的文章中移植的LwIP的工程也是基於FreeRTOS系統的,但是將所有網絡操作都放在了同一個線程中,相當於模擬了無操作系統的情況,使用的是RAW API進行程序設計的。使用RAW API有一個非常非常侷限的地方,就是不能再不同的上下文環境(Context)下同時調用系統API,就像LwIP自己說的:Use lwIP without OS-awareness (no thread, semaphores, mutexes or mboxes). This means threaded APIs cannot be used (socket, netconn, i.e. everything in the 'api' folder), only the callback-style raw API is available (and you have to watch out for yourself that you don't access lwIP functions/structures from more than one context at a time!)。最後一句話說得很清楚,我們不能再多個線程中同時調用LwIP的API函數和結構,因爲它們都是線程不安全的(thread unsafe),通俗點舉例,我們不能同時在兩個不同的線程中創建兩個TCP連接,否則容易產生BUG。

這個問題是十分棘手的,我們需要在一個線程中完成所有網絡操作,包括網卡數據包的接收、運行TCPIP協議棧、創建TCP/UDP連接、接收發送網絡數據以及與其它應用線程進行通訊。LwIP的作者早就意識到這個問題了,他們設計了兩種高層API接口:Sequential API、BSD socket API,本文只說明Sequential API的移植。LwIP的作者創建了一個tcpip線程,主要完成前面我說的網絡操作,並且提供了非常友好的API接口,使得應用程序的設計相比於RAW API變得十分簡潔明瞭。下面是移植Sequential API版本的LwIP的步驟:

1、想要使用Sequential API,我麼需要先在LwIP的配置文件lwipopts.h中將NO_SYS設置成0,LWIP_NETCONN設置成1。然後將LwIP的源碼中的api文件夾中的所有文件都添加到工程中去(不管用沒用到,沒用到的不會被編譯)。

2、創建一個線程,主要用於接收網卡數據,注意這個線程需要在網卡硬件初始化完成之後才能執行。

void Task_network( void *pvParameters )
{
	printf("Task network started\r\n");
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while(1)
	{  
		while(ETH_CheckFrameReceived())
		{ 
			/* process received ethernet packet */
			ethernetif_input(&netif_st);
		}
		vTaskDelayUntil(&xLastWakeTime,10);	//10ms運行週期
	}
}

3、創建sys_arch.c、sys_arch.h文件,這兩個文件是與操作系統相關的,實現了sys.h頭文件中定義的一些操作系統相關的接口函數,LwIP 1.4.1版本的sys.h中定義了一些需要用戶定義的函數,信號量相關的函數定義:

/* Semaphore functions: */

/** Create a new semaphore
 * @param sem pointer to the semaphore to create
 * @param count initial count of the semaphore
 * @return ERR_OK if successful, another err_t otherwise */
err_t sys_sem_new(sys_sem_t *sem, u8_t count);
/** Signals a semaphore
 * @param sem the semaphore to signal */
void sys_sem_signal(sys_sem_t *sem);
/** Wait for a semaphore for the specified timeout
 * @param sem the semaphore to wait for
 * @param timeout timeout in milliseconds to wait (0 = wait forever)
 * @return time (in milliseconds) waited for the semaphore
 *         or SYS_ARCH_TIMEOUT on timeout */
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
/** Delete a semaphore
 * @param sem semaphore to delete */
void sys_sem_free(sys_sem_t *sem);
/** Wait for a semaphore - forever/no timeout */
#define sys_sem_wait(sem)                  sys_arch_sem_wait(sem, 0)
#ifndef sys_sem_valid
/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */
int sys_sem_valid(sys_sem_t *sem);
#endif
#ifndef sys_sem_set_invalid
/** Set a semaphore invalid so that sys_sem_valid returns 0 */
void sys_sem_set_invalid(sys_sem_t *sem);
#endif

/* Time functions. */
#ifndef sys_msleep
void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */
#endif

消息郵箱相關函數定義:

/* Mailbox functions. */

/** Create a new mbox of specified size
 * @param mbox pointer to the mbox to create
 * @param size (miminum) number of messages in this mbox
 * @return ERR_OK if successful, another err_t otherwise */
err_t sys_mbox_new(sys_mbox_t *mbox, int size);
/** Post a message to an mbox - may not fail
 * -> blocks if full, only used from tasks not from ISR
 * @param mbox mbox to posts the message
 * @param msg message to post (ATTENTION: can be NULL) */
void sys_mbox_post(sys_mbox_t *mbox, void *msg);
/** Try to post a message to an mbox - may fail if full or ISR
 * @param mbox mbox to posts the message
 * @param msg message to post (ATTENTION: can be NULL) */
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
/** Wait for a new message to arrive in the mbox
 * @param mbox mbox to get a message from
 * @param msg pointer where the message is stored
 * @param timeout maximum time (in milliseconds) to wait for a message
 * @return time (in milliseconds) waited for a message, may be 0 if not waited
           or SYS_ARCH_TIMEOUT on timeout
 *         The returned time has to be accurate to prevent timer jitter! */
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */
#ifndef sys_arch_mbox_tryfetch
/** Wait for a new message to arrive in the mbox
 * @param mbox mbox to get a message from
 * @param msg pointer where the message is stored
 * @param timeout maximum time (in milliseconds) to wait for a message
 * @return 0 (milliseconds) if a message has been received
 *         or SYS_MBOX_EMPTY if the mailbox is empty */
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg);
#endif
/** For now, we map straight to sys_arch implementation. */
#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
/** Delete an mbox
 * @param mbox mbox to delete */
void sys_mbox_free(sys_mbox_t *mbox);
#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0)
#ifndef sys_mbox_valid
/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
int sys_mbox_valid(sys_mbox_t *mbox);
#endif
#ifndef sys_mbox_set_invalid
/** Set an mbox invalid so that sys_mbox_valid returns 0 */
void sys_mbox_set_invalid(sys_mbox_t *mbox);
#endif

線程相關函數定義:

/** The only thread function:
 * Creates a new thread
 * @param name human-readable name for the thread (used for debugging purposes)
 * @param thread thread-function
 * @param arg parameter passed to 'thread'
 * @param stacksize stack size in bytes for the new thread (may be ignored by ports)
 * @param prio priority of the new thread (may be ignored by ports) */
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio);

在sys_arch.h文件中定義信號量、消息郵箱以及任務的數據類型:

typedef xSemaphoreHandle sys_sem_t;
typedef xQueueHandle sys_mbox_t;
typedef xTaskHandle sys_thread_t;

在sys_arch.c文件中實現RTOS相關函數(現成的一個sys_arch.c函數進行大量修改得到):

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <[email protected]>
 *
 */

/* lwIP includes. */
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#include "FreeRTOS.h"
#include "task.h"


xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION;

// struct timeoutlist
// {
// 	struct sys_timeouts timeouts;
// 	xTaskHandle pid;
// };

/* This is the number of threads that can be started with sys_thread_new() */
#define SYS_THREAD_MAX 6

// static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX];
static u16_t s_nextthread = 0;


/*-----------------------------------------------------------------------------------*/
//  Creates an empty mailbox.
// sys_mbox_t sys_mbox_new(int size)
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
	*mbox = xQueueCreate( size, sizeof( void * ) );

#if SYS_STATS
      ++lwip_stats.sys.mbox.used;
      if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
         lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
	  }
#endif /* SYS_STATS */

	return ERR_OK;
}

/*-----------------------------------------------------------------------------------*/
/*
  Deallocates a mailbox. If there are messages still present in the
  mailbox when the mailbox is deallocated, it is an indication of a
  programming error in lwIP and the developer should be notified.
*/
void sys_mbox_free(sys_mbox_t *mbox)
{
	if( uxQueueMessagesWaiting( *mbox ) )
	{
		/* Line for breakpoint.  Should never break here! */
		portNOP();
#if SYS_STATS
	    lwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */
			
		// TODO notify the user of failure.
	}

	vQueueDelete( *mbox );

#if SYS_STATS
     --lwip_stats.sys.mbox.used;
#endif /* SYS_STATS */
}

int sys_mbox_valid(sys_mbox_t *mbox)
{
	if(*mbox)
		return 1;
	else
		return 0;
}
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{
// 	vQueueDelete(*mbox);
// 	*mbox = NULL;
}
int sys_sem_valid(sys_sem_t *sem)
{
	if(*sem)
		return 1;
	else
		return 0;
}
void sys_sem_set_invalid(sys_sem_t *sem)
{
// 	vSemaphoreDelete(*sem);
// 	*sem = NULL;
}



/*-----------------------------------------------------------------------------------*/
//   Posts the "msg" to the mailbox.
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
	while ( xQueueSendToBack(*mbox, &msg, portMAX_DELAY ) != pdTRUE ){}
}


/*-----------------------------------------------------------------------------------*/
//   Try to post the "msg" to the mailbox.
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
err_t result;

   if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS )
   {
      result = ERR_OK;
   }
   else {
      // could not post, queue must be full
      result = ERR_MEM;
			
#if SYS_STATS
      lwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */
			
   }

   return result;
}

/*-----------------------------------------------------------------------------------*/
/*
  Blocks the thread until a message arrives in the mailbox, but does
  not block the thread longer than "timeout" milliseconds (similar to
  the sys_arch_sem_wait() function). The "msg" argument is a result
  parameter that is set by the function (i.e., by doing "*msg =
  ptr"). The "msg" parameter maybe NULL to indicate that the message
  should be dropped.

  The return values are the same as for the sys_arch_sem_wait() function:
  Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
  timeout.

  Note that a function with a similar name, sys_mbox_fetch(), is
  implemented by lwIP.
*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
void *dummyptr;
portTickType StartTime, EndTime, Elapsed;

	StartTime = xTaskGetTickCount();

	if ( msg == NULL )
	{
		msg = &dummyptr;
	}
		
	if ( timeout != 0 )
	{
		if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) )
		{
			EndTime = xTaskGetTickCount();
			Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
			
			return ( Elapsed );
		}
		else // timed out blocking for message
		{
			*msg = NULL;
			
			return SYS_ARCH_TIMEOUT;
		}
	}
	else // block forever for a message.
	{
		while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary
		EndTime = xTaskGetTickCount();
		Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
		
		return ( Elapsed ); // return time blocked TODO test	
	}
}

/*-----------------------------------------------------------------------------------*/
/*
  Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll
  return with SYS_MBOX_EMPTY.  On success, 0 is returned.
*/
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
void *dummyptr;

	if ( msg == NULL )
	{
		msg = &dummyptr;
	}

   if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) )
   {
      return ERR_OK;
   }
   else
   {
      return SYS_MBOX_EMPTY;
   }
}

/*-----------------------------------------------------------------------------------*/
//  Creates and returns a new semaphore. The "count" argument specifies
//  the initial state of the semaphore.
err_t sys_sem_new(sys_sem_t *xSemaphore, u8_t count)
{
	*xSemaphore = xSemaphoreCreateBinary();
	
	if( *xSemaphore == NULL )
	{
		
#if SYS_STATS
      ++lwip_stats.sys.sem.err;
#endif /* SYS_STATS */
			
		return ERR_BUF;	// TODO need assert
	}
	
	if(count == 0)	// Means it can't be taken
	{
		xSemaphoreTake(*xSemaphore,0);
	}
	else
	{
		xSemaphoreGive( *xSemaphore );
	}

#if SYS_STATS
	++lwip_stats.sys.sem.used;
 	if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {
		lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
	}
#endif /* SYS_STATS */
		
	return ERR_OK;
}

/*-----------------------------------------------------------------------------------*/
/*
  Blocks the thread while waiting for the semaphore to be
  signaled. If the "timeout" argument is non-zero, the thread should
  only be blocked for the specified time (measured in
  milliseconds).

  If the timeout argument is non-zero, the return value is the number of
  milliseconds spent waiting for the semaphore to be signaled. If the
  semaphore wasn't signaled within the specified time, the return value is
  SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
  (i.e., it was already signaled), the function may return zero.

  Notice that lwIP implements a function with a similar name,
  sys_sem_wait(), that uses the sys_arch_sem_wait() function.
*/
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
portTickType StartTime, EndTime, Elapsed;

	StartTime = xTaskGetTickCount();

	if(	timeout != 0)
	{
		if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE )
		{
			EndTime = xTaskGetTickCount();
			Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
			
			return (Elapsed); // return time blocked TODO test	
		}
		else
		{
			return SYS_ARCH_TIMEOUT;
		}
	}
	else // must block without a timeout
	{
// 		while( xSemaphoreTake( *sem, portMAX_DELAY ) != pdTRUE ){}
		xSemaphoreTake( *sem, portMAX_DELAY );
		EndTime = xTaskGetTickCount();
		Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;

		return ( Elapsed ); // return time blocked	
		
	}
}

/*-----------------------------------------------------------------------------------*/
// Signals a semaphore
void sys_sem_signal(sys_sem_t *sem)
{
	xSemaphoreGive( *sem );
}

/*-----------------------------------------------------------------------------------*/
// Deallocates a semaphore
void sys_sem_free(sys_sem_t *sem)
{
#if SYS_STATS
      --lwip_stats.sys.sem.used;
#endif /* SYS_STATS */
			
	vQueueDelete( *sem );
}

/*-----------------------------------------------------------------------------------*/
// Initialize sys arch
void sys_init(void)
{
	int i;

	// Initialize the the per-thread sys_timeouts structures
	// make sure there are no valid pids in the list
	for(i = 0; i < SYS_THREAD_MAX; i++)
	{
// 		s_timeoutlist[i].pid = 0;
// 		s_timeoutlist[i].timeouts.next = NULL;
	}

	// keep track of how many threads have been created
	s_nextthread = 0;
}

/*-----------------------------------------------------------------------------------*/
/*
  Returns a pointer to the per-thread sys_timeouts structure. In lwIP,
  each thread has a list of timeouts which is represented as a linked
  list of sys_timeout structures. The sys_timeouts structure holds a
  pointer to a linked list of timeouts. This function is called by
  the lwIP timeout scheduler and must not return a NULL value.

  In a single threaded sys_arch implementation, this function will
  simply return a pointer to a global sys_timeouts variable stored in
  the sys_arch module.
*/
// struct sys_timeouts *sys_arch_timeouts(void)
// {
// int i;
// xTaskHandle pid;
// struct timeoutlist *tl;

// 	pid =  xTaskGetCurrentTaskHandle();
//               

// 	for(i = 0; i < s_nextthread; i++)
// 	{
// 		tl = &(s_timeoutlist[i]);
// 		if(tl->pid == pid)
// 		{
// 			return &(tl->timeouts);
// 		}
// 	}

// 	// Error
// 	return NULL;
// }

/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
// TODO
/*-----------------------------------------------------------------------------------*/
/*
  Starts a new thread with priority "prio" that will begin its execution in the
  function "thread()". The "arg" argument will be passed as an argument to the
  thread() function. The id of the new thread is returned. Both the id and
  the priority are system dependent.
*/
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
xTaskHandle CreatedTask;
int result;

   if ( s_nextthread < SYS_THREAD_MAX )
   {
      result = xTaskCreate( thread, ( portCHAR * ) name, stacksize, arg, prio, &CreatedTask );

	   // For each task created, store the task handle (pid) in the timers array.
	   // This scheme doesn't allow for threads to be deleted
// 	   s_timeoutlist[s_nextthread++].pid = CreatedTask;

	   if(result == pdPASS)
	   {
		   return CreatedTask;
	   }
	   else
	   {
		   return NULL;
	   }
   }
   else
   {
      return NULL;
   }
}

/*
  This optional function does a "fast" critical region protection and returns
  the previous protection level. This function is only called during very short
  critical regions. An embedded system which supports ISR-based drivers might
  want to implement this function by disabling interrupts. Task-based systems
  might want to implement this by using a mutex or disabling tasking. This
  function should support recursive calls from the same task or interrupt. In
  other words, sys_arch_protect() could be called while already protected. In
  that case the return value indicates that it is already protected.

  sys_arch_protect() is only required if your port is supporting an operating
  system.
*/
sys_prot_t sys_arch_protect(void)
{
	vPortEnterCritical();
	return 1;
}

/*
  This optional function does a "fast" set of critical region protection to the
  value specified by pval. See the documentation for sys_arch_protect() for
  more information. This function is only required if your port is supporting
  an operating system.
*/
void sys_arch_unprotect(sys_prot_t pval)
{
	( void ) pval;
	vPortExitCritical();
}

/*
 * Prints an assertion messages and aborts execution.
 */
void sys_assert( const char *msg )
{	
	( void ) msg;
	/*FSL:only needed for debugging
	printf(msg);
	printf("\n\r");
	*/
    vPortEnterCritical(  );
    for(;;)
    ;
}

上面的移植工作有許多需要注意的地方,例如sys_sem_new函數中需要判斷如果count的值非0,需要執行一次xSemaphoreGive操作,因爲默認創建的信號量的值爲0,否則LwIP將會卡死在獲取信號量操作上面,還有大部分函數的傳入參數都是指針類型的,在函數體內的操作需要注意。然後函數sys_mbox_valid、sys_mbox_set_invalid、sys_sem_valid、sys_sem_set_invalid的具體含義並沒有搞懂具體意義,所以乾脆沒有實現,可能是需要創建一個新的信號量結構體其中包含一個原來的FreeRTOS的信號量和一個標誌位(用於表示該信號量是否Valid)。

編譯正常,會發現系統無法正常運行,會卡死在某些地方,進過都次一步步的debug發現都是在調用FreeRTOS的API的時候出現參數錯誤等問題,例如創建任務時傳入的stacksize爲0,這就會導致系統卡死,而傳入的stacksize是由TCPIP_THREAD_STACKSIZE決定的,所以需要重定義TCPIP_THREAD_STACKSIZE的值。下面是一些需要重定義的宏定義:

#define TCPIP_THREAD_STACKSIZE          512
#define TCPIP_THREAD_PRIO               1
#define TCPIP_MBOX_SIZE                 1024
#define DEFAULT_RAW_RECVMBOX_SIZE       1024
#define DEFAULT_UDP_RECVMBOX_SIZE       1024
#define DEFAULT_TCP_RECVMBOX_SIZE       1024

爲什麼重定義上面這幾個宏呢?因爲運行時需要這幾個宏,如果保持opt.h頭文件中的默認值(都是0)的話,會導致錯誤。到這裏基本上算是移植完成了,具體還有什麼漏掉了,導致有什麼BUG,後面的測試會繼續完善。下面進行測試:

void cb_tcpip_init_done(void *arg)
{
	printf("%s\r\n",__FUNCTION__);
	*(int*)arg = 0;
}

static void Task2( void *pvParameters )
{
	printf("%s\r\n",__FUNCTION__);

	ETH_BSP_Config();
	int flag = 1;
	tcpip_init(cb_tcpip_init_done,&flag);
	while(flag)
		vTaskDelay(10);
	LwIP_Init();
	
    xTaskCreate( Task_network, "Task network", 512, NULL, 2, NULL );
	
	struct netconn *p = netconn_new(NETCONN_TCP);
	printf("%.8X\r\n",p);
	struct ip_addr ipaddr;
	IP4_ADDR(&ipaddr,192,168,2,66);           //local IP地址
	netconn_bind(p,&ipaddr,5678);
	
	IP4_ADDR(&ipaddr,192,168,2,1);           //服務器IP地址
	netconn_connect(p,&ipaddr,8888);
	printf("connected\r\n");
	netconn_write(p,"asddsa",6,NETCONN_COPY);
	
	while(1)
	{
// 		printf("%s\r\n",__FUNCTION__);
		vTaskDelay(1000);
	}
}

測試結果沒問題:

思考了一個問題,上面的移植工作中,有至少兩個任務在運行,一個是負責網卡數據接收的Task_network,一個是負責Sequential API的tcpip_thread,這會不會導致線程間的不安全調用發生呢?仔細看看ethernetif_input函數,裏面有兩個地方會有線程不安全操作的嫌疑,一個是在low_level_input函數中分配內存塊的操作,一個是調用netif->input函數,這裏netif->input在初始化的時候被指向了ethernet_input函數,而ethernet_input函數在之前沒有操作系統時候使用的,那麼在使用操作系統之後,是不是還能使用這個函數呢?得不到一個直接的答案,參考網絡上大神“老衲五木”的文章中的一張圖片:

圖中說明了,網卡在收到數據的時候調用ethernetif_input函數,而ethernetif_input函數調用的是tcpip_input函數,而不是我前面設置的ethernet_input函數,於是我將我的測試程序中的網卡初始化的時候將網卡的input函數指向到tcpip_input函數,測試效果和之前一樣,那麼這個input函數的爲什麼設置成ethernet_input依然能夠在Sequential API模式下運行呢?

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章