【VxWorks5(3),字节跳动架构师讲解Golang开发

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

根据不同的编译器使用不同形式的外部函数声明。

注意观察一下发现,好像是如果是C++编译器的外部函数声明比C编译器的外部函数声明会多函数参数,这个不知道是啥原因,难道C编译器的外部函数声明不需要说明函数参数吗?

上面的推测好像是正确的,C++编译器在对函数生成符号表时,会根据函数参数和函数名一起生成符号表;而C编译器只会根据函数名来生成符号表。

因此对于C++编译器,我们最好把函数参数写出,而对于C语言编译器,我们可以不写,在单独编译包含了该头文件的源文件是,我们不需要详细说明函数参数,等到链接时再确定。

先不管,用代码测试一下

//main.c
#include <stdio.h>
#include “add.h”

int main(void) {
add(1, 3);
}

//add.h
extern int add();//或者extern int add(int a, int b);都可以通过编译

//add.c
int add(int a, int b) {
return a + b;
}

上面的C语言项目使用C编译器可以通过编译,而且是不管add.h中的函数参数可以有也可以没有都可以正常编译运行。

当我们把代码改成如下

//main.cpp
#include
#include “add.h”

using namespace std;
int main(void) {
add(1, 3);
}

//add.h
extern int add();//或者extern int add(int a, int b);都可以通过编译

//add.c
int add(int a, int b) {
return a + b;
}

因为我们使用的头文件是 iostream,所以IDE会使用C++编译器。在使用C++编译器时,该项目是不能通过编译的,提示[Error] too many arguments to function ‘int add()’,意思是你声明的时候没有参数,现在又有了两个参数,因此报错了。

这就是因为C++编译器对函数名的处理了,在main.cpp文件中,我们通过头文件包含的add函数没有参数,因此C++编译器将其处理成符号名 add ,而在add.c 文件中,add函数是有参数的,因此C++编译器会把add函数处理成符号为 add_int_int之类的符号名,当然在他们单独编译时是可能正常的,但到链接时,add函数的两个符号名不能匹配起来,因此会造成错误;而C编译器可以正常处理的原因就是,在C编译器下,函数的符号名只和函数名有关,和函数参数无关。

意外之喜,没想到刚开始看源码,就收获了知识的理解。


头文件内容总结

整个头文件的内容就是这些了,最有意思的应该就是

typedef struct /* MSG_Q_INFO /
{
int numMsgs; /
OUT: number of messages queued /
int numTasks; /
OUT: number of tasks waiting on msg q */

int sendTimeouts; /* OUT: count of send timeouts /
int recvTimeouts; /
OUT: count of receive timeouts */

int options; /* OUT: options with which msg q was created /
int maxMsgs; /
OUT: max messages that can be queued /
int maxMsgLength; /
OUT: max byte length of each message */

int taskIdListMax; /* IN: max tasks to fill in taskIdList /
int * taskIdList; /
PTR: array of task ids waiting on msg q */

int msgListMax; /* IN: max msgs to fill in msg lists /
char ** msgPtrList; /
PTR: array of msg ptrs queued to msg q /
int * msgLenList; /
PTR: array of lengths of msgs */

} MSG_Q_INFO;

看含义应该是消息队列信息结构体类型的定义

里面有各种含义的整型,有数组指针,有二维指针,通过这些数据结构的特性来相应的存储消息队列的各种信息。

int * taskIdList; /* PTR: array of task ids waiting on msg q */任务ID数组

char ** msgPtrList; /* PTR: array of msg ptrs queued to msg q */数组,数组存储的每个元素是消息的字符指针

int * msgLenList; /* PTR: array of lengths of msgs */数组,每个消息的长度


下面就是找来msgQlib.c文件来看看吧

首先了解一下VxWorks中常见的宏定义

#define BOOL int
#define LOCAL static
#define IMPORT extern
#define ERROR -1
#define NULL 0
#define TRUE 1
#define FALSE 0

#define FAST register
#define IMPORT extern
#define LOCAL static

typedef char INT8;
typedef short INT16;
typedef int INT32;
typedef long long INT64;

typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef unsigned long long UINT64;

typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned int UINT;
typedef unsigned long ULONG;

