C语言——文件描述符、系统调用操作文件

news2024/12/25 8:54:59

文件描述符

在Unix-like操作系统中,文件描述符(file descriptor)是一个用于标识打开文件或I/O设备的整数值。它是对底层文件系统的抽象,用于在应用程序和操作系统之间传递文件信息。

文件描述符是一个非负整数,通常是一个小整数。在C语言中,文件描述符被表示为int类型。

每个进程在其打开的文件或设备上都有一组文件描述符,它们是连续的、非重复的整数值。当一个文件或设备被打开时,操作系统会为该文件分配一个文件描述符,并将其返回给应用程序。

常见的文件描述符包括:

  • 标准输入(stdin):文件描述符为0,宏为STDIN_FILENO,通常用于接收应用程序的输入。
  • 标准输出(stdout):文件描述符为1,宏为STDOUT_FILENO,通常用于输出应用程序的结果。
  • 标准错误(stderr):文件描述符为2,宏为STDERR_FILENO,通常用于输出应用程序的错误信息。
应用程序可以使用文件描述符进行各种文件和I/O操作,例如读取文件内容、写入数据、关闭文件等。文件描述符作为操作系统和应用程序之间的桥梁,允许应用程序与文件系统和其他设备进行交互。

在Linux的世界里,一切设备皆文件。我们可以调用系统中 的 I/O函数,对文件进行相应的操作( open()、close()、write() 、read() 等)。

打开现有文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号。文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。

程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这三个文件描述符是默认打开的。

#define STDIN_FILENO  0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符

在程序运行起来后打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。
在这里插入图片描述
最大打开的文件个数
Linux中一个进程最多只能打开NR_OPEN_DEFAULT(即1024)个文件,故当文件不再使用时应及时调用 close() 函数关闭文件。
• 查看当前系统允许打开最大文件个数:

cat /proc/sys/fs/file-max

• 当前默认设置最大打开文件个数1024

ulimit -a

• 修改默认设置最大打开文件个数为4096

ulimit -n 4096

16. 常用文件IO函数(以下均为系统调用)

Linux的man手册共有以下几个章节:

  1. Standard commands (标准命令)
  2. System calls (系统调用)
  3. Library functions (库函数)
  4. Special devices (设备说明)
  5. File formats (文件格式)
  6. Games and toys (游戏和娱乐)
  7. Miscellaneous (杂项)
  8. Administrative Commands (管理员命令)
    使用系统调用函数依然是需要引入头文件的,要引入哪些可以通过man命令查看
man 2 命令

16.1 open函数

man 2 open
在这里插入图片描述

#include <sys/types.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);
功能:
    打开文件,如果文件不存在则可以选择创建。
参数:
    pathname:文件的路径及文件名,可以是相对路径或绝对路径
    flags:打开文件的行为标志,用于指定文件的打开方式和操作选项。必选项O_RDONLY\O_WRONLY\O_RDWR
    mode:权限参数,用于指定新创建文件的访问权限,只有在使用O_CREAT标志创建文件时才有效。
返回值:
    成功:成功返回打开的文件描述符
    失败:-1

open()函数返回一个非负整数的文件描述符(file descriptor),用于后续对文件的读写操作。如果打开文件失败,函数返回-1,并可以使用errno全局变量获取具体的错误码。
flags详细说明
必选项:

取值含义
O_RDONLY以只读的方式打开
O_WRONLY以只写的方式打开
O_RDWR以可读、可写的方式打开

可选项,和必选项按位或起来

取值含义
O_CREAT文件不存在则创建文件,使用此选项时需使用mode说明文件的权限
O_EXCL与O_CREAT一起使用,用于确保创建文件时文件不存在。如果同时指定了O_CREAT,且文件已经存在,则出错
O_TRUNC如果文件存在,则清空文件内容
O_APPEND写文件时,数据添加到文件末尾
O_NONBLOCK对于设备文件, 以O_NONBLOCK方式打开可以做非阻塞I/O

mode补充说明

  1. 文件最终权限:mode & ~umask
  2. shell进程的umask掩码可以用umask命令查看

Ø umask:查看掩码(补码)
Ø umask mode:设置掩码,mode为八进制数
Ø umask -S:查看各组用户的默认操作权限

