當(dāng)前位置:首頁 > 公眾號(hào)精選 > 嵌入式客棧
[導(dǎo)讀]關(guān)注、星標(biāo)嵌入式客棧,干貨及時(shí)送達(dá) [導(dǎo)讀] Linux內(nèi)核代碼龐大,閱讀內(nèi)核書籍總覺得云山霧繞,紙上得來終覺淺,希望通過閱讀代碼撰寫筆記,嘗試將這美人神秘的面紗掀開一角,管中窺豹,見一點(diǎn)真容。水平所限,錯(cuò)誤難免,懇請(qǐng)交流指正。 前情提要 《閱讀內(nèi)核

關(guān)注、星標(biāo)嵌入式客棧,干貨及時(shí)送達(dá)

[導(dǎo)讀] Linux內(nèi)核代碼龐大,閱讀內(nèi)核書籍總覺得云山霧繞,紙上得來終覺淺,希望通過閱讀代碼撰寫筆記,嘗試將這美人神秘的面紗掀開一角,管中窺豹,見一點(diǎn)真容。水平所限,錯(cuò)誤難免,懇請(qǐng)交流指正。

前情提要

《閱讀內(nèi)核系列之EXPORT_SYMBOL展開》將EXPORT_SYMBOL(schedule)展開:

asmlinkage __visible void __sched schedule(void)
{
 struct task_struct *tsk = current;
    
 sched_submit_work(tsk);
 do {
  preempt_disable();
  __schedule(false);
  sched_preempt_enable_no_resched();
 } while (need_resched());
}
EXPORT_SYMBOL(schedule);

全部展開后,得到了什么呢(前文中__EXPORT_SYMBOL(sym, sec)  sec弄錯(cuò)了,修正如下)?

    extern typeof(schedule) schedule;                                    \
    extern __visible void *__crc_schedule __attribute__((weak));         \
    static const unsigned long __kcrctab_schedule                        \
    __used                                                               \
    __attribute__((section("___kcrctab" "" "+" "schedule"), unused))     \
    = (unsigned long) &__crc_schedule;      
   static const char __kstrtab_schedule[]                                \
    __attribute__((section("__ksymtab_strings"), aligned(1)))            \
    = "_" "schedule";                                                    \
    extern const struct kernel_symbol __ksymtab_schedule;                \
    __visible const struct kernel_symbol __ksymtab_schedule              \
    __used                                                               \
    __attribute__((section("___ksymtab" "" "+" "schedule"), unused))    \
    = {
 (unsigned long)&schedule, __kstrtab_schedule };    

這樣還是不直觀,去掉不必要的換行符,整理一下:

