Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1748028
  • 博文数量: 199
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 6186
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-30 11:01
个人简介

Linuxer.

文章存档

2015年(4)

2014年(28)

2013年(167)

分类: LINUX

2013-04-11 15:31:33

原文地址:mdev.c分析 作者:jwwzhh

mdev - Mini udev for busybox
"mdev -s"扫描/sys/class/xxx,在目录中查找dev文件(它的格式为"M:m\n")。例如:/sys/class/tty0/dev,它的内容为"4:0\n"。目录名作为设备名。/sys/class/下的每个文件夹都代表着一个子系统。然后mdev创建/dev/设备名的设备节点。
如果/sys/class/.../dev文件不存在,mdev将按照配置文件执行相应的操作。

int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;

int mdev_main(int argc UNUSED_PARAM, char **argv)
{
    RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);

    /* We can be called as hotplug helper */
    /* Kernel cannot provide suitable stdio fds for us, do it ourself */
    bb_sanitize_stdio();  //阅读这个函数的源代码后,发现这里其实就是判断了/dev/null是否存在。如果打不开,那么就进入die状态。

    /* Force the configuration file settings exactly */
    umask(0);  //配置屏蔽位

    xchdir("/dev");  //切换到/dev目录

    if (argv[1] && strcmp(argv[1], "-s") == 0) {  //如果执行的是mdev -s,这是在shell里调用的。在系统启动时调用。创建所有设备驱动的节点。
        /* Scan:
         * mdev -s
         */

        struct stat st;

        xstat("/", &st);
        root_major = major(st.st_dev);  //"根文件系统所在的设备标识"
        root_minor = minor(st.st_dev);

        /* ACTION_FOLLOWLINKS is needed since in newer kernels
         * /sys/block/loop* (for example) are symlinks to dirs,
         * not real directories.
         * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
         * but we can't enforce that on users)
         */

        if (access("/sys/class/block", F_OK) != 0) {  //判断/sys/class/block这个文件或者目录是否存在。存在返回0,否则返回-1
            /* Scan obsolete /sys/block only if /sys/class/block
             * doesn't exist. Otherwise we'll have dupes.
             * Also, do not complain if it doesn't exist.
             * Some people configure kernel to have no blockdevs.
             */

            recursive_action("/sys/block",  //这个函数是递归函数,它会把/sys/block目录下的所有文件文件夹都去查看一遍,如果发现dev文件,那么将按照/etc/mdev.conf文件进行相应的配置。如果没有配置文件,那么直接创建设备节点。
                ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
                fileAction, dirAction, temp, 0);
        }
        recursive_action("/sys/class",
            ACTION_RECURSE | ACTION_FOLLOWLINKS,
            fileAction, dirAction, temp, 0);
    } else {
        char *fw;
        char *seq;
        char *action;
        char *env_path;
        static const char keywords[] ALIGN1 = "remove\0add\0";
        enum { OP_remove = 0, OP_add };
        smalluint op;

////////////////////////////////////////////////////

/* for arches where byte accesses generate larger code: */
typedef int smallint;
typedef unsigned smalluint;

////////////////////////////////////////////////////
        /* Hotplug:
         * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
         * ACTION can be "add" or "remove"
         * DEVPATH is like "/block/sda" or "/class/input/mice"
         */

经过驱动层分析,所得的环境变量为,这里是以spidev,0.0设备为例:

ACTION=add: kobject_actions[KOBJ_ADD]
DEVPATH=/class/spidev/spidev0.0/: kobject_get_path(kobj, GFP_KERNEL) /sys不存在,这里只统计到/sys目录下
SUBSYSTEM=spidev: dev->bus->name,dev->class->name,如果dev->bus不存在的情况下,那么才使用dev->class->name
MAJOR=MAJOR(dev->devt)
MINOR=MINOR(dev->devt)
PHYSDEVPATH=/devices/platform/atmel_spi.0/spi0.0/: kobject_get_path(&dev->parent->kobj, GFP_KERNEL) /sys不存在,这里只统计到/sys目录下
PHYSDEVBUS=/bus/spi/: dev->parent->bus->name /sys不存在,这里只统计到/sys目录下
PHYSDEVDRIVER=spidev: dev->parent->driver->name
SEQNUM=++uevent_seqnum
HOME=/
PATH=/sbin:/bin:/usr/sbin:/usr/bin

        action = getenv("ACTION");
        env_path = getenv("DEVPATH");
        subsystem = getenv("SUBSYSTEM");
        if (!action || !env_path /*|| !subsystem*/)
            bb_show_usage();
        fw = getenv("FIRMWARE");
        op = index_in_strings(keywords, action);
        /* If it exists, does /dev/mdev.seq match $SEQNUM?
         * If it does not match, earlier mdev is running
         * in parallel, and we need to wait */

        seq = getenv("SEQNUM");
        if (seq) {
            int timeout = 2000 / 32; /* 2000 msec */
            do {
                int seqlen;
                char seqbuf[sizeof(int)*3 + 2];

                seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
                if (seqlen < 0) {
                    seq = NULL;
                    break;
                }
                seqbuf[seqlen] = '\0';
                if (seqbuf[0] == '\n' /* seed file? */
                 || strcmp(seq, seqbuf) == 0 /* correct idx? */
                ) {
                    break;
                }
                usleep(32*1000);
            } while (--timeout);
        }

        snprintf(temp, PATH_MAX, "/sys%s", env_path);
        if (op == OP_remove) {
            /* Ignoring "remove firmware". It was reported
             * to happen and to cause erroneous deletion
             * of device nodes. */

            if (!fw)
                make_device(temp, 1);
        }
        else if (op == OP_add) {
            make_device(temp, 0);
            if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
                if (fw)
                    load_firmware(fw, temp);
            }
        }

        if (seq) {
            xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
        }
    }

    if (ENABLE_FEATURE_CLEAN_UP)
        RELEASE_CONFIG_BUFFER(temp);

    return EXIT_SUCCESS;
}

./include/autoconf.h:#define ENABLE_FEATURE_CLEAN_UP 0

int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;

/* We need to export XXX_main from libbusybox
 * only if we build "individual" binaries
 */

#if ENABLE_FEATURE_INDIVIDUAL
#define MAIN_EXTERNALLY_VISIBLE EXTERNALLY_VISIBLE
#else
#define MAIN_EXTERNALLY_VISIBLE
#endif

./include/autoconf.h:#define ENABLE_FEATURE_INDIVIDUAL 0
./include/libbb.h:#if ENABLE_FEATURE_INDIVIDUAL
./libbb/appletlib.c:#undef ENABLE_FEATURE_INDIVIDUAL
./libbb/appletlib.c:#define ENABLE_FEATURE_INDIVIDUAL 1
./libbb/appletlib.c:#if ENABLE_FEATURE_INDIVIDUAL
通过上面我们可以发现,应该定义的ENABLE_FEATURE_INDIVIDUAL是0。也就是说MAIN_EXTERNALLY_VISIBLE为空。

不过看看EXTERNALLY_VISIBLE是怎么定义的。

/include/platform.h

* -fwhole-program makes all symbols local. The attribute externally_visible
   forces a symbol global. */
#if __GNUC_PREREQ(4,1)  //如果编译器的版本高于4.1版本,那么返回1,否则返回0。
# define EXTERNALLY_VISIBLE __attribute__(( visibility("default")))
//__attribute__ ((__externally_visible__))

#else
# define EXTERNALLY_VISIBLE
#endif

那么还需要看看__GNU_PREREQ

/* Convenience macros to test the version of gcc. */
#undef __GNUC_PREREQ
#if defined __GNUC__ && defined __GNUC_MINOR__
# define __GNUC_PREREQ(maj, min) \
        ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
#else
# define __GNUC_PREREQ(maj, min) 0
#endif

这里解释一下宏定义__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__分别定义了gcc编译器的版本号,例如:gcc4.3.1
下面描述:
__attribute__((visibility("visibility_type")))

此函数属性影响 ELF 符号的可见性。

Note

此属性是 ARM 编译器支持的 GNU 编译器扩展。

语法

__attribute__((visibility("visibility_type")))

其中,visibility_type 是下列值之一:

default

假定的符号可见性可通过其他选项进行更改。缺省可见性将覆盖此类更改。缺省可见性与外部链接对应。

hidden

该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。

internal

除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。

protected

该符号存放在动态符号表中,但定义模块内的引用将与局部符号绑定。也就是说,另一模块无法覆盖该符号。

用法

除指定 default 可见性外,此属性都可与在这些情况下具有外部链接的声明结合使用。

您可在 C 和 C++ 中使用此属性。在 C++ 中,还可将它应用于类型、成员函数和命名空间声明。

示例

void __attribute__((visibility(“internal”))) foo()
{
   ...
}
RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);通过下面分析可知,分配一段缓存,名字为temp。


RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);

# define PATH_MAX 256

#define SCRATCH_SIZE 80

