0%

Linux系统编程笔记

库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些直接拿来用的变量、函数或类。库不能单独运行。

  • 静态库:在程序链接阶段被复制到程序中
  • 动态库:在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用

库的好处:

  1. 代码保密
  2. 方便部署和分发

静态库

命名规则:

  • Linux:libxxx.a
    • lib: 前缀(固定)
    • xxx:库的名字,自己起
    • .a:后缀(固定)
  • windows:libxxx.lib

静态库的制作:

  • gcc获得.o文件
  • 将.o文件打包,使用ar工具(archive)
    • ar rcs libxxx.a .o xxx.o xxx.o
    • r - 将文件插入备存文件中
    • c - 建立备存文件
    • s - 索引

动态库

命名规则:

  • Linux:libxxx.so
  • windows: libxxx.dll

动态库的制作:

  • gcc -c -fpic/fPIC 获得.o 文件
  • gcc -shared *.o -o libxxx.so

程序启动后,动态库会被动态加载到内存中,通过ldd(list dynamic dependencies)命令检查动态库依赖关系。

解决动态库加载失败的问题

  1. 修改DT_RPATH ,临时的
  2. 修改环境变量LD_LIBRARY_PATH
  3. 修改/etc/ld.so.cache文件列表
  4. 修改/lib/, /usr/lib

静态库和动态库的对比

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled.png

静态库、动态库区别来自链接阶段如何处理,分别称为静态链接方式和动态链接方式

静态库的优缺点

  • 优点:
    • 静态库打包到应用程序中加载速度快
    • 发布程序无需提供静态库,移植方便
  • 缺点:
    • 消耗系统资源,浪费内存
    • 更新、部署、发布麻烦

动态库的优缺点

  • 优点:
    • 可以实现进程间资源共享(共享库)
    • 更新、部署、发布简单
    • 可以控制何时加载动态库
  • 缺点:
    • 加载速度比静态库慢
    • 发布程序时需要提供依赖的动态库

Makefile

Makefile规则

  • 一个 Makefile 文件中可以有一个或者多个规则

    目标…:依赖…

    命令(shell命令)

    • 目标:最终要生成的文件(伪目标除外)
    • 依赖:生成目标所需要的文件或是目标
    • 命令:通过执行命令对依赖操作生成目标(命令前必须 Tab 缩进)
  • Makefile 中的其它规则一般都是为第一条规则服务的。

工作原理

  • 命令在执行之前,需要先检查规则中的依赖是否存在
    • 如果存在,执行命令
    • 如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,如果找到了,则执行该规则中的命令
  • 检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
    • 如果依赖的时间比目标的时间晚,需要重新生成目标
    • 如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%201.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%202.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%203.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%204.png

GDB

  • 通常,在为调试而编译时,我们会()关掉编译器的优化选项(-O), 并打开调试选项(-g)。另外,-Wall在尽量不影响程序行为的情况下选项打开所有warning,也可以发现许多问题,避免一些不必要的 BUG。
  • gcc -g -Wall program.c -o program
  • -g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%205.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%206.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%207.png

标准C库函数和Linux系统函数

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%208.png

标准C库IO和Linux系统IO的关系

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%209.png

虚拟地址空间

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%2010.png

文件描述符 File Descriptor

内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%2011.png

Linux 系统IO函数

  • int open(const char *pathname, int flags);
    • 打开文件
    • flags 如 O_CREAT,意思是没有即创建
  • int open(const char *pathname, int flags, mode_t mode);
    • 打开文件,并设置模式
    • mode 可以是 O_RDONLY, O_WRONLY, O_RDWR
  • int close(int fd);
    • 关闭文件描述符
  • ssize_t read(int fd, void *buf, size_t count);
  • ssize_t write(int fd, const void *buf, size_t count);
  • off_t lseek(int fd, off_t offset, int whence);
    • 重定位文件偏移量
    • SEEK_SET:设置offset
    • SEEK_CUR:设置offset为当前加上传的参数offset
    • SEEK_END:设置offset为文件末端加上传的参数offset(记得之后要写入一些字符才能更新)
  • int stat(const char *pathname, struct stat *statbuf);
    • 得到文件状态信息
  • int lstat(const char *pathname, struct stat *statbuf);
    • 得到软连接文件状态信息

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%2012.png

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%2013.png

文件属性操作函数

  • int access(const char *pathname, int mode);
    • 判断文件mode
  • int chmod(const char *filename, int mode);
  • int chown(const char *path, uid_t owner, gid_t group);
  • int truncate(const char *path, off_t length);
    • 截断或延长文件长度

目录操作函数

  • int rename(const char *oldpath, const char *newpath);
  • int chdir(const char *path);
  • char *getcwd(char *buf, size_t size);
  • int mkdir(const char *pathname, mode_t mode);
  • int rmdir(const char *pathname);

目录遍历函数

  • DIR *opendir(const char *name);
    • 返回目录流
  • struct dirent *readdir(DIR *dirp);
    • 返回direct结构体,目录流文件的信息
  • int closedir(DIR *dirp);

Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%201965346d57a64af3beb11244f2eac5b2/Untitled%2014.png

dup, dup2 函数

  • int dup(int oldfd);
    • 复制文件描述符
  • int dup2(int oldfd, int newfd);
    • 重定向文件描述符

fcntl函数

  • int fcntl(int fd, int cmd, … /* arg */ );
    • 复制文件描述符
    • 设置/获取文件的状态标志