typedef int BOOL;
typedef int STATUS;
typedef int ARGINT;

typedef void VOID;

#ifdef __cplusplus
typedef int (FUNCPTR) (…); / ptr to function returning int */
typedef void (VOIDFUNCPTR) (…); / ptr to function returning void /
typedef double (DBLFUNCPTR) (…); / ptr to function returning double
/
typedef float (FLTFUNCPTR) (…); / ptr to function returning float */
#else
typedef int (FUNCPTR) (); / ptr to function returning int */
typedef void (VOIDFUNCPTR) (); / ptr to function returning void /
typedef double (DBLFUNCPTR) (); / ptr to function returning double
/
typedef float (FLTFUNCPTR) (); / ptr to function returning float /
#endif /
_cplusplus */

可以看出VxWorks中的宏定义还是很不错的,清晰好用。

下m面先看msgCreate函数

/*******************************************************************************
*

  • msgQCreate - create and initialize a message queue
  • This routine creates a message queue capable of holding up to
  • messages, each up to bytes long. The routine returns
  • a message queue ID used to identify the created message queue in all
  • subsequent calls to routines in this library. The queue can be created
  • with the following options:
  • .iP “MSG_Q_FIFO (0x00)” 8
  • queue pended tasks in FIFO order.
  • .iP “MSG_Q_PRIORITY (0x01)”
  • queue pended tasks in priority order.
  • .iP “MSG_Q_EVENTSEND_ERR_NOTIFY (0x02)”
  • When a message is sent, if a task is registered for events and the
  • actual sending of events fails, a value of ERROR is returned and the errno
  • is set accordingly. This option is off by default.
  • .LP
  • RETURNS:
  • MSG_Q_ID, or NULL if error.
  • ERRNO: S_memLib_NOT_ENOUGH_MEMORY, S_intLib_NOT_ISR_CALLABLE
  • SEE ALSO: msgQSmLib
    */

MSG_Q_ID msgQCreate
(
int maxMsgs, /* max messages that can be queued /
int maxMsgLength, /
max bytes in a message /
int options /
message queue options /
)
{
MSG_Q_ID msgQId;
void * pPool; /
pointer to memory for messages */
UINT size = (UINT) maxMsgs * MSG_NODE_SIZE (maxMsgLength);

if (INT_RESTRICT () != OK) /* restrict ISR from calling */
return (NULL);

if ((!msgQLibInstalled) && (msgQLibInit () != OK))
return (NULL); /* package init problem */

if ((msgQId = (MSG_Q_ID)objAllocExtra (msgQClassId, size, &pPool)) == NULL)
return (NULL);

if (msgQInit (msgQId, maxMsgs, maxMsgLength, options, pPool) != OK)
{
objFree (msgQClassId, (char *) msgQId);
return (NULL);
}

#ifdef WV_INSTRUMENTATION
/* windview - level 1 event logging routine */
EVT_OBJ_4 (OBJ, msgQId, msgQClassId, EVENT_MSGQCREATE,
msgQId, maxMsgs, maxMsgLength, options);
#endif

return ((MSG_Q_ID) msgQId);
}

先看一下MSG_Q_ID的结构体定义

typedef struct msg_q MSG_Q_ID; / message queue ID /
typedef struct msg_q /
MSG_Q /
{
OBJ_CORE objCore; /
object management /
Q_JOB_HEAD msgQ; /
message queue head /
Q_JOB_HEAD freeQ; /
free message queue head /
int options; /
message queue options /
int maxMsgs; /
max number of messages in queue /
int maxMsgLength; /
max length of message /
int sendTimeouts; /
number of send timeouts /
int recvTimeouts; /
number of receive timeouts /
EVENTS_RSRC events; /
VxWorks events /
} MSG_Q;
typedef struct obj_core /
OBJ_CORE */
{
struct obj_class pObjClass; / pointer to object’s class /
} OBJ_CORE;
typedef struct /
Head of job queue */
{
Q_JOB_NODE first; / first node in queue */
Q_JOB_NODE last; / last node in queue /
int count; /
number of nodes in queue */
Q_CLASS pQClass; / must be 4th long word /
Q_HEAD pendQ; /
queue of blocked tasks /
} Q_JOB_HEAD;
typedef struct qJobNode /
Node of a job queue */
{
struct qJobNode *next;
} Q_JOB_NODE;