/* buffer allocation schemes */
#if ENABLE_FEATURE_BUFFERS_GO_ON_STACK   //定义在栈中
#define RESERVE_CONFIG_BUFFER(buffer,len) char buffer[len]
#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len]
#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
#else
#if ENABLE_FEATURE_BUFFERS_GO_IN_BSS     //定义在bss段中
#define RESERVE_CONFIG_BUFFER(buffer,len) static char buffer[len]
#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len]
#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
#else                                    //定义在堆中。
#define RESERVE_CONFIG_BUFFER(buffer,len) char *buffer = xmalloc(len)
#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer = xmalloc(len)
#define RELEASE_CONFIG_BUFFER(buffer) free(buffer)
#endif
#endif

bb_sanitize_stdio();

    /* We can be called as hotplug helper */
    /* Kernel cannot provide suitable stdio fds for us, do it ourself */
    bb_sanitize_stdio();

void FAST_FUNC bb_sanitize_stdio(void)
{
    bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
}

enum {
    DAEMON_CHDIR_ROOT = 1,
    DAEMON_DEVNULL_STDIO = 2,
    DAEMON_CLOSE_EXTRA_FDS = 4,
    DAEMON_ONLY_SANITIZE = 8, /* internal use */
};


void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
{
    int fd;

    if (flags & DAEMON_CHDIR_ROOT)
        xchdir("/");   //切换到根目录,如果切换失败,那么进入die状态

    if (flags & DAEMON_DEVNULL_STDIO) {  //是否使用DEVNULL设备作为输入输出设备
        close(0);  //文件描述符0,标准输入,缺省是键盘
        close(1);  //文件描述符1,标准输出,缺省是屏幕
        close(2);  //文件描述符2,错误输出,缺省是屏幕
    }

    fd = open(bb_dev_null, O_RDWR);  //bb_dev_null为"/dev/null"
    if (fd < 0) {
        /* NB: we can be called as bb_sanitize_stdio() from init
         * or mdev, and there /dev/null may legitimately not (yet) exist!
         * Do not use xopen above, but obtain _ANY_ open descriptor,
         * even bogus one as below. */

        fd = xopen("/", O_RDONLY); /* don't believe this can fail */
    }

    while ((unsigned)fd < 2)
        fd = dup(fd); /* have 0,1,2 open at least to /dev/null */

    if (!(flags & DAEMON_ONLY_SANITIZE)) {
        if (fork_or_rexec(argv))
            exit(EXIT_SUCCESS); /* parent */
        /* if daemonizing, make sure we detach from stdio & ctty */
        setsid();
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
    }
    while (fd > 2) {
        close(fd--);
        if (!(flags & DAEMON_CLOSE_EXTRA_FDS))
            return;
        /* else close everything after fd#2 */
    }
}

./include/libbb.h:extern const char bb_dev_null[];
./libbb/messages.c:const char bb_dev_null[] ALIGN1 = "/dev/null";


// Die if we can't chdir to a new path.

void FAST_FUNC xchdir(const char *path)
{
    if (chdir(path))   //切换到path目录。如果失败,那么进入die状态。
        bb_perror_msg_and_die("chdir(%s)", path);
}

void FAST_FUNC bb_perror_msg_and_die(const char *s, ...)
{
    va_list p;

    va_start(p, s);
    /* Guard against ": Success" */
    bb_verror_msg(s, p, errno ? strerror(errno) : NULL); //如果系统出错,那么errno将是非0,strerror(errno)将把错误代码转换成字符串信息
    va_end(p);
    xfunc_die();
}

void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
{
    char *msg, *msg1;
    int applet_len, strerr_len, msgeol_len, used;

    if (!logmode)
        return;

    if (!s) /* nomsg[_and_die] uses NULL fmt */
        s = ""; /* some libc don't like printf(NULL) */

    used = vasprintf(&msg, s, p);  //字符串格式化
    if (used < 0)
        return;

    /* This is ugly and costs +60 bytes compared to multiple
     * fprintf's, but is guaranteed to do a single write.
     * This is needed for e.g. httpd logging, when multiple
     * children can produce log messages simultaneously. */


    applet_len = strlen(applet_name) + 2; /* "applet: " */
    strerr_len = strerr ? strlen(strerr) : 0;
    msgeol_len = strlen(msg_eol);
    /* can't use xrealloc: it calls error_msg on failure,
     * that may result in a recursion */

    /* +3 is for ": " before strerr and for terminating NUL */
    msg1 = realloc(msg, applet_len + used + strerr_len + msgeol_len + 3);  //重新分配内存
    if (!msg1) {
        msg[used++] = '\n'; /* overwrites NUL */
        applet_len = 0;
    } else {
        msg = msg1;
        /* TODO: maybe use writev instead of memmoving? Need full_writev? */
        memmove(msg + applet_len, msg, used);
        used += applet_len;
        strcpy(msg, applet_name);
        msg[applet_len - 2] = ':';
        msg[applet_len - 1] = ' ';
        if (strerr) {
            if (s[0]) { /* not perror_nomsg? */
                msg[used++] = ':';
                msg[used++] = ' ';
            }
            strcpy(&msg[used], strerr);
            used += strerr_len;
        }
        strcpy(&msg[used], msg_eol);
        used += msgeol_len;
    }
    //": "": "
    if (logmode & LOGMODE_STDIO) {
        fflush_all();
        full_write(STDERR_FILENO, msg, used);
    }
    if (logmode & LOGMODE_SYSLOG) {
        syslog(LOG_ERR, "%s", msg + applet_len);
    }
    free(msg);
}

