在rtthread中,自带了内存设置函数rt_memset,其目的是将某段内存的内容设置为指定的数值。该函数接受三个参数:源内存地址s,要设置的值c,以及要设置的长度count。在函数中,首先判断了是否使用了RT_USING_TINY_SIZE宏,如果使用了,则通过循环逐个设置内存的值,然后返回源内存地址。
如果没有使用RT_USING_TINY_SIZE宏,则会根据内存对齐情况和设置的值大小进行不同的处理。如果设置的长度大于等于一个块的大小并且内存地址是块对齐的,就会使用较高效的方式进行设置,否则会通过逐个字节设置的方式来完成设置操作。
下面是这个函数的具体内容:
/**
* This function will set the content of memory to specified value
*
* @param s the address of source memory
* @param c the value shall be set in content
* @param count the copied length
*
* @return the address of source memory
*/
void *rt_memset(void *s, int c, rt_ubase_t count)
{
#ifdef RT_USING_TINY_SIZE
char *xs = (char *)s;
while (count--)
*xs++ = c;
return s;
#else
#define LBLOCKSIZE (sizeof(rt_int32_t))
#define UNALIGNED(X) ((rt_int32_t)X & (LBLOCKSIZE - 1))
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
int i;
char *m = (char *)s;
rt_uint32_t buffer;
rt_uint32_t *aligned_addr;
rt_uint32_t d = c & 0xff;
if (!TOO_SMALL(count) && !UNALIGNED(s))
{
/* If we get this far, we know that n is large and m is word-aligned. */
aligned_addr = (rt_uint32_t *)s;
/* Store D into each char sized location in BUFFER so that
* we can set large blocks quickly.
*/
if (LBLOCKSIZE == 4)
{
buffer = (d << 8) | d;
buffer |= (buffer << 16);
}
else
{
buffer = 0;
for (i = 0; i < LBLOCKSIZE; i ++)
buffer = (buffer << 8) | d;
}
while (count >= LBLOCKSIZE * 4)
{
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
count -= 4 * LBLOCKSIZE;
}
while (count >= LBLOCKSIZE)
{
*aligned_addr++ = buffer;
count -= LBLOCKSIZE;
}
/* Pick up the remainder with a bytewise loop. */
m = (char *)aligned_addr;
}
while (count--)
{
*m++ = (char)d;
}
return s;
#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
#endif
}
在这个函数中,先判断有没有使用RT_USING_TINY_SIZE宏。如果使用了这个宏,那么就按着一个一个的字节来进行内存的内容设置。
如果使用了这个宏,先进行了三个宏定义:
#define LBLOCKSIZE (sizeof(rt_int32_t))
#define UNALIGNED(X) ((rt_int32_t)X & (LBLOCKSIZE - 1))
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
第一个宏定义是用于确定块的大小,为什么要使用 LBLOCKSIZE
?
- 提高效率 :处理内存时,如果内存是对齐的,并且可以一次操作多个字节,那么操作的效率会更高。通过一个操作一个块(例如一个块有四个字节)的方式,可以减少循环次数,从而提高性能。
- 内存对齐 :某些硬件平台对未对齐的内存访问会有性能损失,甚至可能导致硬件异常。使用对齐的块大小可以避免这些问题。
第二个宏定义用于检查地址X是否对齐,如果没有对齐,那么结果就是非零也就是真。
第三个宏定义用来判断长度LEN是不是小于块的大小,如果小于那么结果就是真。
接着定义了一个用于for循环的变量i,一个游标指针,一个4字节的缓存变量buffer,一个对齐的地址aligned_addr,最后还定义了一个变量d,用于取下参数c的低八位(因为这里是要对每一个字节,也就是八个比特进行内存设置)。
然后我们进行了一个if判断,进入到这个if判断的条件是被操作的内存足够大并且已经进行了内存对齐。从注释中我们也可以得知进入到if判断的条件:
/* If we get this far, we know that n is large and m is word-aligned. */
下面开始设置内存块的内容。这里分为了两种情况,如果内存对齐的长度为4个字节,经过两次(1 * 2 = 2 2 * 2 = 4)就可以得到一个内存被设置为目标值的内存块。
如果内存对齐长度不为4,只能采用for循环的方式去设置内存块的内容(1 + 1 = 2 2 + 1 =3)。
这样做的目的是为了提高某些情况下的性能,第一种方式完成长度为4的内存块内容的设置只需要进行2次操作,而在第二种方法下进行了3次。随着内存块长度的不多增长,性能差异会更加明显。
举个例子(内存对齐长度为4),如果传入的参数c的值为542 <=> 0010 0001 1110,那么经过rt_uint32_t d = c & 0xff; 这句代码的处理,d = 0001 1110,接着经过两步;
buffer = (d << 8) | d;
buffer |= (buffer << 16);
第一步buffer = 0001 1110 0000 0000 | 0001 1110 = 0001 1110 0001 1110
第二部buffer = 0001 1110 0001 1110 0001 1110 0001 1110
这样下来会得到LBLOCKSIZE长度的目标内存块。
接着的while循环开始对目标地址进行内存设置。但是仍然会有剩余的不足一个LBLOCKSIZE大小的内存没有被设置,所以就需要按字节而不是按块进行内存设置。
最后返回了一个指向内存段开头的指针并取消了开头的三个宏定义:
#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
最后我想了一个问题,为什么这样的函数要有返回值,chatgpt给出的解释是这样的: