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 函数:用于强制将缓冲区中的内容写入文件

系统调用与库函数调用

相关推荐
超级大只老咪10 小时前
快速进制转换
笔记·算法
嵩山小老虎10 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模11 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
a413244711 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.12 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
一只自律的鸡13 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
17(无规则自律)13 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
!chen13 小时前
linux服务器静默安装Oracle26ai
linux·运维·服务器
ling___xi13 小时前
《计算机网络》计网3小时期末速成课各版本教程都可用谢稀仁湖科大版都可用_哔哩哔哩_bilibili(笔记)
网络·笔记·计算机网络
REDcker13 小时前
Linux 文件描述符与 Socket 选项操作详解
linux·运维·网络