Linux--IO

Posted 水澹澹兮生烟.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux--IO相关的知识,希望对你有一定的参考价值。

目录

一.C语言当中文件操作接口(文件输入/输出)

1.1.fopen()

1.2fread()

1.3fwrite()

二.系统调用文件接口

2.1open()

2.2read()

2.3write()

2.4lseek()

2.5close()

2.6代码

三.文件描述符

3.1文件描述符的本质

3.2文件描述符的分配方式

3.3文件描述符和文件流指针的区别

 四.重定向

4.1重定向的本质

4.1.1重定向的符号

4.1.2从文件描述符的角度理解重定向 

 4.2重定向的接口

4.3重定向的实现代码

五.静态库和动态库

 5.1动态库

5.2静态库

六.软硬链接

6.1软连接

6.2硬链接


一.C语言当中文件操作接口(文件输入/输出)

有时,需要程序从文件中读取到信息或把信息写入文件。这种程序与文件交互的形式就是文件重定向。在C语言中我们学习了一些文件操作接口,可以在程序中打开文件,然后使用特殊的I/O函数读取文件中的信息,或把文件信息写入文件。C提供了两种文件模式:文本模式和二进制模式。

1.1.fopen()

File* fopen(const char* path,const char* mode);

参数

  • path:带打开文件的路径
  • mode:以何种方式进行打开(r,w,a)
    r可读
    r+可读可写

    w

    可写
    w+可读可写(如果文件不存在,则创建文件如果是打开文件已存在,则截断文件,清空文件内容)
    a追加(追加写,但是不可读,如果文件不存在则创建文件,存在则在文件末尾写入) 
    a+可读可追加

返回值:

  • 成功:返回文件流FILE
  • 失败:返回NULL

1.2fread()

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

参数:

  • ptr:将文件读到的内容保存在ptr指向的空间当中
  • size:定义读文件时,一个块的大小,单位为字节
  • nmemb:期望可以从文件的多少个块
  • size *nmemb:相当于期望从文件当中都多少个字节
  • stream:文件流指针

返回值:返回的是真正读取到的块的个数

常用方法:将一块的大小定义成1字节,nmemb就可以代表我们期望的多少字节,返回含义也是读到的字节数。

1.3fwrite()

Size_t fwrite(const void* ptr , sizr_t size , size_t nmemb , FILE* stream);

参数:

  • ptr:想往文件当中写入的内容
  • size:定义往文件当中写的块的大小,单位为字节
  • nmemb:期望写多少块
  • stream:文件流指针

返回值:返回的是成功写入到文件当中的块的个数

常用方法: 定义的块的大小为1字节,nmemb则为想要写入的字节数量,返回值则为成功写入的字节数量


二.系统调用文件接口

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问。从数据操作方式这个角度来叔,Linux系统中的文件,可以看作是数据流。对文件进行操作之前,必须先调用标准I/O库函数fopen()将数据流打开,打开数据流之后,就可以对数据流进行输入输出操作。

2.1open()

//需要的头文件
#include<sys/type.h>
#include<sys.stat.h>
#include<fcntl.h>

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

参数:

  • pathname:待要打开的文件
  • flags:以何种方式打开

    有且只能存在一种,并且必须有

    O_RDONLY只读方式
    O_WRONLY只写方式
    O_RDWR读写方式
    可选项O_TRUNC截断文件(清空文件内容)
    O_CREAT文件不存在则创建文件
    O_APPEND追加的方式
    O_EXCL如果文件存在,则打开文件失败
    要将上面的选项进行组合,应该使用按位或的方式进行组合。例如:O_RDWR|O_CREAT|O_APPEND。
  • mode:当文件是新打开的一个文件,就需要给文件设置权限,设置权限的时候,传递8进制数字就可以了。

返回值:成功返回一个文件描述符,失败则返回-1。

注意:open()返回的文件描述符一定是该进程尚未使用的最小描述符。由于程序其定时自动打开标准输入,标准输出,和标准错误输出,一次文件描述符0,1,2会存在,那么第一次调用open()函数打开文件时返回文件描述符通常是3,在调用open()函数就会返回4。可以利用这一点在标准输出,标准输入和标准错误输出上打开一个新未见,实现重定向功能。比如说:首先调用一个close()函数关闭标准描述符1,然后调用open()函数打开一个常规文件,则一定会返回文件描述符1,这是标准输出就不再是终端,而是一个常规文件,再调用printf()函数就不会打印到屏幕上,而是笑道这个文件当中。

