Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用

文章目录

  • 系统调用I/O接口
    • C文件操作
    • Linux文件I/O介绍
    • FD文件描述符-file descriptor
    • 语言层的FILE
    • 重定向、语言缓冲区、内核缓冲区
    • dup2 系统调用
  • 文件系统
    • inode(索引节点)
    • 软硬链接
  • 动、静态库的制作与使用
    • 静态库
    • 动态库
  • 总结与参考

全文约 11560 字,预计阅读时长: 33分钟


系统调用I/O接口

  至今所学的文件操作,无外乎读取和写入;而文件包含:文件本身所具有的属性,和文件的内容。而对文件的操作,都是进程对文件的操作。语言层库函数对硬件的操作必须经过OS的系统调用。

  几乎所有的语言层面都默认打开这些接口,因为都有输入输出的需求,便于语言刚开始的学习上手使用;否则会是这样的场景:在学习这门语言之前呢,铁子们先来学一下打开文件…

  • 库函数 vs 系统调用接口:
    • fopen --> open
    • fclose --> close
    • fread --> read
    • fwrite --> write
  • 查看进程属性的当前路径:每个进程都有一个内置的属性cwd
ps axj | greap t1
ll /proc/t1ID

C文件操作

  • C 库函数 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的数组中的数据写入到给定流 stream 中。
    • 参数:你期望写入多少个 size,从哪个数组写入到哪个 文件 里。
    • 返回值:实际上写入了几个size;
  • 一般 size 设置大一点,count 设置小一点。
char * buf[1024];
int wf = fwrite(buf,sizeof(buf),1,stdout);  
  • 打开文件的 w 写入 :每次写入都是重新写入;意味着之前的文件内容,会被清空。打开文件的 a 写入:追加写入,不清空原始文件内容,在文件最后进行写入,数据增多的过程。
  • C语言默认打开三个文件流:stdin键盘文件stdout / stderr显示器文件。这三个流的类型都是FILE*, fopen返回值类型,文件指针。
  • C 库函数 int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中。如果成功,则返回写入的字符总数,否则返回一个负数。
fprintf(stdout,"%d , %c\n",24,a);

Linux文件I/O介绍

  • open 函数说明:
(1) int open(const char *pathname, int flags);
(2) int open(const char *pathname, int flags, mode_t mode);
  • 返回值:打开文件成功,那么返回文件描述符 fd (file descriptor),值大于或等于0;失败,-1。
  • 参数:
    • pathname 是文件路径,可以是相对路径(即不以 “/” 开头),也可以是绝对路径(即以 “/” 开头)。
    • flags:宏标志位,使用时包含一种访问模式: O_RDONLY (只读)、O_ WRONLY (只写)或 O_RDWR(读写)。
    • mode:文件的权限设置。当参数 flags 指定标志位 O_CREATO_TMPFILE 的时候,必须指定参数 mode。可以用八进制文件掩码设置。
  • 当文件不存在时,O_CREAT 与上述三种任意一种访问模式进行 ‘或’ 运算.。 flags:宏标志位,这些参数只占一个int整形中的一个比特位。读:00;写:01;…
int fd = open("t1.txt",O_ WRONLY|O_CREAT,0644);
if(fd<0)
{
	perroe("open");
	return 1;
}
close(fd)  
  • write:函数说明:ssize_t write(int fd, const void *buf, size_t count);
    • buf 写入哪个 fd 的文件,写多少个字节进去。
int fd = open("t1.txt",O_ WRONLY|O_CREAT,0644);
if(fd<0)
{
	perroe("open");
	return 1;
}

const char* msg  = "hello write\n";
write(fd,msg,strlen(msg));

close(fd);
  • read函数:ssize_t read(int fd, void *buf, size_t count);
    • 从fd文件读取到buf里,读取多大字节
    • 因文件不按字符串的规定,所以读取时,应读 buf大小-1个的字节。最后一位设置成 \0 .
    • 返回值:读取到的字节大小。返回0,则表示读到了文件末尾。
char buf[1024];
ssize_t s = read(fd,buf,sizeof(buf)-1)
if(s>0)
{
	buf[s]='\0';
	printf("%s\n",buf);
}
  • O_APPEND:追加写入
int fd = open(t1.txt,O_WRONLY|O_APPEND);
  • 系统调用接口要兼容自身语法特性,使用成本较高,且不具备可移植性。C语言会自动根据平台,选择自己底层对应的文件接口。

FD文件描述符-file descriptor

  fd 是连续的整数;输入输出错误占了0,1,2。操作系统会把进程打开的多个文件管理起来。文件本身是一个 struct file的结构,内含有读写方法,和内核缓冲区。OS则用打开的文件指针通过struct file_struct内的一个文件指针数组的方式进行管理。

  用户层看到的fd,其实是系统中维护进程和文件对应关系的数组下标,也就是说进程是用PCB里的一个指针这样的接口进行所有的文件操作。缓冲区里的数据通过文件描述符写入对应的目标文件中。
Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第1张图片

  FD的使用规则:找到当前没有被使用的最小的一个下标,作为新的文件描述符。 close 现在的含义代表: 断开 struct file_struct里对应下标的文件指针指向,随后那块文件资源由OS接受处理,释放资源或加入闲置队列。

  所有语言层面的默认打开文件,都是底层系统支持的,默认一个进程运行的时候,就打开了0,1,2.

/* open file information */
	struct files_struct *files; 

语言层的FILE

  • FILE 是一个结构体,内部含有:底层对应的文件描述符下标;应用层,C语言提供的缓冲区数据
 //缓冲区相关
 /* The following pointers correspond to the C++ streambuf protocol. */
 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
 char* _IO_read_ptr; /* Current read pointer */
 char* _IO_read_end; /* End of get area. */
 char* _IO_read_base; /* Start of putback+get area. */
....
 int _fileno; //封装的文件描述符
  • 通过访问成员变量查看fd
printf("%d\n",stdin->_fileno);
...

重定向、语言缓冲区、内核缓冲区

  • 假如关闭 stdout 的fd会发生什么?
int main()
{
	close(1);
	int fd =open("t1.txt",O_CREAT|O_WRONLY,0644);
	..
	printf("hello close(%d)\n",fd);
	fflush(1);//需要强刷一下
	return 0;
}
  • 语言层面fd还是1,内核层面arr[1]的内容指向了新开的 t1.txt 的文件结构体,因此 printf 输出到了 新文件的缓冲区里。这就叫做输出重定向。
  • 为什么有\n不刷新到显示器上呢?
    • 因为普通文件的缓冲区刷新策略是全缓冲,显示器文件:'\n’行刷新。
  • 需要知道重定向和程序替换互不影响;替换只是替换的代码和数据,没有改变文件操作的对应fd。

语言缓冲区 vs 内核缓冲区

printf("hell C buffer !\n");
fprintf(stdout,"hello fprintf\n");
fputs("hello fputs\n",stdout);

const char* msg = "hello write\n";
write(1,msg,strlen(msg));

fork();
//假如 ./t1 > log.txt
//则父子进程都会打印语言区的缓冲区数据。内核缓冲区的只刷新一次。

  内核缓冲区向显示器文件写入,行刷新;父子进程像普通文件写入,全缓冲刷新;临近进程退出时,父子进程都会把缓冲区的数据刷新出去,触发了写实拷贝的现象。


dup2 系统调用

  因为手动关闭stdout,太挫了,所以系统提供了重定向接口函数:int dup2(int oldfd, int newfd);

  当调用dup2(int oldfd,int newfd)之后,若newfd原来已经打开了一个文件,则先关闭这个文件,然后newfd和oldfd指向了相同的文件;若newfd原来没有打开一个文件,则newfd直接指向和oldfd指向相同的文件。
关于dup2()函数

int fd = open("t1.txt",O_WRONLY);
dup2(fd,1);
....
显示器文件就指向了 arr[1] ---> struct file t1.txt

