Linux string.c memcpy等的優化

通用實現

如果在各個 arch 下有相應的實現,則會定義一個 __HAVE_ARCH_XXX 的宏,此時這裏就不會定義通用的實現版本,符號來自 相應的 arch 目錄。

// lib/string.c
...
#ifndef __HAVE_ARCH_MEMCPY
/**
 * memcpy - Copy one area of memory to another
 * @dest: Where to copy to
 * @src: Where to copy from
 * @count: The size of the area.
 *
 * You should not use this function to access IO space, use memcpy_toio()
 * or memcpy_fromio() instead.
 */
void *memcpy(void *dest, const void *src, size_t count)
{
	char *tmp = dest;
	const char *s = src;

	while (count--)
		*tmp++ = *s++;
	return dest;
}
EXPORT_SYMBOL(memcpy);
#endif
...

架構優化

實現

// arch/arm64/lib/memcpy.S
...
#include “copy_temlate.S”
...

使用

// arch/arm64/include/asm/string.h
...
#define __HAVE_ARCH_MEMCPY
extern void *memcpy(void *, const void *, __kernel_size_t);
extern void *__memcpy(void *, const void *, __kernel_size_t);
...

在各個 arch 下如果有相應的實現,則會定義一個 __HAVE_ARCH_XXX 的宏。
include/linux/string.h 中會包含該 asm/string.h,如果沒有定義優化的函數則需要聲明一下,否則無需再次聲明。

通常包含 linxu/string.h 即可使用相應的接口。

// include/linux/string.h
...
/*
 * Include machine specific inline routines
 */
#include <asm/string.h>
...
#ifndef __HAVE_ARCH_MEMCPY
extern void * memcpy(void *,const void *,__kernel_size_t);
#endif
...

copy_{to,from}_user()

  1. 爲什麼需要copy_{to,from}_user(),它究竟在背後爲我們做了什麼?
  2. copy_{to,from}_user()和memcpy()的區別是什麼,直接使用memcpy()可以嗎?
  3. memcpy()替代copy_{to,from}_user()是不是一定會有問題?

gcc common attribute

https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

__attribute__((weak))

大家日常工作中也許遇到過符號重複定義的錯誤。於是瞭解到程序中的符號定義分爲強符號(Strong Symbol)和弱符號(Weak Symbol)。對於C/C++語言來說,編譯器默認函數和初始化了的全局變量爲強符號,未初始化的全局變量爲弱符號。強符號和弱符號都是針對定義來說的,不是針對符號的引用。比如 extern int a,表示 a 是一個外部變量的引用,在該文件內沒有強弱符號的分別,其強弱由定義它的文件和下面的規則確定。

針對強弱符號的概念,鏈接器就會按如下規則處理與選擇被多次定義的全局符號:
規則1:不允許強符號被多次定義(即不同的目標文件中不能有同名的強符號);如果有多個強符號定義,則鏈接器報符號重複定義錯誤。
規則2:如果一個符號在某個目標文件中是強符號,在其他文件中都是弱符號,那麼選擇強符號。
規則3:如果一個符號在所有目標文件中都是弱符號,那麼選擇其中佔用空間最大的一個。比如目標文件A定義全局變量global爲int型,佔4個字節;目標文件B定義global爲double型,佔8個字節,那麼目標文件A和B鏈接後,符號global佔8個字節(儘量不要使用多個不同類型的弱符號,否則容易導致很難發現的程序錯誤)。

__attribute__((weakref))

目前我們所看到的對外部目標文件的符號引用在目標文件被最終鏈接成可執行文件時,它們須要被正確解析,如果沒有找到該符號的定義,鏈接器就會報符號未定義錯誤,這種被稱爲 強引用(Strong Reference)。與之相對應還有一種 弱引用 (Weak Reference),在處理弱引用時,如果該符號有定義,則鏈接器將解析該符號引用;如果該符號未被定義,則鏈接器對於該引用不報錯。鏈接器對於未定義的弱引用,不認爲它是一個錯誤。一般對於未定義的弱引用,鏈接器默認其爲0,或者是一個特殊的值,以便於程序代碼能夠識別。雖然鏈接階段不會報錯,但是如果該符號真的不存在,且程序中未做相應的處理則運行時會出錯。

這種弱符號和弱引用對於庫來說十分有用,比如庫中定義的弱符號可以被用戶定義的強符號所覆蓋,從而使得程序可以使用自定義版本的庫函數;或者程序可以對某些擴展功能模塊的引用定義爲弱引用,當我們將擴展模塊與程序鏈接在一起時,功能模塊就可以正常使用;如果去掉了某些功能模塊,那麼程序也可以正常鏈接,只是缺少了相應的功能,這使得程序的功能更加容易裁剪和組合。

weakref (“target”)
The weakref attribute marks a declaration as a weak reference.
Without arguments, it should be accompanied by an alias attribute naming the target symbol. Optionally, the target may be given as an argument to weakref itself. In either case, weakref implicitly marks the declaration as weak. Without a target, given as an argument to weakref or to alias, weakref is equivalent to weak.

static int x() __attribute__ ((weakref ("y")));
/* is equivalent to... */
static int x() __attribute__ ((weak, weakref, alias ("y")));
/* and to... */
static int x() __attribute__ ((weakref));
static int x() __attribute__ ((alias ("y")));

A weak reference is an alias that does not by itself require a definition to be given for the target symbol. If the target symbol is only referenced through weak references, then the becomes a weak undefined symbol. If it is directly referenced, however, then such strong references prevail, and a definition will be required for the symbol, not necessarily in the same translation unit.
The effect is equivalent to moving all references to the alias to a separate translation unit, renaming the alias to the aliased symbol, declaring it as weak, compiling the two separate translation units and performing a reloadable link on them.
At present, a declaration to which weakref is attached can only be static.

__attribute__((alias(“target”)))

The alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified.


void __f () { /* Do something. */; }
void f () __attribute__ ((weak, alias ("__f")));

給目標 __f 取了弱符號別名爲 f__f 必須在同一個編譯單元,否則會報錯。

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