2.2read()

ssize_t read(int fd,void *buf, size_t count);

 参数:

  • fd:文件描述符
  • buf:将文件内容读到buf指向的空间当中
  • count:期望读多少字节;返回的字节数有时候会小于参数count的值,这几种情况分别是:督导常规文件时在读到count个字节之前已经到达文件末尾;从终端设备读,通常以行为单位,读到换行符就返回;从网络读,根据不同的传输层协议和内核缓存机制,返回值可能小于请求字节数。

返回值:返回读到的字节数

注意:这个读写位置和C标准I/O库值的读写位置可能不同,这个读写位置时记载内核当中的,而C中读写位置时用户空间I/O缓冲区中的位置。

2.3write()

#include<unistd.h>
ssize_t wirte(int fd,const void *buf,size_t count);

 参数:

  • fd:代表想要写入数据的文件描述符
  • buf:参数buf指向写入文件的数据的缓冲区
  • count:代表的是写入文件的数据的字节数

返回值:如果函数调用成功还会写入的字节数,否则返回-1,并设置适当的errno值
注意:当向常规文件写入数据时,返回值回事字节数count,但是当向终端设备或者网络中写入数据时,返回值则不一定为写入的字节数。

2.4lseek()

每个打开文件都记录着当前读写位置,打开文件时读写位置为0,表示文件的开头,通常读写多少个字节就会将读写位置往后移多少个字节。

#include<ysy/types.h>
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence);

参数:

  • fd:文件描述符偏移量
  • offset:
  • whence: 参数whence代表偏移值的相对位置
    SEEK_SET从文件开头的位置计算偏移量
    SEEK_CUR从当前位置开始计算偏移量
    SEEK_END从文件的末尾位置开始计算偏移量

2.5close()

#inlcude<unistd.h>
int close()int fd;

close()函数主要用于关闭一个已经打开的文件,如果函数调用成功,返回值为0,如果调用失败,返回值为-1,并设置成errno值。参数fd是要关闭的文件包描述符。

注意:当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close()函数关闭,所以即使用户程序不调用close()函数,在终止时内核也会自动关闭他打开的所有文件。但是对于网络服务器这种一直在运行的程序,文件描述符一定要及时关闭,负责随着打开的文件越多,会占用大量的文件描述符和操作系统资源。

2.6代码

通过上述函数来对文件进行操作

#include<stdio.h>
#include<sys/types.h>
#include<sys/stateh>
#include<fcntl.h>
#include<unistd.h>

int main(){
	int fd = open("./bite.txt",O_RDWR | O_CREAT,0664);
	if(fd < 0){
		perror("open");
		return 0;
	}
	printf("fd = %d \\n",fd);
	write(fd, "i like linux!", 20);
	lseek(fd, 0 ,SEEK_SET);
	char buf[1024] = {0};
	//-1的作用时预留\\0位置,防止访问越界
	read(fd,buf,sizeof(buf)-1);
	printf("buf: %s\\n",buf);
	return 0;
}


三.文件描述符

3.1文件描述符的本质

我们要从内核当中其进行分析,所谓的文件描述符就是进程与打开文件的一个桥梁,通过它,才可以在进程中对这个文件进行读写操作。

现在我们要理解文件描述符是如何定义的,如下图所示:

3.2文件描述符的分配方式

代码验证:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    //close(0);
    int fd = open("./1.txt", O_RDWR | O_CREAT, 0664);
    if(fd < 0){
        perror("open:");
        return 0;
    }

    printf("fd = %d\\n", fd);

    while(1){
        sleep(1);
    }
    return 0;
}

最小未分配原则

3.3文件描述符和文件流指针的区别

一个文件流指针只能保存一个文件描述符。

