《Unix环境高级编程》ver3是一部非常不错的书籍,学习中遇到的问题和过程整理记录
实现ls命令实例
Unix——一切皆是文件,文件在Unix环境中是非常重要的东西,Unix文件系统就是一个虚拟层次结构,所有目录都挂载于/根目录,文件夹也可以被认为是一种文件,设备也是一种文件,重要的Socket套接字也是一种文件.
#include "include/apue.h"
#include <dirent.h>
int
main(int argc, char *argv[]){
DIR *dp;
struct dirent *dirp;
if (argc != 2)
err_quit("usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL)
err_sys("can`t open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL) {
printf("%s\n", dirp->d_name);
}
closedir(dp);
exit(0);
}
运行
cc main.c
报错
Undefined symbols for architecture x86_64:
"_err_quit", referenced from:
_main in main-5f8e4b.o
"_err_sys", referenced from:
_main in main-5f8e4b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
缺少err_quit和err_sys的二进制代码
编译器编译成为二进制代码分为四个步骤:
- 预处理
预处理过程只是展开宏定义,处理条件编译,展开include指令来插入实际代码和一些杂项工作。 - 编译
编译就是将预处理完成的代码进行词法分析、语义分析和代码优化,最终生成相应的汇编代码文件 - 汇编
然后就是汇编过程,将其翻译为二进制代码 - 链接
链接工作,使用链接器将各个目标文件组装到一起,解决符号依赖等问题,最终形成一个二进制文件,其中还分为链接动态库和静态库的不同。这些阶段都可以指定编译参数来让编译器只执行到某一阶段
> cc -E xxx.c #只执行预处理阶段
> cc -S xxx.c #只执行到编译阶段
> cc -c xxx.c #只执行到汇编阶段
从书的源代码里面找出来,然后使用下面命令将其编译为二进制文件,会生成error.o连接文件
cc -c error.c
使用ar -r liberror.a error.o 将其打包为静态库文件
把liberror.a放到lib文件夹下
把apue.h放到include文件夹下
使用编译命令cc -L./lib -lerror main.c 编译出二进制文件了。
最终生成a.out文件
- .o 文件
编译创建用来link的文件 - .a 文件
静态类库,已经编译好的库
完成步骤:
1. cc -c error.c 编译成link文件
2. ar -r liberror.a error.o 将error.o打包成静态库文件
3. cc -L./lib -lerror main.c 编译出 a.out 二进制执行文件
4. ./a.out ~/test 测试脚本
输出:
test1
test2
ttttt
opendir()函数:打开目录
头文件:
include <dirent.h>
定义函数:
DIR * opendir(const char * name);
函数说明:opendir()用来打开参数name 指定的目录, 并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值.
![](/qrcode.jpg)
返回值:成功则返回DIR* 型态的目录流, 打开失败则返回NULL.
错误代码:
1、EACCESS 权限不足。
2、EMFILE 已达到进程可同时打开的文件数上限。
3、ENFILE 已达到系统可同时打开的文件数上限。
4、ENOTDIR 参数name 非真正的目录。
5、ENOENT 参数name 指定的目录不存在, 或是参数name 为一空字符串。
6、ENOMEM 核心内存不足。
上述ls实例中,opendir函数返回指向DIR结构的指针,我们将该指针传递给readdir函数,然后,在循环中调用readdir来读取每个目录项。