1.静态库制作
gcc -c add.c div.c mult.c sub.c -I ../include/; //-c编译汇编但不链接,获得-o文件
ar rcs libcalc.a add.o div.o mult.o sub.o //把库libcalc.a和.o文件打包成静态库libcalc.a
gcc main.c -o app -I ./include/ -L ./lib -l calc //-I查找头文件,-L查找库路径,-l加载使用库
2.动态库制作
gcc -c fpic add.c div.c mult.c sub.c -I ../include
gcc -shared add.o div.o mult.o sub.o libcalc.so //libcalc.so和.o文件打包得到动态库libcalc.so
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: /home/Linux/lesson06/lilrary/lib //告知系统库搜索路径
gcc -o main.c app -I ./include -L ./lib -l calc
3.静态库和动态库的区别
静态库:
静态库的名字形式为 libxxx.a,前缀是lib,后缀名为.a。
静态库是在链接阶段完成的,将函数库和-o文件打包得到静态库;
程序运行时使用静态库,与函数库无关系;
静态库浪费空间,因为所有相关的文件和库函数打包在一起,但没有外部依赖,直接就可以运行。
动态库:
动态库的名字形式为 libxxx.so,前缀是lib,后缀名为.so。
动态库是在程序运行阶段完成,把库函数的链接载入直到到程序运行阶段使用。
动态库也叫做共享库,动态库在内存中只存在一份,可以被多个进程共享。(动态库与共享内存相比较,动态库不能实现进程间通信)
动态库不需要拷贝到程序中,不会影响程序大小,使用动态库需要依赖外部环境。
4、Makefile
格式:
目标:依赖
命令(shell命令)
目标:最终要生成的文件,可以是Object File,也可以是执行文件。
依赖:要生成目标所需要的文件或是目标。
命令:通过执行命令对依赖操作生成目标
vim Makefile //终端操作,进入Makefile文件中
app : sub.o add.o mult.o div.o main.o
gcc sub.o add.o mult.o div.o main.o -o app
sub.o : sub.c
gcc sub.c -c sub.o
add.o : add.c
gcc add.c -c add.o
mult.o : mult.c
gcc mult.c -c mult.o
div.o: div.c
gcc div.c -c div.o
main.o : main.c
gcc main.c -c mian.o
make //终端操作,执行Makefile
5、gdb调试
list指令,查看代码。可以指定行数、函数名、文件名等操作。
break指令,设置断点。break 行号/函数名/文件名。还可以设置条件断点:比如在for循环中,设置断点停在循环变量等于某个值时。
info指令,查看断点(i b)。
delete指令,删除断点。删除的是断点编号。
disable指令,失效断点。失效的是断点编号。
6、Linux系统IO函数
1) fopen与open的关系:
open是ANSIC标准中的C语言库函数,在不同的系统中应该调不同的内核Api(open是Linux、Unix系统调用函数),返回的是一个指向文件结构的指针。在Linux中,fopen调用open。
2)Linux下读取一个文件的过程
读取一个文件(read)需要IO引擎、VFS、Page Cache高速缓存、通用管理模块、IO调度层等配合完成。
IO引擎(用户层):选择读取文件的库函数,例如sync引擎的read函数、psync引擎的pread函数。进行系统调用到达内核层。
VFS虚拟文件系统:Linux 上抽象一个通用的文件系统模型,对我们提供一组通用的接口,让我们不用在乎 具体文件系统的实现。
Page Cache高速缓存:Linux 就可以把一些磁盘上的文件数据保留在内存中,当用户要访问的文件正好存在于Page Cache内,则直接把数据从内核态拷贝到用户进程的内存中就可以了。如果不存在,那么会申请一个新页,发出缺页中断,然后用磁盘读取到的内容来填充它 ,下次直接使用。
文件系统:文件系统里提供对 VFS 的具体实现。
通用块层:提供一个统一的接口让供文件系统实现者使用,而不用关心不同设备驱动程序的差异,实现出来的文件系统就能用于任何的块设备(机械硬盘、磁盘、U盘)。
IO调度层:从全局出发,尽量让整体磁盘 IO 性能最大化。
驱动程序:驱动程序向磁盘控制发出读取命令控制。
磁盘:控制器读取硬盘数据,填充到Page Cache中的新页框
IO函数:
open函数
int open(const char* pathname, int flags) //打开文件
int open(const char* pathname, int flags, mode_t mode) //创建一个新文件,最终权限 = mode & ~umask
参数:
pathname:文件的绝对路径或者当前路径下文件名
flags:文件操作权限,O_RDONLY O_WRONLY O_RDWR
mode:r=4,w=2,x=1
返回值:
成功,返回一个文件描述符fd
失败,返回-1
read函数
int read(int fd, void* buf, int count)
参数:
fd:文件描述符,open得到
buf:读取数据存放的地方,读取数组地址
count:指定数组大小
返回值:
成功,>0实际读取的字节数,=0文件读取完了
失败,返回-1
write函数
int write(int fd, void *buf, int count)
参数:
fd:文件描述符,open得到的
buf:往磁盘写入的数据
count:要写的数据的实际的大小sizeof(buf)
返回值:
成功,返回实际写入的字节数
失败,返回-1,并设置errno
//将test.txt文件内容复制到cpy.txt文件
//1.通过open打开test.txt文件
int srcfd = open(test.txt, O_RDONLY);
if(srcfd == -1) {
perror("open");
return -1;
}
//2.新建一个文件copy.txt
int desfd = open(copy.txt, O_RDWR | O_CREAT, 0664);
if(desfd == -1) {
perror("open");
return -1;
}
//3.进行拷贝(频繁读写操作)
int len = 0;
char buf[1024] = {0};
//char *a = "abcd";"abcd"存放在常量存储区,通过指针只可以访问字符串常量,而不可以改变它
//char a[20] = "abcd";"abcd"存放在栈,可以通过指针去访问和修改数组内容
while(len = read(srcfd, buf, sizeof(char)) > 0) {
//read中使用sizeof(char),整个数组都用于读,之前的初始化都会被覆盖
write(dstfd, buf, len);
}
//4.关闭文件
close(srcfd);
close(dstfd);
注意事项:write()函数从buf写数据到fd中时,若buf中数据无法一次性读完,那么第二次读buf中数据时,其读位置指针(也就是第二个参数buf)不会自动移动,需要程序员来控制,而不是简单的将buf首地址填入第二参数即可。例如可按如下格式实现读位置移动:write(fp, p1+len, (strlen(p1)-len))。 这样write第二次循环时便会从p1+len处写数据到fp, 之后的也一样。由此类推,直至(strlen(p1)-len)变为0