asmlinkage __visible void __sched schedule(void)
{
 struct task_struct *tsk = current;
    
 sched_submit_work(tsk);
 do {
  preempt_disable();
  __schedule(false);
  sched_preempt_enable_no_resched();
 } while (need_resched());

/*以下部分都屬于EXPORT_SYMBOL(schedule)的展開*/
extern typeof(schedule) schedule;
extern __visible void *__crc_schedule __attribute__((weak));

static const unsigned long __kcrctab_schedule __used                 \
__attribute__((section("___kcrctab+schedule"), unused))    \
= (unsigned long) &__crc_schedule;     

static const char __kstrtab_schedule[] __attribute__((section("__ksymtab_strings"), aligned(1)))  = "_" "schedule";                                     

extern const struct kernel_symbol __ksymtab_schedule; 

__visible const struct kernel_symbol __ksymtab_schedule __used __attribute__((section("___ksymtab" "" "+" "schedule"), unused)) = { 
    (unsigned long)&schedule, __kstrtab_schedule 
}; 

gcc相關(guān)知識(shí)點(diǎn)梳理

要理解上述代碼,感覺還是很難,先來梳理一下其中一些關(guān)鍵字,好多沒見過?憋急。

  • asmlinkage,其一:用于指定函數(shù)的參數(shù)都棧中,而不應(yīng)在寄存器中;其二,指定一個(gè)函數(shù)為asmlinkage,則匯編代碼中可以調(diào)用該函數(shù)。參考https://kernelnewbies.org/FAQ/asmlinkage

  • 閱讀Linux內(nèi)核代碼,發(fā)現(xiàn)大量的文件名同名,如不理清其內(nèi)在機(jī)理,這很讓人頭腦發(fā)脹。這里以linkage.h為例來探討一下。

    看到有博文說asmlinkage其根源如下:

    #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

    但仔細(xì)查看代碼,這僅僅是對(duì)x86體系而言,而比如針對(duì)IA64(英特爾安騰架構(gòu)(Intel Itanium architecture))而言:

    #define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))

    所以不同的體系結(jié)構(gòu)為實(shí)現(xiàn)前述目的是有差異的。

  • __attribute__  ,關(guān)鍵字__attribute__用來指定變量,函數(shù)參數(shù)或結(jié)構(gòu),聯(lián)合以及在C ++中的類成員的特殊屬性。__attribute__關(guān)鍵字后跟一個(gè)用雙括號(hào)括起來的屬性規(guī)范。當(dāng)前通常為變量定義一些屬性。為特定目標(biāo)系統(tǒng)上的變量定義了其他屬性。其他屬性可用于函數(shù)(請(qǐng)參見“函數(shù)屬性”),標(biāo)簽(請(qǐng)參見“標(biāo)簽屬性”),枚舉(請(qǐng)參見“枚舉器屬性”),語句(請(qǐng)參見“語句屬性”)和類型(請(qǐng)參見“類型屬性”)。有需要的時(shí)候可以去查閱gcc文檔:       https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html

  • typeof,是gcc的擴(kuò)展關(guān)鍵字。參考gcc 9.3.0手冊(cè) P475:

    引用表達(dá)式類型的另一種方法是使用typeof。其語法看起來像sizeof,但是該構(gòu)造在語義上類似于使用typedef定義的類型名稱。有兩種方式將參數(shù)寫入typeof:使用表達(dá)式或類型。

    很可能沒見過這種特性,那有啥妙用呢?

    #define SWAP(a, b)  {\
          typeof(a) _t=a;\
          a=b;\
          b=_t;}

    寫的這么復(fù)雜干啥呢?其一、用大括號(hào)括起來定義的_b作用域限定了,不會(huì)有重名的問題,其二、利用typeof(a) _t=a,則可以獲取a的類型,具有普適性。所謂普適性,即便對(duì)這個(gè)宏傳入兩個(gè)結(jié)構(gòu)體也是運(yùn)行的。如果不這么做,用函數(shù)實(shí)現(xiàn)需要做到普適性則比較麻煩,如果一定要做肯定也有辦法,比如swap(void *a, void * b,int length),直接交換內(nèi)存。但遠(yuǎn)不如這個(gè)宏來的簡(jiǎn)單。

    • 表達(dá)式的示例:typeof(x [0](1)) 假設(shè)x是一個(gè)指向函數(shù)的指針數(shù)組;則上述語句描述的類型是函數(shù)值的類型。

    • 類型名示例

      typeof (int *)

      這里描述的類型是指向int的指針類型。

  • __visible ,這在哪里實(shí)現(xiàn)的呢?這是將gcc的__externally_visible__屬性利用宏轉(zhuǎn)定義了,以增加可讀性。用于聲明全局可見。該宏定義位于:

       ./include/linux/compiler_attributes.h中

#if __has_attribute(__externally_visible__)
#define __visible __attribute__((__externally_visible__))
#else
#define __visible
#endif
  • __sched,這個(gè)咋一看,也是一頭霧水。找到出處:./include/sched/debug.h

    /* 聲明存儲(chǔ)位置在.sched.text中. */
    #define __sched __attribute__((__section__(".sched.text")))

    類似地,還有

    #define __init_thread_info __attribute__((__section__(".data..init_thread_info")))
  • weak,若兩個(gè)或兩個(gè)以上全局符號(hào)(函數(shù)或變量名)名字一樣,而其中之一聲明為weak symbol(弱符號(hào)),則這些全局符號(hào)不會(huì)引發(fā)重定義錯(cuò)誤。鏈接器會(huì)忽略弱符號(hào),去使用普通的全局符號(hào)來解析所有對(duì)這些符號(hào)的引用,但當(dāng)普通的全局符號(hào)不可用時(shí),鏈接器會(huì)使用弱符號(hào)。當(dāng)有函數(shù)或變量名可能被用戶覆蓋時(shí),該函數(shù)或變量名可以聲明為一個(gè)弱符號(hào)。當(dāng)weak和alias屬性連用時(shí),還可以聲明弱別名。

  • unused,附加到函數(shù)的此屬性意味著如果該函數(shù)未被使用。GCC不會(huì)對(duì)此功能發(fā)出警告。

  • 兩個(gè)以雙引號(hào)的字符串,編譯預(yù)處理時(shí),會(huì)自動(dòng)連接為一個(gè)字符串

    "_" "schedule" 變成 “_schedule”

  • __used, __unused__屬性,在./include/compiler.h定義

    #define __used   __attribute__((__used__))

    該屬性附加在函數(shù)上,表示即使未引用該函數(shù),也必須將該函數(shù)鏈接在目標(biāo)文件中。

