日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

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

關(guān)注、星標嵌入式客棧,干貨及時送達

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

前情提要

《閱讀內(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弄錯了,修正如下)?

    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)知識點梳理

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

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

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

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

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

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

    #define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))

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

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

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

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

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

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

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

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

    • 類型名示例

      typeof (int *)

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

  • __visible ,這在哪里實現(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,這個咋一看,也是一頭霧水。找到出處:./include/sched/debug.h

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

    類似地,還有

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

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

  • 兩個以雙引號的字符串,編譯預(yù)處理時,會自動連接為一個字符串

    "_" "schedule" 變成 “_schedule”

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

    #define __used   __attribute__((__used__))

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

再看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,指定存儲位置*/
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封裝對外可見*/
__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條目作為一對相對引用鏈接:
*在64位體系結(jié)構(gòu)上,這將大小減小了一半,
*并且消除了需要在可重定位內(nèi)核上進行運
*行時處理的絕對重定位的需求。
*/

#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)度器全局導出

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

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

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

    /*  獲取當前的進程  */
    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());   
    /*  如果該進程被其他進程設(shè)置了TIF_NEED_RESCHED標志,則函數(shù)重新執(zhí)行進行調(diào)度    */
}
EXPORT_SYMBOL(schedule);

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

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

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()) {
            /*主動調(diào)用調(diào)度器*/
   schedule();
   try_to_freeze();
   continue;
  }
  ap_tasklet_fn(0);
 }

 return 0;
}

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

點擊留言/查看留言

END

果喜歡右下點個在看,也會讓我倍感鼓舞

往期精彩推薦




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

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

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

關(guān)注

加群

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

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

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

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

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

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

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

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

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

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

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

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

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

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

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

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

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

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

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

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

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

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