Linux cat 命令源码介绍
发布时间:2021-12-11 12:02:33 所属栏目:教程 来源:互联网
导读:最近在读APUE, 边看还得边做才有效果. 正好Linux下很多命令的是开源的, 可以直接看源码. GNU coreutils 是个不错的选择. 源码包有我们最常用的 ls, cat等命令的源码, 每个命令都比较短小精悍, 适合阅读. 下面是我阅读 cat 命令的一点笔记. 到这里下载源码.
最近在读APUE, 边看还得边做才有效果. 正好Linux下很多命令的是开源的, 可以直接看源码. GNU coreutils 是个不错的选择. 源码包有我们最常用的 ls, cat等命令的源码, 每个命令都比较短小精悍, 适合阅读. 下面是我阅读 cat 命令的一点笔记. 到这里下载源码. 在源码根目录下 ./configure; make 就可以直接编译, 修改后make就可以编译了. 命令源码在 src/目录中, lib/目录下有一些用到的辅助函数和常量定义. 1. 命令行解析 基本上所有的Linux命令都是用getopt函数来解析命令行参数的, cat也不例外, cat使用的是getopt_long函数, 以便解析长参数, 用一些bool变量来存储选项值. 没什么好说的. 2. 检测输入输出文件是否相同 例如 cat test.txt > test.txt 的情况, 输入输出文件相同, 这是不合法的. cat 的输入流由命令行给定, 默认是标准输入(stdin), 输出流是标准输出(stdout). 所以用字符串比较的方法是无法判断输入输出是否是相同. 另外对于一些特殊的文件, 如tty, 我们是允许其输入输出相同的, 如 cat /dev/tty > /dev/tty 是合法的. cat采取的方式是对与regular file, 检测设备编号和i-node是否相同. 忽略对非regular file的检测. 这部分的代码如下: 获得文件属性. if (fstat (STDOUT_FILENO, &stat_buf) < 0) error (EXIT_FAILURE, errno, _("standard output")); 提取文件设备编号和i-node. 对于非 regular 类型的文件, 忽视检测. if (S_ISREG (stat_buf.st_mode)) { out_dev = stat_buf.st_dev; out_ino = stat_buf.st_ino; } else { check_redirection = false; } 进行检查. check_redirection为false就不检查. if (fstat (input_desc, &stat_buf) < 0)<span style="white-space:pre"> </span>// input_desc为输入文件描述符 { error (0, errno, "%s", infile); ok = false; goto contin; } if (check_redirection && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino && (input_desc != STDIN_FILENO)) { error (0, 0, _("%s: input file is output file"), infile); ok = false; goto contin; } Tips: '-' 表示的是标准输入, 如 cat - 命令实际是从标准输入读取字节. 所以cat可以配合管道命令这样用: echo abcd | cat file1 - file2. 只输入 cat 命令默认就是从标准输入读取字节. 3. 一次读写的字节数目 cat是以read, write函数为基础实现的, 一次读写的字节数的多少也影响了程序的性能. insize 和 outsize 变量分别表示一次读和写的字节数目. insize = io_blksize (stat_buf); enum { IO_BUFSIZE = 128*1024 }; static inline size_t io_blksize (struct stat sb) { return MAX (IO_BUFSIZE, ST_BLKSIZE (sb));<span style="white-space:pre"> </span>/* ST_BLKSIZE( )宏的值视系统而定, 在lib/stat-size.h中定义 */ } outsize值的设定类似insize. 4. simple_cat 如 cat 命令不使用任何格式参数, 如 -v, -t. 那么就调用simple_cat来完成操作, simple_cat的优点是速度快, 因为它在某些系统上有可能是以二进制方式读写文件. 参考 man 3 freopen. if (! (number || show_ends || squeeze_blank)) { file_open_mode |= O_BINARY;<span style="white-space:pre"> </span>/* 在linux下O_BINARY为0, 没有任何效果, 但有些系统是表示二进制形式打开文件 */ if (O_BINARY && ! isatty (STDOUT_FILENO)) /* 调用 freopen, 包含错误处理, 将输出流的mode改为"wb" */ xfreopen (NULL, "wb", stdout); } 无任何格式参数, 则调用simple_cat if (! (number || show_ends || show_nonprinting || show_tabs || squeeze_blank)) { insize = MAX (insize, outsize); /* xzz 分配内存, 失败则调用 xmalloc-die() 终止程序并报告错误 */ inbuf = xmalloc (insize + page_size - 1); ok &= simple_cat (<strong>ptr_align</strong> (inbuf, page_size), insize); } ptr_align是一个辅助函数. 因为IO操作一次读取一页, ptr_align是使得缓冲数组的起始地址为也大小的整数倍, 以增加IO的效率. static inline void * ptr_align (void const *ptr, size_t alignment) { char const *p0 = ptr; char const *p1 = p0 + alignment - 1; return (void *) (p1 - (size_t) p1 % alignment); } simple_cat函数很简单 static bool simple_cat ( /* Pointer to the buffer, used by reads and writes. */ char *buf, /* Number of characters preferably read or written by each read and write call. */ size_t bufsize) { /* Actual number of characters read, and therefore written. */ size_t n_read; /* Loop until the end of the file. */ while (true) { /* Read a block of input. */ /* 普通的read可能被信号中断 */ n_read = safe_read (input_desc, buf, bufsize); if (n_read == SAFE_READ_ERROR) { error (0, errno, "%s", infile); return false; } /* End of this file? */ if (n_read == 0) return true; /* Write this block out. */ { /* The following is ok, since we know that 0 < n_read. */ size_t n = n_read; /* full_write 和 safe_read都调用的是 safe_sw, 用宏实现的, * 查看 safe_write.c 就可以发现其实现的关键. */ if (full_write (STDOUT_FILENO, buf, n) != n) error (EXIT_FAILURE, errno, _("write error")); } } } (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |