IO与静态库动态库

一、文件IO
1、什么是系统调用(文件IO)
(1)用户空间进程访问内核的接口,使用内核提供 的各种功能
(2)极大提高了系统的安全性和用户程序的可移植性(因为操作系统大多是多任务的,直接访问硬件会出问题)
2、什么是库函数(标准IO)
(1)库函数是为了实现某个功能而封装起来的API集合(用户编程接口)
(2)提供统一的编程接口,更加便于应用程序的移植
系统调用和库函数的区别************
系统调用发生在内核空间,如果用户空间的一般应用程序使用系统调用来进行文件操作,会有用户空间切换到内核空间的开销。
使用库函数对文件进行操作,由于文件总是存储在存储介质上,因此无论读写都需要访问硬件,实现的。必然引起系统调用,也就是说库函数是通过系统调用
那为什么还要使用库函数呢,读写文件通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),这时,使用库函数就可以大大减少系统调用的次数。这一结果缘于缓冲区技术(先读写缓存区,必要时再访问实际文件)。
系统调用是用户进程进入内核的接口层,它本身并非内核函数,但他是由内核函数实现的


3、什么是文件描述符fd
(1)顺序分配的非负整数,本质是内核中某个数组的下标
(2)内核用以标识一个特定进程正在访问的文件
(3)其他资源(socket/pipe)的访问标识
fd有哪些需要注意的地方呢?
文件描述符fd是内核为了高效管理已经被打开文件所创建的索引,程序刚刚启动时,0、1、2已经被占用表示stdin/stdout/stderr,所以再打开文件只能从3开始。
(4)文件描述符作为内核的一种资源不是无限的,一般打开文件数是系统内存的10%,(kb计算称为系统限制级),查看系统级别的最大打开文件数可以使用sysctl -a | grep fs.file-max命令查看。内核为了不让某一个进程消耗掉所有的文件资源,其也会对单个进程最大打开文件数做默认值处理(称之为用户级限制),默认值一般是1024,使用ulimit -n命令可以查看。
(5)一个文件打开一次就会产生一个文件描述符,即同一个进程打开一个文件多次会产生多个文件描述符,另外每个进程都需要维护一个从零开始的文件描述符表,所以在不同进程中你可能看到相同的文件描述符(可能打开的是同一个文件,也可能不同,因为不在一个表中,所以没有讨论的意义)
4、文件IO相关操作函数
open() 创建或打开文件,打开文件时可以指定文件打开方式及文件的访问权限,常用的是普通文件和设备文件
close() 用于关闭一个打开的文件
read() 从文件中读取数据放到缓存区,并返回实际读取的字节数、从当前位置读取
注:read第三个参数表示存储空间大小,当文件字节数小于存储空间时,返回 值为文件实际大小;当文件字节数大于存储空间时,返回值为存储空间大小

write()将数据写入文件中,并返回实际写入的字节数、从当前位置开始写入
lseek()对文件当前读写位置进行定位,它只能对可定位(可随机访问)文件操作,管道、套接字和大部分字符设备不支持此类操作
注:lseek当有返回值时,可以用来计算文件长度,从文件开头到位移指针处,当不需要返回值时,可以用来定位,通过后两个参数调整位移指针的位置
lseek第二个参数为正则往后偏,为负往前偏
lseek第三个参数:SEEK_SET 文件起始位置、SEEK_END 文件结束位置、SEEK_CUR 文件当前位置(是文件而不是每一行哦)
lseek仅将当前文件的位移量记录在内核中,它并不起任何IO操作,文件位移量可以大于文件的当前长度,在这种情况下,对该文件的操作会延长文件,并形成空洞

fcntl()当多个程序访问同一个文件可能产生竞争,所以给文件上锁来解决纷争,该函数可以给文件施加建议性锁和强制性锁
5、什么是输入和输出
所谓的输入和输出都是站在应用层角度来看待的。
什么是输入?读数据走向:硬件——>内核缓存区——>流缓存区——>读类的操作函数
什么是输出? 写数据走向:写类的操作函数——>流缓存区——>内核缓存区——>硬件
以后所有的操作不管是应用层程序还是内核程序说到读就是输入,说到写就是输出
虚拟内存(通过MMU映射的)
用户空间3G
库函数的调用(操作流缓存区中的数据)
||
流缓存区
—————————————————||——————————————————————————
内核空间1G 系统调用
||
虚拟文件系统
||
驱动程序——>内核缓存区
—————————————————————————————————————————/\———
硬件 键盘 || 读

IO:标准IO/缓存IO/高级IO 文件IO/非缓存IO/低级IO
|| ||
库函数(man 3) 系统调用(man 2)
二、标准IO
1、流的概念
标准IO的核心对象就是流,当标准IO打开一个文件的时候就会在内核里面创建一个FILE结构体描述该文件,我们把这个FILE结构体形象地称为流,标准IO函数都是通过流来进行各种操作。
2、标准IO流的缓冲类型(流缓存的分类)
全缓存:读写存放在磁盘上的普通文件时使用,默认大小是4K,当缓存区满或者执行fflush函数刷新缓存区时执行操作。
行缓存:几乎所有针对终端进行读写的操作都是行缓存,当遇到‘\n’、缓冲区满、fflush操作时会进行读写操作。(stdin,stdout)
无缓存:不对IO操作进行缓存,即在对流的读写时会立刻操作实际的文件(stderr标准出错流就是无缓存,这样使得出错信息可以立刻显示在终端上)
3、流操作的相关函数
fopen(包含路径的文件名,模式) 以某种模式打开一个文件,并返回一个FILE指针
fclose(流指针) 将缓冲区内的数据全部写入文件,并释放相关资源
perror(显示错误内容) 标准IO函数执行时如果出现错误,会把错误码保存在errno 中,通过该函数打印错误信息
fseek() 每个打开的流都有一个当前读写位置,流在打开时,当前读写位置是0,表示文件的开始位置,每读写一次后,当前读写位置自动增加实际读写的大小,在读写流之间可先对流进行定位。SEEK_SET起始位置、SEEK_END结束位置、SEEK_CUR当前位置。
feof()判断文件是否结束,可用于二进制文件

按字节输入输出
getc(FILE*)、fgetc(FILE*) 从指定的流中读取一个字节
getchar()从stdin中读取一个字节
putc(int c,FILE*),fputc(int c,FILE*)向指定的流输出一个字节
putchar()向stdout输出一个字节

按行输入输出
gets(char*buf) buf是存放数据缓存区首地址,gets函数没有限制读取字符串的长度,所以应该保证buffer有足够的存储空间,否则会造成堆栈溢出,破坏不相关变量的值,不安全 -----从标准输入读
fgets() 从指定的流中读出一个size-1个字节的字符串,注意不能保证每次都能读取一行,会自动加’\0’比较安全。
注:fgets有个bug,就是从终端输入会吃换行符,即从stdin输入123,实际读入的是123‘\n’,
可以加个判断语句:
char buf[10];
fgets(buf,10,stdin);
if(buf[strlen(buf)-1]==’\n’)
buf[strlen(buf)-1]=’\0’;
puts(buf);

  ------从指定的流读

puts()
fputs()

按指定大小输入输出
fread
fwrite

格式化输入输出
scanf()
printf() 该函数的实质是一个默认参数是stdout的fprintf()
eg:fprintf(stdout,"%d\n",a);
三、静态库与动态库
1、库特性
(1)什么是库
库是函数源码的二进制文件(某种特殊的二进制,不是可执行文件)
(2)库的分类
静态库
动态库(共享库)
(3)库的名称
静态库:libxxx.a 以lib开头,.a结尾
动态库:libxxx.so 以lib开头,.so结尾

例子:libabc.a 这个整体叫做静态库,而其中的abc叫做静态库名称
lib123.so 整体叫做动态库,其中123叫做动态库名称

2、库的特点
静态库:使用静态库时(编译时),库的相关二进制信息会被加载到可执行程序中
优点:在可执行程序运行过程中,如果静态库被删除,不会影响可执行程序的运行。
缺点:如果在可执行程序中,对一个函数调用多次,那么静态库会被加载多次,造成可执行程序过大,浪费内存空间

动态库:动态库不是在编译时进行加载,而是在调用具体的函数时进行加载(有点和缺点正好和静态库相反),如果可执行程序对一个函数调用多次,动态库只会加载一次。
3、静态库的创建及使用
(1)创建:
a、 准备好某些函数的源代码
b、 编译函数源代码生成.o文件
c、 ar -cr lib库名称.a 需要的.o文件
(2)使用:(注意注意,加载的是库名称,比如libfun.a.是-l fun)
a、 如果静态库没有放到系统路径下:
gcc main.c -L 静态库路径 -l 库的名称
b、 如果静态库存放在系统路径下(/lib、/usr/lib、/usr/local/lib)
gcc main.c -l 库的名称
4、动态库的创建及使用
(1)创建
a、准备好函数的源码
b、编译生成.o文件
gcc -c -fpic *.c文件 -o xxx.o文件 (这是单文件的情况,多文件-o之后的不要)
fpic 位置无关码,源码中的函数及一些变量在加载到内存之前不会有指定好的地址
c、gcc -shared -o lib库名称.so .o文件
(2)使用
(注:其实就是当动态库没有放在系统路径下时,静态库的处理方法不再适用)
如果动态库没有存放到系统路径下:gcc main.c -L 动态库路径 -l 动态库文件名称(下面错误表示没放到系统路径下时执行方式和静态库不一样)
但是执行时发现出错:./a.out: error while loading shared libraries: libmydlladd.so: cannot open shared object file: No such file or directory

如何解决上面的错误:
1、拷贝动态库到系统路径下:gcc main.c -l 动态库名称
2、如果动态库没有存放到系统路径下:在/etc/bash.bashrc文件最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库绝对路径
如果需要使bash.bashrc配置文件生效,需要执行source /etc/bash.bashrc来重启配置文件
gcc main.c -L 动态库路径 -l 动态库文件名称
./a.out 可以正常执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

foreverwlh

你的鼓励将是我创作的巨大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值