取值八进制含义
S_IRWXU00700文件所有者的读、写、可执行权限
S_IRUSR00400文件所有者的读权限
S_IWUSR00200文件所有者的写权限
S_IXUSR00100文件所有者的可执行权限
S_IRWXG00070文件所有者同组用户的读、写、可执行权限
S_IRGRP00040文件所有者同组用户的读权限
S_IWGRP00020文件所有者同组用户的写权限
S_IXGRP00010文件所有者同组用户的可执行权限
S_IRWXO00007其他组用户的读、写、可执行权限
S_IROTH00004其他组用户的读权限
S_IWOTH00002其他组用户的写权限
S_IXOTH00001其他组用户的可执行权限

16.2 close函数

在这里插入图片描述

#include <unistd.h>
int close(int fd);
功能
    关闭已打开的文件
参数:
    fd: 文件描述符,open()的返回值
返回值:
    成功:0
    失败:-1, 并设置errno

需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。

但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

16.3 write函数

在这里插入图片描述

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
    把指定数目的数据写到文件(fd),将buf指向的数据写入到文件描述符fd所指向的文件或设备中。
参数:
    fd: 文件描述符,可以是标准输出(STDOUT_FILENO,值为1)、标准错误(STDERR_FILENO,值为2),或者是通过open函数打开的文件描述符。
    buf: 数据首地址,要写入数据的缓冲区的指针。
    count: 写入数据的长度(字节)
返回值:
    成功:实际写入数据的字节个数
    失败: - 1

write() 函数返回一个 ssize_t 类型的值,表示成功写入的字节数。返回值有以下几种情况:

  • 如果返回值大于等于 0,则表示成功写入了指定字节数。
  • 如果返回值为 -1,则表示发生了错误。此时,可以通过 errno 全局变量来获取具体的错误代码。
  • 如果返回值为 0,则表示没有写入任何数据。这通常发生在写入到非阻塞文件描述符时,当写入缓冲区已满或遇到信号中断时。
需要注意的是,write() 函数可能不会一次性写入所有请求的字节数。在某些情况下,它可能会写入部分数据并返回写入的字节数。如果需要确保全部数据写入,可能需要多次调用 write() 直到写入的字节数达到预期。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
//write file
int main(void) {
	int fd=-1;
    int ret=-1;
    char *str="hello test";
//打开或者创建一个文件,返回文件描述符。文件名就叫做txt
    fd=open("txt",O_WRONLY | O_CREAT,0644);
    if(-1==fd){
    	perror("open");
        return 1;
    }
    printf("fd=%d\n",fd);
//往文件中写入内容
    ret=write(fd,str,strlen(str));
    if (-1==ret){
        perror("write");
    	return 1;
    }
//长度应该是hello test共10个char占10个字节
    printf("write len:%d\n",ret);
    close(fd);
    return 0;
}

16.4 read函数

在这里插入图片描述

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:
    把指定数目的数据读到内存(缓冲区)。从文件描述符fd所指向的文件或设备中读取数据,并将其存储到buf所指向的缓冲区中。
参数:
    fd: 文件描述符,它可以是标准输入(STDIN_FILENO,值为0)、标准输出(STDOUT_FILENO,值为1)、标准错误(STDERR_FILENO,值为2),或者是通过open函数打开的文件描述符。
    buf: 内存首地址,用于存储读取数据的缓冲区的指针
    count: 读取的字节个数
返回值:
    成功:实际读取到的字节个数
    失败: - 1

read函数返回实际读取的字节数,如果返回值为0,则表示已到达文件末尾,如果返回值为-1,则表示出现错误。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
//read file
#define SIZE 128
int main(void) {
	int fd=-1;
	int ret=-1;
	char *str="hello test";
 	//打开或者创建一个文件,返回文件描述符。文件名就叫做txt
	fd=open("txt",O_RDONLY);
	if(-1==fd){
    	perror("open");
    	return 1;
     }
	printf("fd=%d\n",fd);
	//读取文件中内容
	char buf[SIZE];
	memset(buf,0,SIZE);
	ret=read(fd,buf,SIZE);
	if (-1==ret){
		perror("read");
		return 1;
	}
	//长度应该是hello test共10个char占10个字节
	printf("read len:%d\n",ret);
	close(fd);
	return 0;
}

阻塞和非阻塞的概念
读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。

从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。

同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

【注意】阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。

以非阻塞方式打开文件程序示例:

#include <unistd.h> //read
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h> //EAGAIN

