www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

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

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

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

前情提要

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

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);

全部展開(kāi)后,得到了什么呢(前文中__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)的展開(kāi)*/
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)梳理

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

  • 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為例來(lái)探討一下。

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

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

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

    很可能沒(méi)見(jiàn)過(guò)這種特性,那有啥妙用呢?

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

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

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

    • 類(lèi)型名示例

      typeof (int *)

      這里描述的類(lèi)型是指向int的指針類(lèi)型。

  • __visible ,這在哪里實(shí)現(xiàn)的呢?這是將gcc的__externally_visible__屬性利用宏轉(zhuǎn)定義了,以增加可讀性。用于聲明全局可見(jià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")))

    類(lèi)似地,還有

    #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)來(lái)解析所有對(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)展式

好了,前面的都整明白了,再來(lái)看前面的那段代碼:

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)的展開(kāi)*/
/*利用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ì)外可見(jiàn)*/
__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)核代碼中全局可見(jiàn),也就意味著其他的內(nèi)核模塊可以使用該函數(shù)。但是貌似還是沒(méi)有回答說(shuō)為啥要將調(diào)度器對(duì)外導(dǎo)出,潛意識(shí)我們會(huì)認(rèn)為調(diào)度器直接在后臺(tái)像個(gè)勤勞的大管家,在哪里不停的忙活就完了,難不成其他模塊還要主動(dòng)去調(diào)用調(diào)度器不成。為了驗(yàn)證猜想,搜一下吧:

看來(lái)猜想沒(méi)錯(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();    /*  開(kāi)啟內(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)度器究竟如何工作,還沒(méi)開(kāi)始讀,如有興趣,請(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),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

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

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見(jiàn),不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(chē)(EV)作為新能源汽車(chē)的重要代表,正逐漸成為全球汽車(chē)產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車(chē)的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車(chē)的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車(chē) 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車(chē)場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周?chē)娮釉O(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開(kāi)關(guān)電源具有效率高的特性,而且開(kāi)關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開(kāi)關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