int FAST_FUNC fflush_all(void)
{
    return fflush(NULL);  //这里为NULL的意思是,清空所有输出流。让它真正显示或者写入到硬盘中。
}

/*
 * Write all of the supplied buffer out to a file.
 * This does multiple writes as necessary.
 * Returns the amount written, or -1 on an error.
 */

ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len)
{
    ssize_t cc;
    ssize_t total;

    total = 0;

    while (len) {
        cc = safe_write(fd, buf, len);

        if (cc < 0) {
            if (total) {
                /* we already wrote some! */
                /* user can do another write to know the error code */
                return total;
            }
            return cc;    /* write() returns -1 on failure. */
        }

        total += cc;
        buf = ((const char *)buf) + cc;
        len -= cc;
    }

    return total;
}

ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count)
{
    ssize_t n;

    do {
        n = write(fd, buf, count);
    } while (n < 0 && errno == EINTR);  //errno=EINTR说明是由于信号打断了,那么就重写。

    return n;
}

void FAST_FUNC xfunc_die(void)
{
    if (die_sleep) {
        if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH)
         && die_sleep < 0
        ) {
            /* Special case. We arrive here if NOFORK applet
             * calls xfunc, which then decides to die.
             * We don't die, but jump instead back to caller.
             * NOFORK applets still cannot carelessly call xfuncs:
             * p = xmalloc(10);
             * q = xmalloc(10); // BUG! if this dies, we leak p!
             */

            /* -2222 means "zero" (longjmp can't pass 0)
             * run_nofork_applet() catches -2222. */

            longjmp(die_jmp, xfunc_error_retval ? xfunc_error_retval : -2222);
        }
        sleep(die_sleep);
    }
    exit(xfunc_error_retval);
}

# define close(fd) do { \
    int dfd = (fd); \
    if (close(dfd) < 0) \
        bb_error_msg("bug on %d: closing %d(0x%x)", \
            __LINE__, dfd, dfd); \
} while (0)


// Die if we can't open an existing file and return a fd.

int FAST_FUNC xopen(const char *pathname, int flags)
{
    return xopen3(pathname, flags, 0666);
}

// Die if we can't open a file and return a fd.

int FAST_FUNC xopen3(const char *pathname, int flags, int mode)
{
    int ret;

    ret = open(pathname, flags, mode);
    if (ret < 0) {
        bb_perror_msg_and_die("can't open '%s'", pathname);
    }
    return ret;
}

recursive_action("/sys/block",
    ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
    fileAction, dirAction, temp, 0);

/* File callback for /sys/ traversal */
static int FAST_FUNC fileAction(const char *fileName,
        struct stat *statbuf UNUSED_PARAM,
        void *userData,
        int depth UNUSED_PARAM)
{
    size_t len = strlen(fileName) - 4; /* can't underflow */
    char *scratch = userData;

    /* len check is for paranoid reasons */
    if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)  //这里的意思就是说文件名应该是dev,通过dev文件获取设备的主设备号和次设备号。
        return FALSE;

    strcpy(scratch, fileName);  //把文件名存储到srcatch,它就是刚开始传进来的temp缓存区。
    scratch[len] = '\0';
    make_device(scratch, 0);   //创建设备节点。

    return TRUE;
}

/* Directory callback for /sys/ traversal */
static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
        struct stat *statbuf UNUSED_PARAM,
        void *userData UNUSED_PARAM,
        int depth)
{
    /* Extract device subsystem -- the name of the directory
     * under /sys/class/ */

    if (1 == depth) {
        free(subsystem);
        subsystem = strrchr(fileName, '/');
        if (subsystem)
            subsystem = xstrdup(subsystem + 1);  //获取子系统名字。在执行mdev.conf配置的命令时,作为环境变量:SUBSYSTEM=subsystem。
    }

    return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
}

enum {
    ACTION_RECURSE = (1 << 0),
    ACTION_FOLLOWLINKS = %3

阅读(1546) | 评论(0) | 转发(0) |
0

上一篇:Linux启动代码分析

下一篇:LINUX netlink机制

给主人留下些什么吧!~~