"asmlinkage" 與 函數調用參數傳遞

來源:互聯網
上載者:User

 相信大家在看linux的source code的時候,都會注意到asmlinkage這個宏,它是用來做什麼的呢?

The asmlinkage tag is one other thing that we should observe about this simple function. This is a #define for some gcc magic that tells the compiler that the function should not expect to find any of its arguments in registers (a common optimization), but only on the CPU's stack. Recall our earlier assertion that system_call consumes its first argument, the system call number, and allows up to four more arguments that are passed along to the real system call. system_call achieves this feat simply by leaving its other arguments (which were passed to it in registers) on the stack. All system calls are marked with the asmlinkage tag, so they all look to the stack for arguments. Of course, in sys_ni_syscall's case, this doesn't make any difference, because sys_ni_syscall doesn't take any arguments, but it's an issue for most other system calls.

    看一下/usr/include/asm/linkage.h裡面的定義:
    #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
    __attribute__是關鍵字,是gcc的C語言擴充,regparm(0)表示不從寄存器傳遞參數

    如果是__attribute__((regparm(3))),那麼調用函數的時候參數不是通過棧傳遞,而是直接放到寄存器裡,被調用函數直接從寄存器取參數

    還有一種是:

    #define fastcall __attribute__((regparm(3))) 
    #define asmlinkage __attribute__((regparm(0))) 
    函數定義前加宏asmlinkage ,表示這些函數通過堆棧而不是通過寄存器傳遞參數。 
    gcc編譯器在彙編過程中調用c語言函數時傳遞參數有兩種方法:一種是通過堆棧,另一種是通過寄存器。預設時採用寄存器,假如你要在你的彙編過程中調用c語言函數,並且想通過堆棧傳遞參數,你定義的c函數時要在函數前加上宏asmlinkage.

    asmlinkage long sys_nice(int increment)
    "asmlinkage" 是在 i386 system call 實作中相當重要的一個 gcc 標籤(tag)。
當 system call handler 要調用相對應的 system call routine 時,便將一般用途緩衝器的值 push 到 stack 裡,因此 system call routine 就要由 stack 來讀取 system call handler 傳遞的參數。這就是 asmlinkage 標籤的用意。
     system call handler 是 assembly code,system call routine(例如:sys_nice)是 C code,當 assembly code 調用 C function,並且是以 stack 方式傳參數(parameter)時,在 C function 的 prototype 前面就要加上 "asmlinkage"。加上 "asmlinkage" 後,C function 就會由 stack 取參數,而不是從 register 取參數(可能發生在程式碼最佳化後)。

    更進一步的說明...
    80x86 的 assembly 有 2 種傳遞參數的方法:
    1. register method
    2. stack method
    Register method 大多使用一般用途(general-purpose)緩衝器來傳遞參數,這種方法的好處是簡單且快速。另外一種傳遞參數的做法是使用 stack(堆棧),assembly code 的模式如下:
    push number1
    push number2
    push number3
    call sum
    在 'sum' procedure 裡取值的方法,最簡單的做法是:
    pop ax 
    pop ax
    pop bx
    pop cx
    Stack Top 是放 IP,我們傳給 sum procedure 的參數由 stack 的後一個 entry 開始讀取。
    其它有關 asmlinkage
    1. asmlinkage 是一個定義
    2. "asmlinkage" 被定義在 /usr/include/linux/linkage.h
    3. 如果您看了 linkage.h,會發現 "__attribute__" 這個文法,這是 gcc 用來定義 function attribute 的文法


在大型C語言項目工程或者linux核心中我們都會經常見到兩個FASTCALL和armlinkage

兩個標識符(修飾符),那麼它們各有什麼不同呢?今天就給大家共同分享一下自己的心得.

大家都知道在標準C系中函數的形參在實際傳入參數的時候會涉及到參數存放的問題,那麼這些參數存放在哪裡呢? 有一定理論基礎的朋友一定會肯定地回答:這些函數參數和函數內部局部變數一起被分配到了函數的局部堆棧中,真的是這樣嗎?其實還有例外的情況:

首先作為linux作業系統,它不一定就只運行在X86平台下面,還有其他平台例如ARM, PPC,達芬奇等等,所以在不同的處理器結構上不能保證都是通過局部棧傳遞參數的,可能此時就有朋友就會問:不放在棧中能放在哪裡呢?熟悉ARM的朋友一定知道ARM對函數調用過程中的傳參定義了一套規則,叫 ATPCS(內地叫AAPCS),規則中明確指出ARM中R0-R4都是作為通用寄存器使用,在函數調用時處理器從R0-R4中擷取參數,在函數返回時再將需要返回的參數一次存到R0-R4中,也就是說可以將函數參數直接存放在寄存器中,所以為了嚴格區別函數參數的存放位置,引入了兩個標記,即 asmlinkage和FASTCALL,前者表示將函數參數存放在局部棧中,後者則是通知編譯器將函數參數用寄存器儲存起來.

我們在搜尋 一些額外的線索,ARM中R0-R4用於存放傳入參數,隱約告訴我們,作為高水平嵌入式系統開發人員,或者高水平C語言程式員,函數的參數不應該大於5個,那麼有人就會反過來問:超過5個的那些參數又何去何從?我的回答是:傳入參數如果超過5個,多餘的參數還是被存放到局部棧中,此時有人可能又會問:將函數參數傳入局部棧有什麼不好?我的回答是:表面上沒什麼不好,但是如果你是一名具有linux核心修養的程式員,你就會隱約記得linux中, 不管是系統調用,還是系統陷阱都會引起使用者空間陷入核心空間,我們知道,系統空間的許可權級是0,使用者空間的許可權級為3,系統調用從許可權級為3的使用者空間陷到許可權級為0的核心空間,必然引起堆棧切換,linux系統將從全域任務狀態棧TSS中找到一個合適的核心棧資訊儲存覆蓋當前SP,SS兩個寄存器的內容,以完成堆棧切換,此時處於核心空間所看到的棧已不是使用者空間那個棧,所以在調用的時候壓入使用者棧的資料就在陷入核心的那個瞬間,被滯留在使用者空間棧, 核心根本不知道它的存在了,所以作為安全考慮或者作為高水平程式員的切身修養出發,都不應該向系統調用級函數傳入過多的參數。(分析見下文)

進程切換:

linux下的進程切換隻發生的核心態。首先需要說明一點的是,在linux下有段的概念,但是沒有段的實際應用。linux只有四個段,分別是使用者程式碼片段、使用者資料區段、核心程式碼片段、核心資料區段,基地址都是0x00000000,大小為4G,這樣就使得linux下的邏輯地址與線性地址是相同的。#define __KENEL_CS 0X10 //在gdt的index=2,rpl=0#define __KERNEL_DS 0x18 //在gdt的index=3,rpl=0#define __USER_CS 0x23 //在gdt的index=4,rpl=3#defind __USER_DS 0x2b //在gdt的index=5,rpl=3 從使用者態切換到核心態時,因為使用不同的堆棧段,所以需要進行堆棧切換;而從核心態切換到核心態時,使用的都是核心資料區段,所以不需要切換堆棧,但是需要修改TSS的esp0。

這就說明了在核心態,可以使用寄存器或堆棧傳遞參數,而在使用者態向核心態傳遞參數最好使用寄存器的緣由(堆棧切換)!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.