Linux下一切皆文件

  对于OS而言,把硬件抽象出来,用一样的名字表示;打开的文件都会创建一个struct_file的数据结构,通过双向链表链接起来…。为什么数据不直接拷贝进内核?有权限切换等备工作,花费资源和时间;相反直接放在用户层缓冲区,解耦合提高效率。所有对数据的操作必须先加载到内存,cpu处理,通过OS,刷新到外设上。


文件系统

  10几年前,是机械硬盘的时代,虽然现在是SSD,速度快了一个数量级;但在cpu面前是很慢。所以书上还是以机械硬盘为例。机械硬盘的一个扇区是512Byte。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。“块"的大小,最常见的是4KB,即连续八个 sector(扇区)组成一个 block。

  进程向外设写数据时,是等内存的一个页框(站在OS角度看待内存的使用单位 4KB)写满了,OS在联系对应的外设驱动将数据刷新出去。
Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第2张图片

  • 进程和放在硬盘里普通文件通过缓冲区建立关系。OS对进程有管理方法,也需要对硬盘上的诸多文件进行管理。
    • 由于外设型号发展太快,OS不能对特定型号的外设定制特有的读写方式,那样太低效了,没有可扩展性和兼容性。
    • 于是计算机将硬盘抽象一层,把它看做一个有着巨大容量的线性空间数组,数组的每个单位是512字节;我OS不管你是什么磁道,扇区,盘面什么的,只负责向这样的数组内写入,这样的映射关系由磁盘驱动维护。
  • 面对这样大的空间,计算机提供了文件管理系统,文件系统和操作系统可以看作一种“合作”关系,而数据库管理系统则是依赖于操作系统的一种应用
    • 文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,
    • 文件管理系统由系统实用程序组成,它们可作为具有特权的应用程序来运行。
    • 文件系统允许用户创建称为文件的数据集,会把正对文件的读写转换为针对设备原始扇区的读写。

严格地说,文件系统是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型(Abstract data type)多个扇区合并在一起称为块儿组group block,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。


inode(索引节点)

Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第3张图片
   Linux 下支持多种文件系统。将磁盘这样的块设备抽象为线性的空间,管理好一个区,其他区就是一样的管理方式。

超级块(Super Block):存放文件系统本身的结构信息。包括 inode 和 block 的总量、已经使用量和剩余量,以及文件系统的格式和相关信息等。

blocks:多个4KB(扇区*8)大小的集合,保存的都是特定文件的内容

inode Table:包含了文件系统的所有文件列表。一个inode(节点)是在一个表项里,包含文件数据(元数据)。每个文文件有自己的一个inode(结构体)

BlockBitmap:假设有10000+个blocks,10000+比特位:比特位和特定的block是一一对应的,其中比特位为1,代表该block被占用,否则表示可用

inode Bitmap:假设有10000+个inode结点,就有10000+个比特位,比特位和特定的inode是一一对应的,其中比特位为1表示inode被占用,否则表示可用。

Group Descriptor Table(GDT):快组描述符,这个快组多大,已经使用多少了,有多少个inode,已经占用了多少个,还剩多少,一共有多少个block,使用了多少……

这些都是能够让一共文件的信息可追溯,可管理 格式化

inode Table

Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第4张图片

inode

  通常情况下,文件系统会将文件的实际内容和属性分开存放:不仅如此,inode 中还记录着文件数据所在 block 块的编号; 文件的实际内容保存在 block 中(数据块),类似衣柜的隔断,用来真正保存衣物。每个 block都有属于自己的编号。当文件太大时,可能会占用多个 block 块。

  • inode的结构体定义如下