可以看到MSG_Q是结构体的名称,MSG_Q_ID是结构体的指针(指针也可以看成是结构体的ID)。

msgQCreate函数其实最重要的是调用了msgQInit函数,msgQInit函数的代码如下

/*******************************************************************************
*

  • msgQInit - initialize a message queue

  • This routine initializes a message queue data structure. Like msgQCreate()

  • the resulting message queue is capable of holding up to messages,

  • each of up to bytes long.

  • However, instead of dynamically allocating the MSG_Q data structure,

  • this routine takes a pointer to the MSG_Q data structure to be

  • initialized, and a pointer to the buffer to be use to hold

  • queued messages. must point to a 4 byte aligned buffer

  • that is ( * MSG_NODE_SIZE ()).

  • The queue can be created with the following options:

  • MSG_Q_FIFO queue pended tasks in FIFO order

  • MSG_Q_PRIORITY queue pended tasks in priority order

  • RETURNS: OK or ERROR.

  • ERRNO: S_msgQLib_INVALID_QUEUE_TYPE

  • SEE ALSO: msgQCreate()

  • NOMANUAL
    */

STATUS msgQInit
(
FAST MSG_Q pMsgQ, / pointer to msg queue to initialize /
int maxMsgs, /
max messages that can be queued /
int maxMsgLength, /
max bytes in a message /
int options, /
message queue options /
void * pMsgPool /
pointer to memory for messages */
)
{
FAST int nodeSize = MSG_NODE_SIZE (maxMsgLength);
FAST int ix;
FAST Q_CLASS_ID msgQType;

if ((!msgQLibInstalled) && (msgQLibInit () != OK))
return (ERROR); /* package init problem */

bzero ((char *) pMsgQ, sizeof (pMsgQ)); / clear out msg q structure */

/* initialize internal job queues */

switch (options & MSG_Q_TYPE_MASK)
{
case MSG_Q_FIFO: msgQType = Q_FIFO; break;
case MSG_Q_PRIORITY: msgQType = Q_PRI_LIST; break;

default:
errnoSet (S_msgQLib_INVALID_QUEUE_TYPE);
return (ERROR);
}

if ((qInit ((Q_HEAD *) &pMsgQ->msgQ, qJobClassId, msgQType,
0, 0, 0, 0, 0, 0, 0, 0, 0) != OK) ||
(qInit ((Q_HEAD *) &pMsgQ->freeQ, qJobClassId, msgQType,
0, 0, 0, 0, 0, 0, 0, 0, 0) != OK))
return (ERROR);

/* put msg nodes on free list */

for (ix = 0; ix < maxMsgs; ix++)
{
qJobPut (pMsgQ, &pMsgQ->freeQ, (Q_JOB_NODE *) pMsgPool,
Q_JOB_PRI_DONT_CARE);
pMsgPool = (void *) (((char *) pMsgPool) + nodeSize);
}

/* initialize rest of msg q */

pMsgQ->options = options;
pMsgQ->maxMsgs = maxMsgs;
pMsgQ->maxMsgLength = maxMsgLength;

eventInit (&pMsgQ->events);

#ifdef WV_INSTRUMENTATION
if (wvObjIsEnabled)
{
/* windview - connect level 1 event logging routine */
objCoreInit (&pMsgQ->objCore, msgQInstClassId);
}
else
#endif
objCoreInit (&pMsgQ->objCore, msgQClassId);

return (OK);
}

注意一些bzero函数

void bzero
(
char buffer, / buffer to be zeroed /
int nbytes /
number of bytes in buffer */
)
{
bfill (buffer, nbytes, 0);
}

bfill函数

/*******************************************************************************
*

  • bfill - fill a buffer with a specified character
  • This routine fills the first characters of a buffer with the
  • character . Filling is done in the most efficient way possible,
  • which may be long-word, or even multiple-long-word stores, on some
  • architectures. In general, the fill will be significantly faster if
  • the buffer is long-word aligned. (For filling that is restricted to
  • byte stores, see the manual entry for bfillBytes().)
  • RETURNS: N/A
  • SEE ALSO: bfillBytes()
    */

