前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux系统读取目录内文件顺序

Linux系统读取目录内文件顺序

作者头像
书唐瑞
发布2022-06-02 15:03:25
3.5K0
发布2022-06-02 15:03:25
举报
文章被收录于专栏:Netty历险记

在上一篇应用依赖不同的Netty版本引发的错误文章中, 在WEB-INF/lib目录下存在多个版本的Netty, 应用加载jar包的顺序颠倒, 导致应用启动报错. 而重点就在于加载jar包顺序.

本篇文章, 我们简单验证下, 在Linux系统中, 读取目录下的文件, 它的顺序是怎样的.

我们还是拿上篇文章使用的实验环境

当然我这里增加了2个文件, read_dir.c 和 read_dir.py . C程序和Python程序, 接下来会使用这2个程序分别验证下在Linux系统中, 读取目录下文件的顺序是怎样的.

代码还是上一篇文章中的Example.java代码, 提前先类加载SingleThreadEventExecutor类, 然后在启动一个Netty服务端.

编译&运行Example.java代码

代码语言:javascript
复制
// Example.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class Example {

    public static void main(String[] args) throws Exception {
        Class.forName("io.netty.util.concurrent.SingleThreadEventExecutor");

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(8);
        EventLoopGroup businessGroup = new NioEventLoopGroup(8);

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        try {

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)                    
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) {
                            ChannelPipeline channelPipeline = ch.pipeline();

                        }
                    });

            ChannelFuture channelFuture = serverBootstrap.bind("127.0.0.1", 8080).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

如下图, 程序正常运行

既然能正常运行, 表明程序是先加载的netty-all-4.1.43.Final.jar.

使用ll命令查看目录下的文件时, 如下图

netty-all-4.1.43.Final.jar的确排在netty-common-4.1.29.Final.jar前面, 那是因为它默认是按照字母排序的, 这个依据在man手册中可以查找到, 如下

man ls

描述中已经说明, ls默认按照字母次序排序文件

如果使用ll -r 查看目录内容, 又会看到另一种排序结果, 如下图, netty-common-4.1.29.Final.jar排在netty-all-4.1.43.Final.jar前面了

那么我们平时写的Java程序, 在加载某个目录下的Jar文件时, 比如Tomcat读取WEB-INF/lib目录下的jar文件时, 先读取哪个后读取哪个总该有个顺序吧, 它的底层不会像ls命令排序那样的, 那么它的底层是依据什么呢? 往下看

这里写了一个C程序(read_dir.c), 它的功能就是读取当前目录下的文件

代码语言:javascript
复制
// read_dir.c

#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct linux_dirent {
   unsigned long  d_ino;
   off_t          d_off;
   unsigned short d_reclen;
   char           d_name[];
};

#define BUF_SIZE 1024

int
main(int argc, char *argv[])
{
   int fd;
   long nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for (;;) {
       // 调用系统函数getdents()
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       printf("--------------- nread=%ld ---------------\n", nread);
       printf("inode#              file type    d_reclen    d_off   d_name\n");
       for (long bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           printf("%18ld  ", d->d_ino);
           d_type = *(buf + bpos + d->d_reclen - 1);
           printf("%-10s ", (d_type == DT_REG) ?  "regular" :
                            (d_type == DT_DIR) ?  "directory" :
                            (d_type == DT_FIFO) ? "FIFO" :
                            (d_type == DT_SOCK) ? "socket" :
                            (d_type == DT_LNK) ?  "symlink" :
                            (d_type == DT_BLK) ?  "block dev" :
                            (d_type == DT_CHR) ?  "char dev" : "???");
           printf("%4d %10jd       %s\n", d->d_reclen,
                   (intmax_t) d->d_off, d->d_name);
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

编译这个C程序

gcc -o read_dir read_dir.c

执行生成的read_dir, 输出结果如下

【第一列inode】在Linux文件系统中, 标识一个文件并不是根据它的名称, 而是根据这个inode值. 不同文件的inode值不同.

比如在tmp目录下有三个文件,分别是-not,1.txt,2.txt

如果要删除1.txt , 可以使用rm 1.txt把文件删除掉. 但是当使用rm -not删除-not文件时, 它就会提示错误

rm 命令会把中划线-后面当成命令参数, 而rm没有-n的命令参数,因此报错了. 这个时候我们就可以使用inode值删除文件.查看文件的inode值

-not文件的inode值是5018049, 于是使用rm `find . -inum 5018049`;命令就可以删除-not文件.

【第二列file type】表示文件类型

【第三列d_reclen】表示文件长度

【第四列d_off】可以理解成这个文件在目录中的偏移, 具体含义在它的结构体中有说明, 上面输出的每行记录都使用下面的结构体表示

【第五列d_name】表示文件名

而我们读取目录下的文件就是根据d_off值排序的.

我们再次使用Python语言程序验证下

代码语言:javascript
复制
#! /usr/bin/env python

import os

r = os.listdir(".")
print(r)

输出的结果与C程序一致, 毕竟Python语言底层也是调用相同的C库函数.

我们在另一台Linux机器上验证下

程序启动报错了, 表明程序先加载的netty-common-4.1.29.Final.jar, 我们使用C程序验证下.

如上图, 根据d_off排序, netty-common-4.1.29.Final.jar的确排在netty-all-4.1.43.Final.jar前面, 所以应用启动报错了.

我们简单写个Java程序读取当前目录, 看一下Java程序读取的目录中的文件列表是否与上面一致.

代码语言:javascript
复制
import java.io.File;

public class ReadDir {
    public static void main(String[] args) throws Exception {
        File file = new File("/root/2022-3-16");
        String[] files = file.list();
        for (String fileName : files) {
            System.out.println(fileName);
        }
    }
}

编译&运行

输出的结果与C程序一样, 毕竟Java程序底层也需要依赖C库函数.

对应的底层系统调用API是getdents

可以参考

https://man7.org/linux/man-pages/man2/getdents.2.html

或man getdents 查看下相关的介绍

附录: 本篇文章的实验代码地址

https://github.com/infuq/infuq-others/tree/master/Lab/2022-3-16

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Netty历险记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档