Linux文件处理

文章目录

文件描述符及文件处理

在 Linux 中设备和目录都可以看做文件(一切皆文件),主要包括四种类型:

普通文件 ,② 目录文件 ,③ 链接文件 ,④ 设备文件

文件描述符

Linux 的内核利用文件描述符来访问文件

  • 定义

    文件描述符是非负整数作为索引指向内核中的进程打开文件表

    当打开一个现存文件或新建一个文件时,内核会向进程返回一个文件描述符

    当读写文件时,也需要使用文件描述符来指定待读写的文件

  • 标准文件描述符

    通常一个进程启动时,要打开三个文件:标准输入标准输出标准错误处理

    它们的文件描述符分别为:0、1 和 2

    在国际标准组织IEEE制定的ISO标准中,定义了宏符号常量 STDIN-FILENOSTDOUT_FILENOSTDERR_FILENO,用于替代0、1 和 2(定义位于头文件 <unistd.h> 中)

    • 0:标准输入(STDIN_FILENO
    • 1:标准输出(STDOUT_FILENO
    • 2:标准错误(STDERR_FILENO

系统调用

系统调用是指操作系统提供给用户程序的一组"特殊"接口,用户程序可以通过这组"特殊"接口来获得操作系统内核提供的特殊服务

在Linux中用户程序不能直接访问内核 提供的服务

为了更好的保护内核空间,将程序的运行空间分为内核空间用户空间,他们运行在不同的级别上,在逻辑上是相互隔离的

【详细说明】

  • 通常情况下,应用程序是通过**操作系统提供的应用编程接口(API)**而不是直接通过系统调用来编程
  • 操作系统 API 通常都以 C 库函数的方式提供,Linux 也是如此
  • 系统调用和 C 库函数之间并不是一一对应的关系,可能几个不同的函数会调用到同一个系统调用
  • 系统命令位于C库的更上层,是利用 C 库实现的可执行程序,比如最为常用的 ls、cd 等命令

【系统调用的作用】

  • 屏蔽硬件差异,提供统一接口(抽象底层设备)

    系统调用可以为用户空间提供访问硬件资源的统一接口,应用程序不必去关注具体的硬件访问操作

    (如读写文件时,应用程序不用去管磁盘类型,甚至于不用关心是哪种文件系统)

  • 提高系统安全性,防止非法访问内核

    系统调用可以对系统进行保护,保证系统的稳定和安全,系统调用的存在规定了用户进程进入内核的具体方式

    (用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核,有了这样的进入内核的统一访问路径限制才能保证内核的安全)

文件处理

Linux对文件处理有两种方式:

  • 系统调用直接与内核交互 ,如 openreadwrite

    特点:无缓冲,每次调用触发内核操作,效率较低但实时性强

    【说明】

    基于 Linux 的系统调用,由操作系统的系统调用完成对文件的操作

    Linux 通过系统调用进行的文件处理,主要是指进行打开文件、读文件、写文件及关闭文件等 I/O 操作

    大多数情况下,只需要用到5个函数:openreadwritelseekclose

    这几个函数不需要经过缓冲就能立即执行,因此,被称为不带缓冲的I/O操作,即每一个函数都只调用内核中的一个系统调用

  • 库函数调用封装系统调用,提供高级接口 ,如 fopen, fread, fwrite

    特点:带缓冲,减少系统调用次数,提高效率(如全缓冲、行缓冲、无缓冲模式)

    【说明】

    基于 C 语言的库函数,标准 C 库函数提供的文件操作函数如 fopen, fread, fwrite, fclose, fflush, fseek 等,它是独立于操作系统

【注意】系统调用是为了方便使用操作系统的接口,库函数则是为了人们程序的移植性更好

【无缓冲与有缓冲的区别】

  • 无缓冲 I/O:立即执行操作,不借助用户态缓冲区
  • 有缓冲 I/O:使用用户态缓冲区,I/O 操作可能延后执行(如 fwrite 需调用 fflush

open / create / close 函数

open 函数

open打开或创建文件

调用 open 函数所需要的头文件如下:

c 复制代码
#include <stdio.h>
#include <fcntl.h>

其函数为:

c 复制代码
int open(const char *pathname, int oflag, int perms);
  • pathname: 要打开或创建的文件的名字
  • oflag:可用来说明此函数的多个选择项
    • O_RDONLY:只读打开
    • O_WRONLY:只写打开
    • O_RDWR :读、写打开
    • O_APPEND:每次写时都加到文件的尾端
    • O_CREAT:若此文件不存在则创建它(使用此选择项时,需同时说明第三个参数,用其说明该新文件的存取许可权限)
    • O_TRUNC:如果此文件存在,而且为只读或只写成功打开,则将其长度截短为 0
    • O_EXCL:如果使用本参数,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件
  • perms:权限(八进制 ,如0666表示所有用户可读写),仅当创建新文件时才使用perms参数

函数返回值:若文件打开成功则返回文件描述符 ;若出错则返回 -1

create 函数

creat以只写方式创建一个文件,若文件已经存在,则把它清空为 0

调用 creat 函数所需要的头文件如下:

c 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

其函数为:

c 复制代码
int creat(const char * pathname, mode_t mode); 

//【注意】此函数等效于:
int open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
  • pathname :要创建的文件名称
  • mode:跟 open 的第三个参数相同,可读,可写,可执行

函数返回值:若成功为只写打开的文件描述符 ,若出错为 -1

close 函数

close:用于关闭一个打开的文件,并释放文件描述符

调用 close 函数所需要的头文件为:

c 复制代码
#include <stdio.h>

其函数为:

c 复制代码
int close (int fd);

fd:文件描述符

函数返回值:若成功为0,若出错为-1

当一个进程终止时,它所有的打开文件都由内核自动关闭,很多程序都使用这一功能而不显式地用 close 关闭打开的文件


【举例】用可读写方式新建(打开)一个文件

由于要以可读写的方式来打开文件,因此,对文件的操作权限为4(可读)及2(可写)之和,即操作权限为6;如果希望文件拥有者、同组用户、不同组用户均具有读和写的权限,则该文件的权限为666

c 复制代码
#include<stdio.h> 
#include<fcntl.h> 
int main(){
    int fd; 
    fd=open("a.txt",O_CREAT|O_TRUNC|O_RDWR,0600); 
 
    printf("open file: a.txt fd=%d \n",fd); 
    close(fd); 
    return 0; 
}

read / write / lseek 函数

read 函数

read从打开的文件中读取数据

当 read 从终端设备文件中读取数据时,通常一次读取指定的字节数

调用 read 函数所需要的头文件为:

c 复制代码
#include <unistd.h>

其函数为:

c 复制代码
ssize_t read(int fd, void *buf, size_t  count);
  • fd :文件描述符,标识要读取的文件

    如果为 0,则从标准输入读数据,类似于 scanf() 的功能

  • buf :指定存储器读取数据的缓冲区

  • count :指定读取数据的字节数

函数返回值:实际读到的字节数,若返回值为0,则已经达到文件尾,若返回-1则出错

write 函数

write :用于向打开的文件实现写入数据的操作

写操作的位置从文件的当前位移量处开始

若磁盘已满或超出该文件的长度,则 write 函数返回错误值

调用 write 函数所需要的头文件为:

c 复制代码
#include <unistd.h>

其函数为:

c 复制代码
ssize_t write(int fd, const void * buf, size_t count);
  • fd:文件描述符,标识了要写入的目标文件

    例如:

    fd 的值为1,就向标准输出写数据,也就是在显示屏上显示数据;

    如果为 2 ,则向标准错误文件写数据

  • *buf:待写入的字符缓冲区,是一个字符串指针

  • count:要写入的字符数

函数返回值:为已写入的字节数,若返回 -1 则出错

对于普通文件,写操作从文件的当前位移量处开始

如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的结尾处。在一次成功写之后,该文件位移量增加实际写的字节数

lseek 函数

lseek:用于将文件指针定位到相应的位置,以进行读写操作

c 复制代码
off_t lseek(int fd, off_t offset, int whence);
  • fd:文件描述符
  • offset:偏移量,每一读写操作所需要移动的字节数,可正可负(向前、向后移)
  • whence:指出偏移的方式(有三种参数)
    • SEEK_SET:偏移到offset位置处(相对文件头)
    • SEEK_CUR:偏移到当前位置+offset位置处
    • SEEK_END:偏移到文件尾+offset位置处

函数返回值:调用成功则返回最终的偏移量 (从文件头开始数),调用失败则返回 -1

【举例1】返回当前的偏移量

c 复制代码
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);

【举例2】返回文件大小

c 复制代码
off_t currpos;
currpos = lseek(fd, 0, SEEK_END);

【举例1】创建一新文件 test.txt,并把字符串"Hello, welcome to you"写到新创建的文件a.txt

c 复制代码
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main(){
	int fd;
	fd=open("a.txt",O_CREAT|O_TRUNC|O_RDWR,0666);

	char * buf="Hello,welcome to you.\n";
	int len = strlen(buf);
	int size = write(fd,buf,len);

	printf("write to  a.txt fd=%d char \n",size);
	close(fd);
	return 0;  
}

【举例2】在当前目录,新建一个a.txt文件,并编辑一些内容;通过程序,读取a.txt中的内容

c 复制代码
#include<stdio.h>
#include<fcntl.h>
int main(){
	int fd;
	char buf[50];

	fd=open("a.txt",O_RDONLY);
	printf("OPEN FILE : a.txt fd=%d \n",fd);
	lseek(fd,0,SEEK_SET);
	int size = read(fd,buf,22);
	printf("READ FROM FILE: %s \n size=%d \n",buf,size);
	close(fd);
	return 0;
}

标准I/O库

  • fopen 函数:类似于系统调用中的open函数

  • fread/fwrite 函数:

    fread 函数从文件流中读取数据,对应于系统调用中的 read

    fwrite函数从文件流中写数据,对应于系统调用中的 write

  • fclose 函数:关闭指定的文件流 stream,使用 fclose() 函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区

  • fflush 函数:用于强制将缓冲区中的内容写入文件

系统调用与库函数调用

相关推荐
sulikey3 小时前
从实验出发深入理解Linux目录权限:r、w、x分别控制什么?能否进入目录到底由谁决定?
linux·运维·服务器·ubuntu·centos
Cathy Bryant3 小时前
大模型损失函数(二):KL散度(Kullback-Leibler divergence)
笔记·神经网络·机器学习·数学建模·transformer
疯狂吧小飞牛3 小时前
ip rule 策略路由
linux·网络·tcp/ip·运维开发
qq_398586543 小时前
Threejs入门学习笔记
javascript·笔记·学习
缘友一世3 小时前
深入理解Shell与反弹Shell:从原理到实战
linux·网络安全·shell
stark张宇3 小时前
网络排错全流程:从DNS解析到防火墙,逐层拆解常见问题
linux·网络协议·dns
hour_go4 小时前
TCP/IP协议相关知识点
网络·笔记·网络协议·tcp/ip
潘达斯奈基~4 小时前
在使用spark的applyInPandas方法过程中,遇到类型冲突问题如何解决
大数据·笔记
我先去打把游戏先5 小时前
ESP32学习笔记(基于IDF):IOT应用——WIFI连接
笔记·单片机·嵌入式硬件·mcu·物联网·学习·esp32