【linux】基础IO

打开文件

    #include <stdio.h>
    FILE *fopen(const char *path, const char *mode);
    FILE *fdopen(int fildes, const char *mode);
    FILE *freopen(const char *path, const char *mode,FILE *stream);

DESCRIPTION 描述
函数 fopen 打开文件名为 path 指向的字符串的文件,将一个流与它关联。

       参数 mode 指向一个字符串,以下列序列之一开始 (序列之后可以有附加的字符):

       r      打开文本文件,用于读。流被定位于文件的开始。

       r+     打开文本文件,用于读写。流被定位于文件的开始。

       w      将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始。

       w+     打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始。

       a      打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾。

       a+     打开文件,用于追加
              (在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾。

具体说一下截断和追加
在这里插入图片描述
所谓截断就是在新插入数据时会将原来的数据删除后在进行插入
在这里插入图片描述
直接在文件后面进行插入数据

打开文件上限

ulimit -a可以查看当前进程所有资源上限
在这里插入图片描述
可以看到当前进程 open file 上限为1024
但你自己写一个只打开文件的代码会发现,最多可以打开1021个文件,因为每个进程创建的时候,都会默认打开三个文件流,标准输入(stdin)、标准输出(stdout)、标准错误(strerr)

一直打开文件代码如下:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main()
  5 
  6 {
  7 int count =0;
  8   while(1)
  9   {
 10    FILE* fp=fopen("my_file","a+");
 11 
 12    if(!fp)
 13    {
 14      break;
 15    }
 16    ++count;                                           
 17   }
 18   printf("count:%d\n",count);
 19   return 0;
 20 }

openfile

读文件和写文件

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
函数功能:把ptr所指向的数组中的数据写入到给定流stream中

ptr-- 这是指向要被写入的元素数组的指针。
size-- 这是要被写入的每个元素的大小,以字节为单位。
nmemb-- 这是元素的个数,每个元素的大小为 size 字节。
stream-- 这是指向FILE对象的指针,该FILE对象指定了一个输出流。

返回值:如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。 [2]
  1 #include<stdio.h>                                  
  2 #include<unistd.h>                         
  3 #include<string.h> 
  4 int main()                                          
  5 {                   
  6 FILE* fp=fopen("my_file","a+");
  7                                         
  8 if(!fp)                
  9 {
 10   printf("open error!");
 11 }        
 12                                                      
 13 const char* ptr="hello!\n";
 14 
 15 fwrite(ptr,strlen(ptr),1,fp);                         
 16                                       
 17   return 0;
 18 }
 19        

fwrit演示

fseek函数
文件头(SEEK_SET),当前位置(SEEK_CUR),文件尾(SEEK_END)
功能:重定位流(数据流/文件)上的文件内部位置指针
注意:文件指针指向文件/流。位置指针指向文件内部的字节位置,随着文件的读取会移动,文件指针如果不重新赋值将不会改变或指向别的文件。

头文件:
#include <stdio.h>

用法:
int fseek(FILE *stream, long offset, int fromwhere);

第一个参数stream为文件指针
第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
简言之:
fseek(fp,100L,0);把stream指针移动到离文件开头100字节处;
fseek(fp,100L,1);把stream指针移动到离文件当前位置100字节处;
fseek(fp,-100L,2);把stream指针退回到离文件结尾100字节处。
描述:
函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere(偏移起始位置:文件头0(SEEK_SET),当前位置1(SEEK_CUR),文件尾2(SEEK_END))为基准,偏移offset(指针偏移量)个字节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置。
fseek函数和lseek函数类似,但lseek返回的是一个off_t数值,而fseek返回的是一个整型。

返回值:
成功,返回0,失败返回非0值,并设置error的值,可以用perror()函数输出错误。

关闭文件

C 库函数int fclose(FILE *stream)关闭流 stream。刷新所有的缓冲区。
声明编辑
下面是 fclose() 函数的声明。
int fclose(FILE *stream)
参数编辑
stream-- 这是指向 FILE 对象的指针,该FILE对象指定了要被关闭的流。
返回值编辑
如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
在调用fopen函数之后记得加一个fclose函数及可
open函数
int open(const char*pathname,int flags);

int open(const char*pathname,int flags,mode_t mode);

该函数有两个函数原型,其实是参数的数量问题。

1、int open(const char*pathname,int flags);

第一个参数是要打开的文件名,第二个参数是特殊常量,用于指定怎么打开文件。具体常量如下

参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读,写打开
这三个常量,必须制定一个且只能指定一个
O_CREAT: 若文件不存在,则创建它,需要使用mode选项。来指明新文件的访问权限
O_APPEND: 追加写,如果文件已经有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容

返回值:成功打开返回打开文件的文件描述符,是int类型,失败返回-1.

2、 int open(const char*pathname,int flags,mode_t mode);

前两个参数与上相同,第三个参数是设定该文件的权限,具体参数如下

S_IRUSR    : 文件所有者有读(r)权限

S_IWUSR   : 文件所有者有写(w)权限

S_IRGRP   : 文件所属组有读(r)权限

S_IWGRP  : 文件所属组有写(w)权限

S_IROTH   : 文件所属other有读(r)权限

S_IWOTH  : 文件所属other有写(w)权限
write函数
头文件:<unistd.h>
write有两种用法。一种是:ssize_t write(int fd, const void *buf, size_t nbyte);
fd:文件描述符;
buf:指定的缓冲区,即指针,指向要写入的数据;
*buf:写入的数据
nbyte:要写入文件指定的字节数,即写入数据的大小
返回值:写入文档的字节数(成功);-1(出错)
write函数把buf中nbyte写入文件描述符handle所指的文档,成功时返回写的字节数,错误时返回-1.
另一种是: write(const char* str,int n)
str是字符指针或字符数组,用来存放一个字符串。n是int型数,它用来表示输出显示字符串中字符的个数。
write("string",strlen("string");表示输出字符串常量
read函数
ssize_t read(int fd,void* buf,size_t count)
fd:文件描述符;
buf:指定的缓冲区,即指针,指向要写入的数据;
*buf:写入的数据
count:最大可以读多少字节,注意:需要预留\0的位置
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。

lseek函数
off_t lseek(int fd,off_t offset,int whence)
fd:文件描述符

offset:偏移量,正数表示正向偏移,负数表示负向偏移
origin:设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
close函数
int close(int fd)
该函数用来关闭已打开文件,指定参数fd为open()或creat()打开的文件描述符
返回值:关闭成功返回0,失败返回-1

写一个系统调用代码
  1 #include<stdio.h>                                     
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<string.h>
  5 int main()
  6 
  7 {
  8 //flags:
  9 //O_RDONLY: 只读打开
 10 //O_WRONLY: 只写打开
 11 //O_RDWR: 读,写打开
 12 //这三个常量,必须制定一个且只能指定一个
 13 //O_CREAT: 若文件不存在,则创建它,需要使用mode选项。>    来指明新文件的访问权限
 14 //O_APPEND: 追加写,如果文件已经有内容,这次打开文件所    写的数据附加到文件的末尾而不覆盖原来的内容
 15 //
 16   int fd=open("./my_file",O_RDWR|O_CREAT,0664);
 17   if(fd<0)
 18   {
 19     perror("open");
 20     return 0;
 21   }
 22     printf("fd:%d\n",fd);
 23  int write_size=write(fd,"hello-linux",11);
 24  if(write_size<0)
 25  {
 26    perror("write");
 27    return 0;
 28  }
 29 printf("write_size:%d\n",write_size);
 30 lseek(fd,0,SEEK_SET);
 31 char buf[1024];
 32 memset(buf,'\0',sizeof(buf));
 33 int read_size=read(fd,buf,sizeof(buf)-1);
 34 if(read_size<0)
 35 {
 36   perror("read_size");
 37   return 0;
 38 }
 39 printf("read_size:%d\n",read_size);
 40 printf("buf :%s\n",buf);    
 41 close(fd);
 42 return 0;
 43 }  

在这里插入图片描述

文件描述符:是数组fd_array(该数组的每个元素都对应一个文件信息)的下标,下标从0开始,所以文件描述符是一个正整数

程序猿通过文件描述符,找到fd_array数组中对应元素,从而操作文件,内核通过操作元素对应的文件信息来实现文件操作

在这里插入图片描述

文件描述符分配规则:最小未占用,即分配当前最小的数组小标且该数组小标当前未被占用,越小越优先分配

文件流指针:由于直接使用文件描述符进行操作不方便,也不安全,所以将其进行封装之后,就有了文件流指针

当进行运行时,会默认打开三个文件

标准输入标准输出标准错误类型
stdinstdoutstderr–> FILE* ---- 文件流指针
012–> int ---- 文件描述符

区别:

  1. 文件流指针时标准库操作句柄:FILE*,文件描述符时系统调用接口句柄:int fd
  2. 文件流指针是封装文件描述符的,文件流指针底层的_IO_FILE结构体当中,保存了文件描述符。
  3. 文件描述符是和内核关联,文件流指针是和所调用的库函数关联
  4. 缓冲区:库函数接口当中维护的一段内存,分为读缓冲区和写缓冲区,存在于文件流指针结构体中

缓冲区:库函数接口当中维护的一段内存,分为读和写缓冲区,存在于文件流指针结构体中
联系:
文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针

读写缓冲区是库函数来维护的,并不是内核维护。进程终止时刷新缓冲区,就是操作该缓冲区,

_exit(系统调用)和exit(库函数)
_exit不会刷新缓冲区,_exit是内核的代码,根内核没有缓冲区
exit会刷新缓冲区,库函数缓冲区即读写缓冲区

由于打印内容实现写到写缓冲区当中,在使用文件流指针的时候,必须加上\n或者其他强制刷新缓冲区的操作

重定向

1.重定向符号
>               输出重定向到一个文件或设备 覆盖原来的文件
>!              输出重定向到一个文件或设备 强制覆盖原来的文件
>>             输出重定向到一个文件或设备 追加原来的文件
<               输入重定向到一个程序 
2.标准错误重定向符号
2>             将一个标准错误输出重定向到一个文件或设备 覆盖原来的文件  b-shell
2>>           将一个标准错误输出重定向到一个文件或设备 追加到原来的文件
2>&1         将一个标准错误输出重定向到标准输出 注释:1 可能就是代表 标准输出
>&             将一个标准错误输出重定向到一个文件或设备 覆盖原来的文件  c-shell
|&              将一个标准错误 管道 输送 到另一个命令作为输入
3.命令重导向示例

在 bash 命令执行的过程中,主要有三种输出入的状况,分别是:

  1. 标准输入;代码为 0 ;或称为 stdin ;使用的方式为 <
  2. 标准输出:代码为 1 ;或称为 stdout;使用的方式为 1>
  3. 错误输出:代码为 2 ;或称为 stderr;使用的方式为 2>
重定向的本质:new_fd拷贝old_fd,(二者都是文件描述符)
4.重定向过程举例说明
将标准输出(stdout)的内容重定向到new_file当中,所以stdin的fd=1,假设当前目录下只有new_file这一个文件,则new_file的fd=3,
重定向过程如下:
1.new_file的文件描述符拷贝stdout的文件描述符
2.若拷贝成功,则关闭new_file的文件描述符
但以下两种情况除外:
①拷贝的是一个无效的文件描述符,则不会关闭new_file文件描述符
②拷贝的new_file文件描述符和stout文件描述符具有相同的值,则也不会关闭new_file文件描述符,new_file的文件描述符和stout的文件描述符指向同一个文件
5.dup()和dup2()
#include<unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
两个均为复制一个现存的文件的描述
两个函数的返回:若成功为新的文件描述,若出错为-1;
dup()函数返回的新的文件描述符是当前可用文件描述符中最小数值

dup()的举例



  1 #include<stdio.h>           
  2 #include<fcntl.h>
  3 #include<unistd.h>
  4 
  5 int main()
  6 {
  7    //flags:
  8    //O_RDONLY: 只读打开
  9    //O_WRONLY: 只写打开
 10    //O_RDWR: 读,写打开
 11    //这三个常量,必须制定一个且只能指定一个
 12    //O_CREAT: 若文件不存在,则创建它,需要使用mode选项。>    来指明新?<M-C-G>件的访问权限
 13    //O_APPEND: 追加写,如果文件已经有内容,这次打开文件所    写的数据附加到文件的末尾而不覆盖原来的内容
 14    //        
 15    int fd;
 16    int new_fd;
 17    fd=open("dup",O_RDWR|O_CREAT,0666);
 18   if(!fd)
 19   {
 20     printf("open error!!!");
 21 
 22   }
 23   printf("fd:%d\n",fd);
 24   new_fd=dup(fd);
 25   printf("new_fd:%d\n",new_fd);
 26   write(new_fd,"abcde",5);
 27   close(fd);
 28   close(new_fd);
 29 
 30   return 0;
 31 }      

在这里插入图片描述

dup2()与dup()的区别在于可以用newfd来指定新描述符数值,若newfd指向的文件已经被打开,会先将其关闭。若newfd等于oldfd,就不关闭newfd,newfd和oldfd共同指向一份文件

dup2()举例

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<fcntl.h>
    4 
    5 int main()
    6 
    7 {
    8 int old_fd;
    9 int new_fd;
   10 
   11 old_fd=open("dup2",O_RDWR,"a+");
   12 if(!old_fd)
   13 {
   14   printf("open error!!!");
   15 }
   16 printf("old_fd:%d\n",old_fd);
   17 
   18 dup2(old_fd,new_fd);  
   19 printf("hello!!!\n");                                                                                             
   20   return 0;
   21 }

在这里插入图片描述

dup2(old_fd,new_fd)的意思:new_fd指向old_fd指向的文件描述符,即指向标准输出文件描述符指向dup2

静态库和动态库

静态库:在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库。

静态库特点是可执行文件中包含了库代码的一份完整拷贝

缺点就是被多次使用就会有多份冗余拷贝。

  • windows环境下静态库的后缀为.lib,windows环境下的执行程序,编译阶段依赖静态库(.lib),程序运行阶段依赖动态库(.dll)
  • linux环境下,静态库命名规范,必须是"lib[your_libary_name].a"lib为前缀,中间是静态库名,扩展名为.a
 静态库的生成:生成静态库的时候,使用.o文件进行编译生成
 ar -rc lib[your_libary_name].a [xxx].o [xxx].o
 静态库的使用:
 -L [path] 指定链接的库文件路径;
 -l [your_libary_name]:指定链接库文件名称
 ege:gcc main.c -o main -L [path] -l [your_libary_name]

动态库
  • windows环境下动态库后缀.dll
  • linux环境下,动态库命名规范,必须是"lib[your_libary_name].so"lib为前缀,中间是静态库,扩展名为.so

ldd file

列出file所需的所有共享库,具体细节可使用 man ldd
在这里插入图片描述


动态库的生成
    -shared 生成共享库
    -fPIC 产生位置无关的代码

动态库的使用
    -L [path] 指定链接的库文件路径;
    -l [your_libary_name]:指定链接库文件名称

如何让程序可以找到依赖的动态库

  1. 将动态库放到当前可执行程序的目录下
  2. 在环境变量当中设置动态库的搜索路径,设置环境变量LD_LIBRART_PATH
  3. 不要将其他库(自己写的库或者第三方库)和操作系统库文件放到一起
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值