再看EXPORT_SYMBOL(schedule)展式

好了,前面的都整明白了,再來看前面的那段代碼:

asmlinkage __visible void __sched schedule(void)
{
 struct task_struct *tsk = current;
    
 sched_submit_work(tsk);
 do {
  preempt_disable();
  __schedule(false);
  sched_preempt_enable_no_resched();
 } while (need_resched());


/*以下部分都屬于EXPORT_SYMBOL(schedule)的展開*/
/*利用typeof全局聲明schedule函數(shù)*/
extern typeof(schedule) schedule;
/*全局聲明__crc_schedule,并聲明為weak屬性*/
extern __visible void *__crc_schedule __attribute__((weak));

/*局部const定義__crc_schedule,指定存儲(chǔ)位置*/
static const unsigned long __kcrctab_schedule __used                 \
__attribute__((section("___kcrctab + schedule"), unused))    \
= (unsigned long) &__crc_schedule;     

static const char __kstrtab_schedule[] __attribute__((section("__ksymtab_strings"), aligned(1)))  = "_schedule";                                     

extern const struct kernel_symbol __ksymtab_schedule; 

/*將schedule 及字符串屬性利用kernel_symbol封裝對(duì)外可見*/
__visible const struct kernel_symbol __ksymtab_schedule __used __attribute__((section("___ksymtab + schedule"), unused)) = { 
    (unsigned long)&schedule, __kstrtab_schedule 
}; 

kernel_symbol 位于./include/linux/export.h 中:

#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#include <linux/compiler.h>
/*
*將ksymtab條目作為一對(duì)相對(duì)引用鏈接:
*在64位體系結(jié)構(gòu)上,這將大小減小了一半,
*并且消除了需要在可重定位內(nèi)核上進(jìn)行運(yùn)
*行時(shí)處理的絕對(duì)重定位的需求。
*/

#define __KSYMTAB_ENTRY_NS(sym, sec)     \
 __ADDRESSABLE(sym)      \
 asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
     " .balign 4         \n" \
     "__ksymtab_" #sym ":    \n" \
     " .long " #sym "- .    \n" \
     " .long __kstrtab_" #sym "- . \n" \
     " .long __kstrtabns_" #sym "- . \n" \
     " .previous     \n")


#define __KSYMTAB_ENTRY(sym, sec)   \
 __ADDRESSABLE(sym)      \
 asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
     " .balign 4         \n" \
     "__ksymtab_" #sym ":    \n" \
     " .long " #sym "- .    \n" \
     " .long __kstrtab_" #sym "- . \n" \
     " .long 0         \n" \
     " .previous         \n")