struct inode {
        struct hlist_node       i_hash;              /* 哈希表 */
        struct list_head        i_list;              /* 索引节点链表 */
        struct list_head        i_dentry;            /* 目录项链表 */
        unsigned long           i_ino;               /* 节点号 */
        atomic_t                i_count;             /* 引用记数 */
        umode_t                 i_mode;              /* 访问权限控制 */
        unsigned int            i_nlink;             /* 硬链接数 */
        uid_t                   i_uid;               /* 使用者id */
        gid_t                   i_gid;               /* 使用者id组 */
        kdev_t                  i_rdev;              /* 实设备标识符 */
        loff_t                  i_size;              /* 以字节为单位的文件大小 */
        struct timespec         i_atime;             /* 最后访问时间 */
        struct timespec         i_mtime;             /* 最后修改(modify)时间 */
        struct timespec         i_ctime;             /* 最后改变(change)时间 */
        unsigned int            i_blkbits;           /* 以位为单位的块大小 */
        unsigned long           i_blksize;           /* 以字节为单位的块大小 */
        unsigned long           i_version;           /* 版本号 */
        unsigned long           i_blocks;            /* 文件的块数 */
        unsigned short          i_bytes;             /* 使用的字节数 */
        spinlock_t              i_lock;              /* 自旋锁 */
        struct rw_semaphore     i_alloc_sem;         /* 索引节点信号量 */
        struct inode_operations *i_op;               /* 索引节点操作表 */
        struct file_operations  *i_fop;              /* 默认的索引节点操作 */
        struct super_block      *i_sb;               /* 相关的超级块 */
        struct file_lock        *i_flock;            /* 文件锁链表 */
        struct address_space    *i_mapping;          /* 相关的地址映射 */
        struct address_space    i_data;              /* 设备地址映射 */
        struct dquot            *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */
        struct list_head        i_devices;           /* 块设备链表 */
        struct pipe_inode_info  *i_pipe;             /* 管道信息 */
        struct block_device     *i_bdev;             /* 块设备驱动 */
        unsigned long           i_dnotify_mask;      /* 目录通知掩码 */
        struct dnotify_struct   *i_dnotify;          /* 目录通知 */
        unsigned long           i_state;             /* 状态标志 */
        unsigned long           dirtied_when;        /* 首次修改时间 */
        unsigned int            i_flags;             /* 文件系统标志 */
        unsigned char           i_sock;              /* 可能是个套接字吧 */
        atomic_t                i_writecount;        /* 写者记数 */
        void                    *i_security;         /* 安全模块 */
        __u32                   i_generation;        /* 索引节点版本号 */
        union {
                void            *generic_ip;         /* 文件特殊信息 */
        } u;
};

  如图所示:文件系统先格式化出 inode 和 block 块,假设某文件的权限和属性信息存放到 inode 4 号位置,这个 inode 记录了实际存储文件数据的 block 号有 4 个,分别为 2、7、13、15,由此,操作系统就能快速地找到文件数据的存储位置。
Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第5张图片

  • 看到更多关于文件的信息:stat
[root@localhost linux]# stat test.c
 File: "test.c"
 Size: 654 Blocks: 8  IO Block: 4096    //8个扇区
Device: 802h/2050d Inode: 263715 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-09-13 14:56:57.059012947 +0800
Modify: 2017-09-13 14:56:40.067012944 +0800
Change: 2017-09-13 14:56:40.069012948 +0800

  文件的三个时间:Access 最后访问时间。有时候看到访问了时间没更新,是因为内核提升效率,降低访问时间的更新频率。Modify 文件内容最后修改时间;Change 属性最后修改时间。文件的内容修改影响文件属性的修改。修改属性不一定影响内容时间。

寻找当前目录

  OS怎么找到当前目录的呢?系统也是一级一级找的,第一次找到后会把路径缓存起来;大多数OS目录下,不允许存在相同的文件名,大多数文件系统也是相互借鉴的;文件名作为K值索引对应的 inode ,进而可以找到对应的文件内容。

打开文件

  表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:实际上,系统内部这个过程分成三步:首先,目录树是提前加载到内存的。系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

