2012.1.29 - Linux内核 - 系统调用

Posted on 2013-02-13 23:45  SnakeHunt2012  阅读(291)  评论(0)    收藏  举报

本文始作于2012年1月29日,刊登于人人网,于2013年2月13日迁移至此

现在是19:16,上午九点多就被曲叫去他家吃饭了,还喝了酒,不过不多。起得太早了,感觉一天都特别困。回来之后开机登录额xmii,它第一次使用是有简单入门提示的,提示还挺简单,很快的功夫就已经能进行基本的界面操作了,操作还都比较简单,但是没说关于配置文件的问题。刚才睡了一觉,现在醒来感觉好多了,越来越感觉现在不像话了,寒假还有不到一个月了,要赶紧写实验,写开发了啊,过两天《和声学》的答案到了,就得开始学和声了,后面还有pureweber的开发任务呢。

昨天最后一点一直过不去,errno一直运作不正确,老是显示-1,昨天晚上的想法是有可能是errno设置的方法不对,可能系统里面有专门设置errno用的函数,一般像Java/C++这样的工程语言都好干这事,明明一句话就能设置,非得调一个函数,用专门的函数设置。今天我在虚拟机里用a.c调试的时候,突然想到这个errno是用户态的啊,他是在who.c里面定义的宏,而我写的who.c里面定义的errno是编译操作系统时的宏,跟本就不是一个变量啊,这根本就是两个地址啊,而且还是一个在用户区一个在内核区,于是我就明白了,在who.c里面自己开一个errno根本就属于掩耳盗铃,自欺欺人嘛,他俩根本就不在一个时代,这就好比一个是真实的人,一个小说中的人物。然后我又进一步想,既然这样,那我要是想把a.c里面errno进行修改就属于修改调用函数外面的变量了,这必须得通过传参啊,而且还是跨界传参,但是iam()和whoiam()参数表不给你传这种东西啊。得动用户态的东西,还传不了地址,这怎么可能办得到呢?后来百度,根本就没有类似的问题,在google,事实证明还是google好使:

errno是libc的全局变量,内核并不会直接访问它。 
系统调用返回时,把错误结果放在寄存器%eax中, 
libc会把这个%eax复制给errno,然后返回-1给调用的程序。 
这样,程序得到的系统调用的结果是-1,具体的错误在errno中。 

然后我猛然想到,这是那段汇编代码的事啊,转头看include/unistd.h,就是这段代码:

 1 #define _syscall1(type,name,atype,a) \
 2 type name(atype a) \
 3 { \
 4 long __res; \
 5 __asm__ volatile ("int $0x80" \
 6     : "=a" (__res) \
 7     : "0" (__NR_##name),"b" ((long)(a))); \
 8 if (__res >= 0) \
 9     return (type) __res; \
10 errno = -__res; \
11 return -1; \
12 }

 

宏展开之后他会被展开成:

int iam(const char *name)
{
    long __res;
    __asm__ volatile (
        "int $0x80"
        : "=a" (__res)
        : "0" (__NR_iam),"b" ((long)(name)));
    if (__res >= 0)
        return int __res;
    errno = -__res;
    return -1;
}

 

原来每个系统调用都必须传errno,而且在嵌入汇编的时候就已经写好了,我却还琢磨着自己写呢。还是代码读得不到位啊。更令我惊异的是,从这短代码里可以看出来,传给errno竟然是iam()的return的值!!!我iam()return的值竟然是给errno的!!!还取反了!!!那我return的就应该是-EINVAL了,怪不得昨天看别的系统调用函数的时候看见好多函数return的都是一个宏取负,原来传的就是错误类型。总结一下,在设计系统调用函数的时候,return的是错误类型号,如果return的是非负数则有自己的含义,你自己规定,在这里就表示传过去几个字符串,如果传的是非负数则代表出错类型,把它取反就可以在error.h里查找到是哪种错误。好了,现在改好了,果然就是这个问题,然后就满分了:

testlab2.c 测试通过

 

testlab2.sh 测试通过



iam.c 代码:

#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#define __LIBRARY__
#include <unistd.h>

_syscall1(int, iam, const char*, name);

int main(int argc, char *argv[])
{
    if (strlen(*(argv + 1)) > 23)
        return 0;
    iam(*(argv + 1));
    return 0;
}

whoiam.c 代码:

#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#define __LIBRARY__
#include <unistd.h>

_syscall2(int, whoiam, char *, name, unsigned int, size);

int main(void)
{
    char str[100];
    whoiam(str, 100);
    printf("%s\n", str);
    return 0;
}

who.c 代码:

/*
 *  linux/kernel/who.c
 *
 *  Copyleft 2012   BlackSun2012
 */
#include <errno.h>
#include <linux/kernel.h>
#include <asm/segment.h>

#define MAX 23

char str[MAX + 1];

int sys_iam(const char *name)
{
    int i;

    for (i = 0; i <= MAX; ++i) {
        str[i] = get_fs_byte(name + i);
        if (str[i] == '\0')
            return i;
    }
    return -EINVAL; 
}

int sys_whoiam(char *name, unsigned int size)
{
    int i;
    extern int errno;

    for (i = 0; i <= size; ++i) {
        put_fs_byte(str[i], name + i);
        if (str[i] == '\0')
            return i;
    }
    return -EINVAL;
}