struct kernel_symbol {
 int value_offset;
 int name_offset;
 int namespace_offset;
};
#else
#define __KSYMTAB_ENTRY_NS(sym, sec)             \
 static const struct kernel_symbol __ksymtab_##sym      \
 __attribute__((section("___ksymtab" sec "+" #sym), used)) \
 __aligned(sizeof(void *))                     \
 = { (unsigned long)&sym, __kstrtab_##sym, __kstrtabns_##sym }


#define __KSYMTAB_ENTRY(sym, sec)                 \
 static const struct kernel_symbol __ksymtab_##sym      \
 __attribute__((section("___ksymtab" sec "+" #sym), used)) \
 __aligned(sizeof(void *))                     \
 = { (unsigned long)&sym, __kstrtab_##sym, NULL }


struct kernel_symbol {
 unsigned long value;
 const char *name;
 const char *namespace;
};
#endif

為何將主調(diào)度器全局導(dǎo)出

至此,調(diào)度對(duì)外導(dǎo)出就基本明晰了,但是進(jìn)一步引申思考?為什么還要將調(diào)度器schedule以模塊形式對(duì)外導(dǎo)出呢?EXPORT_SYMBOL對(duì)外導(dǎo)出,那么導(dǎo)出的作用域究竟多大呢,所包住的函數(shù)在內(nèi)核代碼中全局可見,也就意味著其他的內(nèi)核模塊可以使用該函數(shù)。但是貌似還是沒有回答說為啥要將調(diào)度器對(duì)外導(dǎo)出,潛意識(shí)我們會(huì)認(rèn)為調(diào)度器直接在后臺(tái)像個(gè)勤勞的大管家,在哪里不停的忙活就完了,難不成其他模塊還要主動(dòng)去調(diào)用調(diào)度器不成。為了驗(yàn)證猜想,搜一下吧:

看來猜想沒錯(cuò),事實(shí)上:schedule就是主調(diào)度器的函數(shù), 在內(nèi)核中的許多地方, 如果要將CPU分配給與當(dāng)前活動(dòng)進(jìn)程不同的另一個(gè)進(jìn)程, 都會(huì)直接主動(dòng)調(diào)用主調(diào)度器函數(shù)schedule.該函數(shù)完成如下工作:

  1. 確定當(dāng)前就緒隊(duì)列, 并在保存一個(gè)指向當(dāng)前(仍然)活動(dòng)進(jìn)程的task_struct指針;
  2. 檢查死鎖, 關(guān)閉內(nèi)核搶占后調(diào)用__schedule完成內(nèi)核調(diào)度;
  3. 恢復(fù)內(nèi)核搶占, 然后檢查當(dāng)前進(jìn)程是否設(shè)置了重調(diào)度標(biāo)志TLF_NEDD_RESCHED, 如果該進(jìn)程被其他進(jìn)程設(shè)置了TIF_NEED_RESCHED標(biāo)志, 則函數(shù)重新執(zhí)行進(jìn)行調(diào)度。
asmlinkage __visible void __sched schedule(void)
{

    /*  獲取當(dāng)前的進(jìn)程  */
    struct task_struct *tsk = current;

    /*  避免死鎖 */
    sched_submit_work(tsk);
    do {
        preempt_disable();                    /*  關(guān)閉內(nèi)核搶占  */
        __schedule(false);                    /*  完成調(diào)度  */
        sched_preempt_enable_no_resched();    /*  開啟內(nèi)核搶占  */
    } while (need_resched());   
    /*  如果該進(jìn)程被其他進(jìn)程設(shè)置了TIF_NEED_RESCHED標(biāo)志,則函數(shù)重新執(zhí)行進(jìn)行調(diào)度    */
}
EXPORT_SYMBOL(schedule);

以./drivers/s390/crypto/ap_bus.c 的函數(shù)ap_poll_thread為例:

/*ap_poll_thread():輪詢完成的請(qǐng)求的線程。AP總線輪詢線程
*該線程的目的是在循環(huán)中輪詢存在的請(qǐng)求,如果有一個(gè)“空閑”的cpu,
*就不需要做什么。 只要有其他任務(wù)或所有消息都已傳遞,輪詢就會(huì)停止。*/

static int ap_poll_thread(void *data)
{
 DECLARE_WAITQUEUE(wait, current);

 set_user_nice(current, MAX_NICE);
 set_freezable();
 while (!kthread_should_stop()) {
  add_wait_queue(&ap_poll_wait, &wait);
  set_current_state(TASK_INTERRUPTIBLE);
  if (ap_suspend_flag || !ap_pending_requests()) {
   schedule();
   try_to_freeze();
  }
  set_current_state(TASK_RUNNING);
  remove_wait_queue(&ap_poll_wait, &wait);
  if (need_resched()) {
            /*主動(dòng)調(diào)用調(diào)度器*/
   schedule();
   try_to_freeze();
   continue;
  }
  ap_tasklet_fn(0);
 }

 return 0;
}

關(guān)于內(nèi)核調(diào)度器究竟如何工作,還沒開始讀,如有興趣,請(qǐng)繼續(xù)關(guān)注。

點(diǎn)擊留言/查看留言

END

果喜歡右下點(diǎn)個(gè)在看,也會(huì)讓我倍感鼓舞

往期精彩推薦




Linux 內(nèi)核架構(gòu)分析
優(yōu)化嵌入式Linux的啟動(dòng)時(shí)間之啟動(dòng)腳本
優(yōu)化嵌入式Linux的啟動(dòng)時(shí)間之內(nèi)核
優(yōu)化嵌入式Linux的啟動(dòng)時(shí)間之文件系統(tǒng)
優(yōu)化嵌入式Linux的啟動(dòng)之工具鏈/應(yīng)用程序優(yōu)化
嵌入式Linux引導(dǎo)以及U-Boot移植介紹

關(guān)注置頂:掃描左下二維碼關(guān)注公眾號(hào)加星

討論加群:掃描右下二維碼添加,發(fā)送“加群”

關(guān)注

加群

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