删除文件

  删除一个文件做了什么呢?清楚文件两个位图的信息,1变0,再把数据块儿中的映射关系去掉。创建文件时,则是块儿组里在 inode 位图中,找一个位置,在 inode 表中分配一个 inode 保存文件属性信息;在数据块位图中找到一块儿空间,分配相应的数据块儿。如果是空目录,就不用分配数据块儿。

  联系平时实践,大家格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。


软硬链接

软连接

  • 软链接的文件,有自己的独立 inode 编号,数据块中保存的是文件的路径加文件本身。类似于windows 中的快捷方式;

一方面避免了直接把可执行程序文件暴露在外面;一方面便捷了在外层使用执行程序,避免路径太深。

[saul@VM-12-7-centos tt730]$ ln -s t1.x t1.soft
[saul@VM-12-7-centos tt730]$ ls -l
total 0
lrwxrwxrwx 1 saul saul 4 Jul 31 20:48 t1.soft -> t1.x  
-rw-rw-r-- 1 saul saul 0 Jul 31 20:47 t1.x

硬链接

  硬链接的文件,没有自己独立的 inode 编号;是在同一个文件inode编号的block下创建一个:与源文件属性和内容相同的新文件名和 inode 之间的映射关系。类似对文件的引用

当前路径下的 . 便是一个硬链接 ,所以看到刚创建一个目录,显示的硬链接数就是2。

[saul@VM-12-7-centos tt730]$ touch t1.x
[saul@VM-12-7-centos tt730]$ ll
total 0
-rw-rw-r-- 1 saul saul 0 Jul 31 20:54 t1.x   //没有硬链接时  1
[saul@VM-12-7-centos tt730]$ ln t1.x t1.hard
[saul@VM-12-7-centos tt730]$ ll
total 0
-rw-rw-r-- 2 saul saul 0 Jul 31 20:54 t1.hard  //硬连接数都改变了
-rw-rw-r-- 2 saul saul 0 Jul 31 20:54 t1.x
[saul@VM-12-7-centos tt730]$ rm t1.x
[saul@VM-12-7-centos tt730]$ ll
total 0
-rw-rw-r-- 1 saul saul 0 Jul 31 20:54 t1.hard  //硬连接数减一..

动、静态库的制作与使用

使用别人顶尖工程师的代码为了提高开发效率和鲁棒性。使用别人功能的三个途径:库、开源代码、基本的网络功能调用。

  • 动态库(.so):动态链接用动态库。程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 静态库静态库(.a):静态链接使用静态库。程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静
    态库

一般为更好地支持开发,第三方或者语言库,都必须提供两个库,一个静态库,一个动态库,方便程序员根据需要进行bin的生成。

库的命名

  • libc.so 取消前缀lib,去掉 . 之后的内容,剩下的就是库的名字。Linux下有动态库优先使用动态库,没动有静用静。

库的搜索路径:从左到右搜索-L指定的目录。由环境变量指定的目录 (LIBRARY_PATH)。由系统指定的目录。

ls /user/include/...
ls /lib64/libc*...//查看C库。

静态库

  • 多个头文件:方法的声明,注意防止重复包含;多个库文件:每个编译到目标文件时停止。 将多个库文件归档打包。

使用时应说明:头文件路径、库文件路径、库文件名:gcc -t1.c -0 t1 -I . -L . -lmytest; -I 指定头文件路径;-L 指定库路径;-l(小写L) 指定库名。静态链接:进程地址空间的代码区就包含了静态库的代码,用不着共享区。

//方法声明和实现
 #pragma once//add.h
 int add(int x ,int y);
#include "add.h"//add.c
 int add(int a, int b)
 {
 return a + b;
 }
 ----
 #pragma once//sub.h
 int sub(int a, int b);
 #include "sub.h"//sub.c
 int sub(int a, int b)
 {
 return a - b;
 }
 ----//编译
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
----//库文件归档打包
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
----//查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a 
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o	//t:列出静态库中的文件
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o	//v:verbose 详细信息
----//使用时
#include 
 #include "add.h"
 #include "sub.h"
 
 int main( void )
 {
 int a = 10;
 int b = 20;
 printf("add(10, 20)=%d\n", a, b, add(a, b));
 a = 100;
 b = 20;
 printf("sub(%d,%d)=%d\n", a, b, sub(a, b));
 }