void bfill
(
FAST char buf, / pointer to buffer /
int nbytes, /
number of bytes to fill /
FAST int ch /
char with which to fill buffer */
)
{
#if (CPU_FAMILY != I960) || !defined(GNUC) || defined(VX_IGNORE_GNU_LIBS)
FAST long *pBuf;
char *bufend = buf + nbytes;
FAST char *buftmp;
FAST long val;

if (nbytes < 10)
goto byte_fill;

val = (ch << 24) | (ch << 16) | (ch << 8) | ch;

/* start on necessary alignment */

while ((int)buf & ALIGNMENT)
*buf++ = ch;

buftmp = bufend - sizeof (long); /* last word boundary before bufend */

pBuf = (long *)buf;

/* fill 4 bytes at a time; don’t exceed buf endpoint */

do
{
*pBuf++ = val;
}
while ((char *)pBuf < buftmp);

buf = (char *)pBuf - sizeof (long);

/* fill remaining bytes one at a time */

byte_fill:
while (buf < bufend)
buf++ = ch;
#else /
IGNORE GNU LIBS */
(void) memset ((void )buf, (int) ch, (size_t) nbytes);
#endif /
IGNORE GNU LIBS */
}

可以看到会根据预编译选择自己实现memset或者直接调用memset函数。


下面看看msgQSend函数

/*******************************************************************************
*

  • msgQSend - send a message to a message queue
  • This routine sends the message in of length to the message
  • queue . If any tasks are already waiting to receive messages
  • on the queue, the message will immediately be delivered to the first
  • waiting task. If no task is waiting to receive messages, the message
  • is saved in the message queue and if a task has previously registered to
  • receive events from the message queue, these events are sent in the context
  • of this call. This may result in the unpending of the task waiting for
  • the events. If the message queue fails to send events and if it was
  • created using the MSG_Q_EVENTSEND_ERR_NOTIFY option, ERROR is returned
  • even though the send operation was successful.
  • The parameter specifies the number of ticks to wait for free
  • space if the message queue is full. The parameter can also have
  • the following special values:
  • .iP “NO_WAIT (0)” 8
  • return immediately, even if the message has not been sent.
  • .iP “WAIT_FOREVER (-1)”
  • never time out.
  • .LP
  • The parameter specifies the priority of the message being sent.
  • The possible values are:
  • .iP “MSG_PRI_NORMAL (0)” 8
  • normal priority; add the message to the tail of the list of queued
  • messages.
  • .iP “MSG_PRI_URGENT (1)”
  • urgent priority; add the message to the head of the list of queued messages.
  • .LP
  • USE BY INTERRUPT SERVICE ROUTINES
  • This routine can be called by interrupt service routines as well as
  • by tasks. This is one of the primary means of communication
  • between an interrupt service routine and a task. When called from an
  • interrupt service routine, must be NO_WAIT.
  • RETURNS: OK on success or ERROR otherwise.
  • ERRNO:
  • .iP “S_distLib_NOT_INITIALIZED”
  • Distributed objects message queue library (VxFusion) not initialized.
  • .iP “S_smObjLib_NOT_INITIALIZED”
  • Shared memory message queue library (VxMP Option) not initialized.
  • .iP “S_objLib_OBJ_ID_ERROR”
  • Invalid message queue ID.
  • .iP “S_objLib_OBJ_DELETED”
  • Message queue deleted while calling task was pended.
  • .iP “S_objLib_OBJ_UNAVAILABLE”
  • No free buffer space when NO_WAIT timeout specified.
  • .iP “S_objLib_OBJ_TIMEOUT”
  • Timeout occurred while waiting for buffer space.
  • .iP “S_msgQLib_INVALID_MSG_LENGTH”
  • Message length exceeds limit.
  • .iP “S_msgQLib_NON_ZERO_TIMEOUT_AT_INT_LEVEL”
  • Called from ISR with non-zero timeout.
  • .iP “S_eventLib_EVENTSEND_FAILED”
  • Message queue failed to send events to registered task. This errno
  • value can only exist if the message queue was created with the
  • MSG_Q_EVENTSEND_ERR_NOTIFY option.
  • .LP
  • SEE ALSO: msgQSmLib, msgQEvStart
    */