int main() {
    // /dev/tty --> 当前终端设备
    // 以不阻塞方式(O_NONBLOCK)打开终端设备,0 1 2进程启动就被占用了,所以最小fd为3
    int fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);
    char buf[10];
    int n;
    n = read(fd, buf, sizeof(buf));
    if (n < 0) {
        // 如果为非阻塞,但是没有数据可读,此时全局变量 errno 被设置为 EAGAIN
        if (errno != EAGAIN) {
            perror("read /dev/tty");
            return -1;
        }
        printf("没有数据\n");
    }
    return 0;
}
#include <stdio.h>
int main(void) {
	//阻塞
	char ch=-1;
	ch=getchar();
	putchar(ch);
}

16.5 lseek函数

在这里插入图片描述

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:
    改变文件的偏移量
参数:
    fd:文件描述符
    offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。
    whence:其取值如下:
        SEEK_SET:从文件开头移动offset个字节
        SEEK_CUR:从当前位置移动offset个字节
        SEEK_END:从文件末尾移动offset个字节
返回值:
    若lseek成功执行, 则返回新的偏移量
    如果失败, 返回-1

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。

读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1651845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

这个Python库Streamlit,5分钟内搭建可视化WEB应用

在数据科学的世界里&#xff0c;将分析结果快速、直观地呈现给非技术背景的决策者&#xff0c;是一项重要的技能。而Streamlit&#xff0c;这个开源的Python库&#xff0c;正是为此而生。它允许数据科学家和工程师通过少量的代码&#xff0c;快速创建和分享数据应用。今天&…

用HAL库改写江科大的stm32入门例子_9-1 串口发送

设置串口&#xff1a; 选项说明&#xff1a; 写一个串口发送函数&#xff1a; // serial send string function void serial_send_string(char *str) {HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 1000); } main函数中调用发送信息&#xff1a; uint8_t dat…

AIGC实战——多模态模型DALL.E 2

AIGC实战——多模态模型DALL.E 2 0. 前言1. 模型架构2. 文本编码器3. CLIP4. 先验模型4.1 自回归先验模型4.2 扩散先验模型 5. 解码器5.1 GLIDE5.2 上采样器 6. DALL.E 2 应用6.1 图像变体6.2 先验模型的重要性6.3 DALL.E 2 限制 小结系列链接 0. 前言 DALL.E 2 是 OpenAI 设计…

docker搭建代码审计平台sonarqube

docker搭建代码审计平台sonarqube 一、代码审计关注的质量指标二、静态分析技术分类三、sonarqube流程四、快速搭建sonarqube五、sonarqube scanner的安装和使用 一、代码审计关注的质量指标 代码坏味道 代码规范技术债评估 bug和漏洞代码重复度单测与集成 测试用例数量覆盖率…

WPF 图片显示某一部分区域

效果图&#xff1a; 代码&#xff1a; <Image Width"32"HorizontalAlignment"Right"Height"32"Source"../../Resources/Images/BLUEWOLF.jpg"><Image.Clip><PathGeometry><PathFigure StartPoint"32,32&quo…

区块链 | NFT 相关论文:Preventing Content Cloning in NFT Collections(三)

&#x1f436;原文&#xff1a; Preventing Content Cloning in NFT Collections &#x1f436;写在前面&#xff1a; 这是一篇 2023 年的 CCF-C 类&#xff0c;本博客只记录其中提出的方法。 F C o l l N F T \mathbf{F_{CollNFT}} FCollNFT​ and Blockchains with Native S…

基于springboot+mybatis+vue的项目实战之(后端+前后端联调)

步骤&#xff1a; 1、项目准备&#xff1a;创建数据库&#xff08;之前已经创建则忽略&#xff09;&#xff0c;以及数据库连接 2、建立项目结构文件夹 3、编写pojo文件 4、编写mapper文件&#xff0c;并测试sql语句是否正确 5、编写service文件 6、编写controller文件 …

二、双fifo流水线操作——verilog练习与设计

文章目录 一、案例分析二、fifo_ctrl模块设计2.1 波形设计&#xff1a;2.2 代码实现2.2.1 fifo_ctrl2.2.2 顶层文件top_fifo_ctrl&#xff08;rx和tx模块省略&#xff09;2.2.3 仿真文件tb_fifo_ctrl 2.3波形仿真 一、案例分析 案例要求&#xff1a;写一个 fifo 控制器&#x…

