五、系统调用(2)-例子分析

用户程序调用getuid()
C库给所用系统调用作出了定义,但是自己定义系统调用不想改变库,则在自己mian函数前加入:_syscall0(int, mysyscall )

#include <linux/unistd.h>
int main()
{
    int i = getuid();
    printf("getuid:%d\n", i);
    return 0;
} 

getuid经unistd.h宏转换

  • getuid变为__NR_getuid
  • 这里无参数到寄存器
  • 返回类型为int
  • __NR_getuid根据宏转换为24
//linux/include/asm-i386/unistd.h
#define __NR_getuid      24
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
__syscall_return(type,__res); \
}

int getuid(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_getuid)); \
__syscall_return(int,__res); \
}
/*
    "int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_getuid)); \
    看成一句,进入软中断
*/

进入软中断,执行system_call

  • 系统启动时,系统调用的软中断已经在traps.c中注册好
  • 发生系统调用软中断自动进入system_call
  • 查询sys_call_table找到第24个系统调用sys_setuid16
//linux/arch/i386/kernel/entry.S
ENTRY(system_call)
    pushl %eax          # save orig_eax
    SAVE_ALL
    GET_CURRENT(%ebx)
    testb $0x02,tsk_ptrace(%ebx)   # PT_TRACESYS
    jne tracesys
    cmpl $(NR_syscalls),%eax
    jae badsys
    call *SYMBOL_NAME(sys_call_table)(,%eax,4)
    movl %eax,EAX(%esp)     # save the return value
ENTRY(ret_from_sys_call)
    cli             # need_resched and signals atomic test
    cmpl $0,need_resched(%ebx)
    jne reschedule
    cmpl $0,sigpending(%ebx)
    jne signal_return
restore_all:
    RESTORE_ALL
//linux/arch/i386/kernel/entry.S
ENTRY(sys_call_table)
    .......
    .long SYMBOL_NAME(sys_getuid16)         /* 24 */
    .......

执行/linux/kernel/uid16.c中sys_getuid16

  • asmlinkage 是gcc中标志,说明该函数从堆栈中而不是从寄存器中那参数
    -asmlinkage 等同于attribute((regparm(0)))
asmlinkage long sys_getuid16(void)
{
    return high2lowuid(current->uid);
}

内核空间数据处理
    用户空间要传递数据给内核,通过传递指向用户态数据的结构指针。用户态传递给内核是一个指针加一个范围,有可能超过进程地址空间,破坏运行环境。内核总是要对这个地址范围进行检验,包括是否正确,能不能读、写等。

  • linux/include/asm-i386/uaccess.h
//copy_to_user中会调用access_ok(type, addr, size)检测
//type:指定检查类型(读、写等)
//后两个参数为范围和长度
copy_from_user(void *to, const *from, int c);
copy_to_user(void *to, const *from, int c);
  • include/linux/kernel.h

  • 编写内核代码时常用的一个函数,用来大洋信息到console,或者系统日志。比printf(内核中不可用)多一个表示打印级别。级别比console高打印到console,否则打印到系统日志/var/log/message

asmlinkage int printk(const char * fmt, ...)
    __attribute__ ((format (printf, 1, 2)));
#define KERN_EMERG  "<0>"   /* system is unusable           */
#define KERN_ALERT  "<1>"   /* action must be taken immediately */
#define KERN_CRIT   "<2>"   /* critical conditions          */
#define KERN_ERR    "<3>"   /* error conditions         */
#define KERN_WARNING    "<4>"   /* warning conditions           */
#define KERN_NOTICE "<5>"   /* normal but significant condition */
#define KERN_INFO   "<6>"   /* informational            */
#define KERN_DEBUG  "<7>"   /* debug-level messages         */
  • include/linux/slab.h
//内核编程时分配内存,flags表示分配内存的类型
extern void *kmalloc(size_t size, int flags);
  • _ _init表示这个函数使用后就可以丢弃
  • _ _exit表示如果不是以模块方式编译这段程序,则后面的函数可以丢弃
int __init init_myf()
{
}

void __exit exit_myf()
{
}

猜你喜欢

转载自blog.csdn.net/qq_23084801/article/details/78748731