STATUS msgQSend
(
FAST MSG_Q_ID msgQId, /* message queue on which to send /
char * buffer, /
message to send /
FAST UINT nBytes, /
length of message /
int timeout, /
ticks to wait /
int priority /
MSG_PRI_NORMAL or MSG_PRI_URGENT */
)
{
FAST MSG_NODE * pMsg;

if (ID_IS_SHARED (msgQId)) /* message Q is shared? /
{
if (ID_IS_DISTRIBUTED (msgQId)) /
message queue is distributed? */
{
if (msgQDistSendRtn == NULL)
{
errno = S_distLib_NOT_INITIALIZED;
return (ERROR);
}
return ((*msgQDistSendRtn) (msgQId, buffer, nBytes,
timeout, WAIT_FOREVER, priority));
}

if (msgQSmSendRtn == NULL)
{
errno = S_smObjLib_NOT_INITIALIZED;
return (ERROR);
}

return ((*msgQSmSendRtn) (SM_OBJ_ID_TO_ADRS (msgQId), buffer, nBytes,
timeout, priority));
}

/* message queue is local */

if (!INT_CONTEXT ())
TASK_LOCK ();
else
{
if (timeout != 0)
{
errnoSet (S_msgQLib_NON_ZERO_TIMEOUT_AT_INT_LEVEL);
return (ERROR);
}
}

restart:
if (OBJ_VERIFY (msgQId, msgQClassId) != OK)
{
if (!INT_CONTEXT ())
TASK_UNLOCK ();
return (ERROR);
}

#ifdef WV_INSTRUMENTATION
/* windview - level 1 event logging routine */
EVT_OBJ_5 (OBJ, msgQId, msgQClassId, EVENT_MSGQSEND, msgQId,
buffer, nBytes, timeout, priority);
#endif

if (nBytes > msgQId->maxMsgLength)
{
if (!INT_CONTEXT ())
TASK_UNLOCK ();
errnoSet (S_msgQLib_INVALID_MSG_LENGTH);
return (ERROR);
}

pMsg = (MSG_NODE *) qJobGet (msgQId, &msgQId->freeQ, timeout);

if (pMsg == (MSG_NODE *) NONE)
{
timeout = SIG_TIMEOUT_RECALC(timeout);
goto restart;
}

if (pMsg == NULL)
{
#if FALSE
msgQId->sendTimeouts++;
#else

/*

  • The timeout stat should only be updated if a timeout has occured.
  • An OBJ_VERIFY needs to be performed to catch the case where a
  • timeout indeed occured, but the message queue is subsequently
  • deleted before the current task is rescheduled.
    */

if (errnoGet() == S_objLib_OBJ_TIMEOUT)
{
if (OBJ_VERIFY (msgQId, msgQClassId) == OK)
msgQId->sendTimeouts++;
else
errnoSet(S_objLib_OBJ_DELETED);
}

#endif /* FALSE */

if (!INT_CONTEXT ())
TASK_UNLOCK ();
return (ERROR);
}

pMsg->msgLength = nBytes;
bcopy (buffer, MSG_NODE_DATA (pMsg), (int) nBytes);

if (qJobPut (msgQId, &msgQId->msgQ, &pMsg->node, priority) != OK)
{
if (!INT_CONTEXT ())
TASK_UNLOCK ();

return (ERROR); /* errno set by qJobPut() */
}

if (!INT_CONTEXT ())
TASK_UNLOCK ();

return (OK);
}

看一下里面的bcopy函数

/*******************************************************************************
*

  • bcopy - copy one buffer to another
  • This routine copies the first characters from to
  • . Overlapping buffers are handled correctly. Copying is done
  • in the most efficient way possible, which may include long-word, or even
  • multiple-long-word moves on some architectures. In general, the copy
  • will be significantly faster if both buffers are long-word aligned.
  • (For copying that is restricted to byte, word, or long-word moves, see
  • the manual entries for bcopyBytes(), bcopyWords(), and bcopyLongs().)
  • RETURNS: N/A
  • SEE ALSO: bcopyBytes(), bcopyWords(), bcopyLongs()
    */