浅谈消息队列和云存储

1970年代末&#xff0c;消息系统用于管理多主机的打印作业&#xff0c;这种削峰解耦的能力逐渐被标准化为“点对点模型”和稍复杂的“发布订阅模型”&#xff0c;实现了数据处理的分布式协同。随着时代的发展&#xff0c;Kafka&#xff0c;Amazon SQS&#xff0c;RocketMQ&…

【EasySpider】EasySpider+mysql执行配置异常

问题 使用易采集工具操作时候&#xff0c;遇到一个执行异常&#xff0c;后来发现没有选择数据类型 Loading stealth.min.js MySQL config file path: ./mysql_config.json 成功连接到数据库。 Successfully connected to the database. Traceback (most recent call last):…

联发科技发布天玑9300+旗舰5G生成式AI芯片 | 最新快讯

5 月 7 日消息&#xff0c;联发科技今天举办了天玑开发者大会 2024。大会上&#xff0c;联发科技开启了“天玑 AI 先锋计划”&#xff0c;联合业界生态企业发布了《生成式 AI 手机产业白皮书》&#xff0c;分享了生成式 AI 端侧部署的解决方案“天玑 AI 开发套件”。同时&#…

WPF中页面加载时由于TreeView页面卡顿

示例&#xff1a;右侧界面的数据根据左侧TreeView的选项加载不同的数据&#xff0c;页面加载时会把所有的数据加载一遍&#xff0c;导致页面卡顿。 解决办法&#xff1a; <Setter Property"IsSelected" Value"{Binding IsSelected}"/>

【@ohos.events.emitter (Emitter)】

ohos.events.emitter (Emitter) 本模块提供了在同一进程不同线程间&#xff0c;或同一进程同一线程内&#xff0c;发送和处理事件的能力&#xff0c;包括持续订阅事件、单次订阅事件、取消订阅事件&#xff0c;以及发送事件到事件队列的能力。 说明&#xff1a; 本模块首批接…

Windows下安装人大金仓数据库

1、点击安装包进行安装 2、双击进行安装 3、点击确定 4、接着选择下一步 5、勾选接收 6、选择授权文件 7、显示授权文件信息 8、选择安装位置 9

SEO之高级搜索指令(二)

初创企业需要建站的朋友看这篇文章&#xff0c;谢谢支持&#xff1a; 我给不会敲代码又想搭建网站的人建议 新手上云 &#xff08;接上一篇。。。。&#xff09; 5 、inanchor: inanchor:指令返回的结果是导入链接锚文字中包含搜索词的页面。百度不支持inanchor:。 比如在 Go…

解读TO B软件企业财报:回看2023,展望2024

随着2023年全年财报的陆续公布&#xff0c;TO B软件企业的未来方向愈发清晰。这些企业不仅在技术革新、财务稳健、客户多元化、AI应用和全球化战略上取得了显著进展&#xff0c;更在激烈的市场竞争中展现出了顽强的生命力。 然而&#xff0c;这一切的成就并非没有代价&#x…

[虚拟机+单机]梦幻契约H5修复版_附GM工具

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 视频演示 [虚拟机单…

领域驱动设计架构演进

领域驱动设计由于其强调对领域的深入理解和关注业务价值,其架构演进依赖于领域的变化和特定领域中的技术实践。 初始阶段 一个单体架构,所有的功能都集成在一个应用程序中,领域模型可能还不完全清晰,甚至并未形成。这个阶段主要是为了验证产品的可行性,快速迭代并尽快推…

开抖音小店需要交多少保证金?全类目选择,一篇了解

哈喽~我是电商月月 做抖音小店前大家都会搜索“入驻抖音小店需要准备什么东西&#xff1f;”其中就包含了一项&#xff1a;类目保证金的缴纳 那到底要交多少钱&#xff1f;很多新手朋友还是不太了解 今天我就给大家解答这个问题&#xff0c;首先&#xff0c;我们要知道抖店的…

华为eNSP综合实验-网络地址转换

实验完成之后,在AR1的g0/0/1接口抓包,查看地址转换 实现私网pc访问公网pc 实验命令展示 SW1: vlan batch 12 #创建vlan interface e0/0/1 #进入接口配置vlan端口 port link-type access port default vlan 12 q interface e0/0/2 #进入接口配置vlan端口 port link-type ac…