文件流指针的本质如下图:

 注意: 

  1. 一个文件流指针只能保存一个文件描述符,但是一个文件描述符可以被多个文件流指针打开。
  2. C标准库对应的缓冲区是读缓冲区和写缓冲区。
  3. 文件流指针和文件描述符的关系是文件流至真当中包含文件描述符。
  4. exit()和_exit()之间的区别是exit()函数比他多了回调函数和刷新缓冲区。

 四.重定向

4.1重定向的本质

4.1.1重定向的符号

  • >    清空重定向
  • >>  追加重定向

4.1.2从文件描述符的角度理解重定向 

我们可以参考文件描述符的本质的图进行参考,在这个图里面,当前我们创建了这个进程之后,就有了三个文件描述符,标准输入,标注输出和标准错误输出,有了这三个文件描述符后,当我们再次在文件当中用open()打开一个文件,我们就创建了一个3号描述符,而三号描述符也对应了struct file*这个结构体,而当前的这些结构体在磁盘当中与某个文件与之对应,而此时标准输入,标注输出和标准错误输出对应的是设备文件,而此时我们想要让标准输出的内容从定向到文件当中,也就是说将1好文件描述符原本输出到设备当中改成输出到3号文件当中,也就可以说让1号文件描述符的struct file*指针指向3号文件描述符对4应的struct file结构体。这就是重定向的本质。

 4.2重定向的接口

int dup(int oldfd ,int newfd);

newfd拷贝oldfd的值,将newfd重定向为oldfd。如果要成功,则要关闭newfd,且让newfd指向oldfd。

失败的情况:

  1. oldfd是一个非法的文件描述符,或者不存在的文件描述符。函数调用失败,并且没有关闭newfd。
  2. newfd和oldfd的值是相等的,则dup2函数什么事情都没有做。

4.3重定向的实现代码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(){//将标准输出重定向成一个txt文件
    int fd = open("./bite.txt", O_RDWR | O_CREAT, 0664);//打开一个文件
    if(fd < 0){//失败
        perror("open");
        return 0;
    }
    dup2(fd, 1);
    printf("hello world...\\n");
    return 0;
}

五.静态库和动态库

静态库和动态库都是程序代码的集合,将代码集合封装在了库文件当中,提供给调用者使用。

 5.1动态库

1.特征:在windows操作系统下,动态库文件后缀为.dll;在linux操作系统下,前缀为lib,后缀为.so;例如libxyz.so。

2.生成动态库:使用gcc/g++编译命令,需要用到两个命令行参数

  • -FPIC:产生位置无关的代码
  • -shared:生成共享库

3.使用:使用gcc/g++的命令行参数

  • -L [path]:指定当前链接库文件所在路径
  • -L [库文件名称(去掉前缀和后缀的名称)]:连接某个库文件

4.动态库的环境变量:LD_LIBRARY_PATH

5.可执行程序找到自己依赖的动态库的方式

  • 将动态库放到可执行程序的路径下
  • 配置LD_LIBRARY_PATH环境变量
  • 放到系统库的路径下,/lib64

5.2静态库

1.特征:在windows操作系统下,动态库文件后缀为.lib;在linux操作系统下,前缀为lib,后缀为.a;例如libxyz.a。

2.生成静态库:注意点使用目标程序进行编译生成

  • 第一段:使用gcc/g++将源代码文件编译成目标文件(.o文件)
  • 第二段:使用ar -rc命令编译目标文件生成静态库

六.软硬链接

6.1软连接

1.生成链接文件: ln -s

2.特征:

软链接文件就是源文件快捷方式。用命令ll -i查看文件信息的时候,inode节点号本质上对应的是文件系统当中的概念,在inode当中保存了文件在磁盘当中的存储位置。注意的是,在删除软链接文件对应的源文件的时候,需要将软链接文件也删除掉!原因是他会重新创建原文件,并且inode相同。

6.2硬链接

1.生成: ln

2.特征:源文件与硬链接文件inode相同,说明硬链接文件是源文件的一个拷贝,而且还是浅拷贝,此时存在一个风险,就是用户在使用完硬链接文件后直接将内存释放掉,那么空间直接不存在,那么再次要使用其源文件的时候就会非法,此时我们就使用了一个技术引用计数。当引用计数大于等于2的时候,不会真正free()掉。

以上是关于Linux--IO的主要内容,如果未能解决你的问题,请参考以下文章

Linux--IO

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板