void bcopy
(
const char source, / pointer to source buffer */
char destination, / pointer to destination buffer /
int nbytes /
number of bytes to copy */
)
{
FAST char *dstend;
FAST long *src;
FAST long *dst;
int tmp = destination - source;

if (tmp <= 0 || tmp >= nbytes)
{
/* forward copy */

dstend = destination + nbytes;

/* do byte copy if less than ten or alignment mismatch */

if (nbytes < 10 || (((int)destination ^ (int)source) & ALIGNMENT))
goto byte_copy_fwd;

/* if odd-aligned copy byte */

while ((int)destination & ALIGNMENT)
*destination++ = *source++;

src = (long *) source;
dst = (long *) destination;

do
{
*dst++ = *src++;
}
while (((char *)dst + sizeof (long)) <= dstend);

destination = (char *)dst;
source = (char *)src;

byte_copy_fwd:
while (destination < dstend)
*destination++ = source++;
}
else
{
/
backward copy */

dstend = destination;
destination += nbytes - sizeof (char);
source += nbytes - sizeof (char);

/* do byte copy if less than ten or alignment mismatch */

if (nbytes < 10 || (((int)destination ^ (int)source) & ALIGNMENT))
goto byte_copy_bwd;

/* if odd-aligned copy byte */

while ((int)destination & ALIGNMENT)
*destination-- = *source–;

src = (long *) source;
dst = (long *) destination;

do
{
*dst-- = *src–;
}
while (((char *)dst + sizeof(long)) >= dstend);

destination = (char *)dst + sizeof (long);
source = (char *)src + sizeof (long);

byte_copy_bwd:
while (destination >= dstend)
*destination-- = source–;
}
}
#endif /
IGNORE GNU LIBS */

再看一下msgQReceive

/*******************************************************************************
*

  • msgQReceive - receive a message from a message queue
  • This routine receives a message from the message queue .
  • The received message is copied into the specified , which is
  • in length. If the message is longer than ,
  • the remainder of the message is discarded (no error indication
  • is returned).
  • The parameter specifies the number of ticks to wait for
  • a message to be sent to the queue, if no message is available when
  • msgQReceive() is called. The parameter can also have
  • the following special values:
  • .iP “NO_WAIT (0)” 8
  • return immediately, whether a message has been received or not.
  • .iP “WAIT_FOREVER (-1)”
  • never time out.
  • .LP
  • WARNING: This routine must not be called by interrupt service routines.
  • RETURNS:
  • The number of bytes copied to , or ERROR.
  • ERRNO: S_distLib_NOT_INITIALIZED, S_smObjLib_NOT_INITIALIZED,
  •    S_objLib_OBJ_ID_ERROR, S_objLib_OBJ_DELETED,
    
  •    S_objLib_OBJ_UNAVAILABLE, S_objLib_OBJ_TIMEOUT,
    
  •    S_msgQLib_INVALID_MSG_LENGTH, S_intLib_NOT_ISR_CALLABLE
    
  • SEE ALSO: msgQSmLib
    */

int msgQReceive
(
FAST MSG_Q_ID msgQId, /* message queue from which to receive /
char * buffer, /
buffer to receive message /
UINT maxNBytes, /
length of buffer /
int timeout /
ticks to wait */
)
{
FAST MSG_NODE * pMsg;
FAST int bytesReturned;

if (INT_RESTRICT() != OK) /* errno set by INT_RESTRICT() */
return ERROR;

if (ID_IS_SHARED (msgQId)) /* message Q is shared? /
{
if (ID_IS_DISTRIBUTED (msgQId)) /
message queue is distributed? */
{
if (msgQDistReceiveRtn == NULL)
{

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
*

  • SEE ALSO: msgQSmLib
    */

int msgQReceive
(
FAST MSG_Q_ID msgQId, /* message queue from which to receive /
char * buffer, /
buffer to receive message /
UINT maxNBytes, /
length of buffer /
int timeout /
ticks to wait */
)
{
FAST MSG_NODE * pMsg;
FAST int bytesReturned;

if (INT_RESTRICT() != OK) /* errno set by INT_RESTRICT() */
return ERROR;

if (ID_IS_SHARED (msgQId)) /* message Q is shared? /
{
if (ID_IS_DISTRIBUTED (msgQId)) /
message queue is distributed? */
{
if (msgQDistReceiveRtn == NULL)
{

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-SZFDzf86-1713177729852)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值