----//运行 main。c
[root@localhost linux]# gcc main.c -o main -I . -L . -lmymath  /-l跟库名即可

C语言编译的时候,没用这么明显的选项,是因为:

  库文件和头文件在默认路径下能找到;gcc编译C代码时,默认就应链接 libc;

  自己的文件不加选项,需要将头文件和库文件拷贝到默认路径下—这也叫做库的安装。还可以更改内核级的配置文件。拷贝自己库文件或更改配置文件都容易污染官方的库文件。


动态库

  • shared: 表示生成共享库格式;合并多个库文件生成动态库时 gcc -shared -o libtest.so *.o
  • fPIC:产生位置无关码(position independent code)
    • 不论进程共享区的代码的某一部分映射到C库,或者物理内存到虚拟地址怎么映射,一定保证库中产生的代码,与你这个库加载到内存当中的位置和映射到内存共享区的任何区域,是没有关系的。jump能链接上这个库。
  • makefile 或者库文件编译至目标文件时的选项:gcc -fPIC -c add.c
    • 库名规则:libxxx.so

  gcc 编译使用库时和静态库的使用方法相同…。但解决运行时路径问题,即编译器知道了头文件的位置、库文件的位置,但运行时系统加载器不知道 .so 动态库的路径。 ldd test:发现 libtest.so not found。

  解决的方法:

  • 拷贝.so文件到系统共享库路径下, 一般指/usr/lib(容易污染官方库文件)
  • 更改环境变量:LD_LIBRARY_PATH…还好
  • ldconfig 更改配置文件/etc/ld.so.conf.d/,ldconfig更新(不推荐)
  • 动态链接:所有使用同一个库的进程,其进程地址空间的共享区通过页表映射至同一块物理内存上的动态库(库也是文件,运行时加载到内存上)。从而实现动态链接。
---//makefile 动态库文件编译及合并的方法使用
libmymath.so:add.o sub.o
	gcc -shared -o $@ $^
add.o:add.c
	gcc -fPIC -c $^
sub.o:sub.c
	gcc -fPIC -c $^
---//make...
----//引入库的使用
[saul@VM-12-7-centos tt730]$ echo $LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/saul/tt730/libtest.so
gcc test.c -o test -I . -L . -lmymath

---//不用make的制作使用过程
[saul@VM-12-7-centos tt730]$ vim add.h
[saul@VM-12-7-centos tt730]$ vim add.c
[saul@VM-12-7-centos tt730]$ ls
add.c  add.h
[saul@VM-12-7-centos tt730]$ gcc -fPIC -c add.c //会自动生成。o
[saul@VM-12-7-centos tt730]$ ls
add.c  add.h  add.o
[saul@VM-12-7-centos tt730]$ gcc -shared -o libtest.so *.o
[saul@VM-12-7-centos tt730]$ ls
add.c  add.h  add.o  libtest.so
[saul@VM-12-7-centos tt730]$ echo $LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/saul/tt730/libtest.so
[saul@VM-12-7-centos tt730]$ touch t1.c
[saul@VM-12-7-centos tt730]$ vim t1.c
#include "add.h"
#include 
int main()
{
    printf("%d\n",add(20,30));
    return 0;
}
[saul@VM-12-7-centos tt730]$ gcc t1.c -o t1 -I . -L . -ltest
[saul@VM-12-7-centos tt730]$ ./t1
50
  • 第三方库:jsoncpp库、boost库等…

总结与参考

  • 总结:

Linux:基础I/O | FD文件描述符 | 重定向 | 文件系统 | 动、静态库的制作与使用_第6张图片

  • 参考:

linux文件管理(inode、文件描述符表、文件表)

基础IO(下)——Linux

linux中文件索引节点知识

你可能感兴趣的:(Linux,linux,服务器,开发语言,后端,中间件)