当前位置 博文首页 > lyndon_li的博客:UNIX 操作系统体系结构调整

    lyndon_li的博客:UNIX 操作系统体系结构调整

    作者:[db:作者] 时间:2021-06-10 18:15

    起因

    闲来无事时通常会打开 github/trending 看看当前大家热门的项目是什么,同时也希望看到自己感兴趣的项目,以此来提高自己。这天,突然看到了一个名为《程序员应该访问的最佳网站中文版》,里面有一 part 是《bash和bash脚本》,这种程序员+生产力相关内容是我最感兴趣的类目之一了,点进去浏览了一会。突然一个历史遗留的模糊问题又浮现在了我的脑海:shell 和 bash 是什么关系?。虽说之前也曾整理过这个问题,但是禁不住时间的洗礼,这不又得百度一下。搜到了《Shell是什么?1分钟理解Shell的概念!》,里面有句话:

    我们运行一个命令,大部分情况下 Shell 都会去调用内核暴露出来的接口,这就是在使用内核。接口其实就是一个一个的函数,使用内核就是调用这些函数。这就是使用内核的全部内容了吗?嗯,是的!除了函数,你没有别的途径使用内核。

    这句话对我来说太有用了。简直是盘古开天辟地,把我脑海中的混沌,一下辟成两半,清澈起来了。以至于我还哪管什么是 shell,什么是 bash,赶紧借这句话进一步理清一下内核、系统调用、shell 等之间的关系。

    man 手册

    从哪开始理呢?首先想到的是 man 手册,man 手册共分 8 部分,如下:

    1  Executable programs or shell commands
    2  System calls (functions provided by the kernel)
    3  Library calls (functions within program libraries)
    4  Special files (usually found in /dev)
    5  File formats and conventions eg /etc/passwd
    6  Games
    7  Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
    8  System administration commands (usually only for root)
    

    审视了十秒钟,感觉哪里不对劲,突然恍然大悟,怎么把 可执行程序和 shell 命令 放在了第一页,应该把 系统调用 放在第一页才符合从底层到上层的排列顺序嘛,怪不得我之前用了好久才记住 1 2 3 分别代表哪部分的内容,原来是不符合习惯呀(终于找到借口了)。为了确认自己的观点,打开了《UNIX环境高级编程第二版》,UNIX 操作系统的体系结构图了然如下:
    在这里插入图片描述
    结合这张图,所以我建议 man 手册 的前 3 部分排序改为如下:

    1  System calls (functions provided by the kernel)
    2  Library calls (functions within program libraries)
    3  Executable programs or shell commands
    

    即:

    1  系统调用(内核提供的函数接口)
    2  库函数 (建立在系统调用之上的公用函数库,应用软件既可以使用公用函数库,也可以使用系统调用)
    3  可执行程序或 shell 命令
    

    怀疑 UNIX 操作系统体系结构的准确性

    自己私自调整 man 手册 的前 3 部分排序后,心中不由窃喜。突然,又有一个细节引起了我的思考,为什么 可执行程序或 shell 命令 要排在 库函数 的后面,这样排序得依据 shell 命令必须使用了库函数 这个理由才行,再仔细看看上面那张 UNIX 的体系结构图,貌似 shell库函数 是平级的。并没有调用关系,难道事实真的是这样吗?

    源码验证

    下载 coreutils-5.0 源码。其实看 busybox 源码也一样,不过个人感觉 coreutils 更经典一些。

    coreutils 是GNU下的一个软件包,包含linux下的 ls 等常用命令。 —— 百度百科

    随便找一个,就看我们最熟悉的 ls 吧,打开 ls.c,部分代码如下(仅保留我们关心的部分):

    /* Read directory `name', and list the files in it.
       If `realname' is nonzero, print its name instead of `name';
       this is used for symbolic links to directories. */
    
    static void
    print_dir(const char *name, const char *realname)
    {
    ...
      dirp = opendir(name);
      if (!dirp)
      {
        error(0, errno, "%s", quotearg_colon(name));
        exit_status = 1;
        return;
      }
    
      if (LOOP_DETECT)
      {
        struct stat dir_stat;
        int fd = dirfd(dirp);
    
        /* If dirfd failed, endure the overhead of using stat.  */
        if ((0 <= fd
                 ? fstat(fd, &dir_stat)
                 : stat(name, &dir_stat)) < 0)
        {
          error(0, errno, _("cannot determine device and inode of %s"),
                quotearg_colon(name));
          exit_status = 1;
          return;
        }
    
    
        DEV_INO_PUSH(dir_stat.st_dev, dir_stat.st_ino);
      }
    
    ...
    
      if (files_index)
        print_current_files();
    }
    
    int
    main (int argc, char **argv)
    {
      ...
      while (pending_dirs)
      {
        thispend = pending_dirs;
        pending_dirs = pending_dirs->next;
    
        if (LOOP_DETECT)
        {
          if (thispend->name == NULL)
          {
            /* thispend->name == NULL means this is a marker entry
    		 indicating we've finished processing the directory.
    		 Use its dev/ino numbers to remove the corresponding
    		 entry from the active_dir_set hash table.  */
            struct dev_ino di = dev_ino_pop();
            struct dev_ino *found = hash_delete(active_dir_set, &di);
            /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
            assert(found);
            dev_ino_free(found);
            free_pending_ent(thispend);
            continue;
          }
        }
    
        print_dir(thispend->name, thispend->realname);
    
        free_pending_ent(thispend);
        print_dir_name = 1;
      }
      ...
      exit (exit_status);
    }
    

    可以看到,ls 命令的源码中,

    • 既包含 系统调用 ,如:stat()、fstat(),所在头文件 <sys/stat.h>
    • 又包含 库函数,如:opendir(),所在头文件 <dirent.h>

    UNIX 操作系统体系结构调整

    所以,我又发现了《UNIX 环境高级编程第二版》中的一处不合理的地方(个人认为)。遂将结构更改为如下:
    在这里插入图片描述

    下一篇:没有了