一、從使用者態訪問系統調用通常,系統調用靠C庫支援。使用者程式通過包含標準標頭檔並和C庫連結,就可以使用系統調用。但如果你僅僅寫出系統調用,glibc庫恐怕並不提供支援。
這裡有一個好訊息還有一個壞訊息,好訊息是Linux本身提供了一組宏定義linux/include/asm-x86_64/unistd.h檔案中。壞訊息是在2.6.20之後的核心版本取消了這一系列的宏,導致一開始編譯源檔案的時候出錯,最後在2.6.18中找到了這段代碼。其實這段彙編主要的作用就是將系統調用號傳遞給EAX寄存器,同時將從EAX寄存器取出傳回值。
//test.c#include <stdio.h>#include <syscall.h>#include <linux/errno.h>#include <errno.h>#define __NR_foo 312#define MAX_ERRNO 127#define __syscall "syscall"#define __syscall_clobber "r11","rcx","memory"#define __syscall_return(type,res)\ do{\ if((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)){\ errno = -res;\ res = -1;\ }\ return (type)(res);\ }while(0)#define _syscall0(type,name)\ type name(void){\ long __res;\ __asm__ volatile(__syscall\ : "=a" (__res)\ : "0" (__NR_##name) : __syscall_clobber );\ __syscall_return(type,__res);\ }_syscall0(long,foo)int main(int argc,char** argv){ long stack_size = 0; stack_size = foo(); if(stack_size == -1) perror("ERROR"); printf("The kernel stack size is %ld\n",stack_size); return 0;}Output: The kernel stack size is 8192
這裡面有幾點需要注意。
(1)由於我所編譯的環境的核心版本是3.2.6,而之前也已經介紹過在2.6.20之後unistd.h檔案中不再存在有這些宏,因此宏需要自己聲明。上面的宏聲明是我從2.6.18核心中拷貝過來的。
(2)除此之外還會遇到一個宏__syscall_return(type,res)用於返回系統調用執行後的傳回值。
(3)在_syscallX這一系列宏當中存在一個__syscall宏,在源檔案的開始定義為“syscall”,曾經試圖去找它的定義,但是沒有發現。需要說明的是,在我最初用“int $0x80”而不是“syscall”的時候,系統調用不成功。ERROR返回錯誤:Dad Address。
(4)在聲明_syscall0(long,foo)後面沒有分號。
下面的例子是從網上發現的。可以看到通過函數syscall將系統調用號傳遞進去。這樣調用系統調用更方便。
//test1.c#include <stdio.h>#include <asm/unistd_64.h>#include <syscall.h>#include <sys/syscall.h>#define __NR_foo 312int main(int argc,char** argv){ long ret = 0; ret = syscall(__NR_foo); printf("Thread Stack Size is %ld\n",ret); return 0;}Output: Thread Stack Size is 8192
二、從使用者態訪問超級調用以下是在網上發現的一個使用者態調用Xen中hypervisor的例子。該例子中類似上面系統調用在Xen中新添加了一個超級調用。
#include <stdio.h>#include <errno.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/types.h>#include <fcntl.h>#include <string.h>#include <xenctrl.h>#include <xen/sys/privcmd.h>#define __HYPERVISOR_print_string 39int main(int argc,char** argv){ int fd = 0; int ret = 0; char* message = NULL; if(argc != 2) { printf("Please input one parameter!\n"); return -1; } message = (char*)malloc(sizeof(char) * strlen(argv[1] + 1)); if (message == NULL) { printf("malloc failed"); return -1; } strcpy(message,argv[1]); privcmd_hypercall_t hcall = {__HYPERVISOR_print_string, {message,0,0,0,0}}; fd = open("/proc/xen/privcmd",O_RDWR); if(fd < 0) { perror("open"); exit(1); } else printf("fd=%d\n",fd); ret = ioctl(fd,IOCTL_PRIVCMD_HYPERCALL,&hcall); perror("ioctl err"); printf("ret = %d\n",ret); close(fd); return 0;}
之後便以Xen,重新載入Xen hypervisor,在xm dmesg命令下即可查看hypercall運行結果。