Linux系统编程--IO系统调用

news2025/1/15 4:08:01

文章目录

  • 一、I/O系统调用
    • 1.open() 打开文件
      • 1.1 所需基础知识
      • 1.2. open() 详解
      • 1.3 示例代码
    • 2.read() 读取文件
      • 2.1.基础知识
      • 2.2.read() 详解
      • 2.3. 读入所有字节
    • 3.write() 写文件
      • 3.1. 基础背景知识
      • 3.2.write() 详解
      • 3.3.示例代码
      • 3.4.注意点
        • 3.4.1.同步IO
          • 1. fsync() 和fdatasync()
          • 2.sync()
    • 4.close() 关闭文件
    • 5.lseek() 查找
    • 6.定位读写
        • 6.1代码示例
    • 7.截短文件
      • 7.1 代码详解
    • 8.I/O多路复用
      • 8.1.基础知识
      • 8.2.select() 实现同步I/O多路复用

一、I/O系统调用

1.open() 打开文件

1.1 所需基础知识

Linux遵循一切皆是文件的理念,因此,很多的交互工作都是通过读取和写入文件来完成。
文件必须被打开才能被访问。文件可以以只读方式或者只写方式打开,或者两者兼有。一个打开的文件通过唯一的文件描述符进行引用,该描述符是打开文件的元数据至其本身的映射在Linux内核中,这个描述符,用一个整数表示 (int) ,简写为fd。文件描述符在用户空间中共享,允许用户程序用文件描述符直接访问文件。

1.2. open() 详解

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//调用成功返回文件描述符,失败返回-1

int open (const char* name, int flags);

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

flags参数必须是一下之一:

参数含义
O_RDONLY只读
O_WRONLY只写
O_RDWR读写模式

flags参数可以和以下一个或多个值进行按位或运算,用以修改打开文件请求的行为。

参数含义
O_APPEND追加模式打开,文件位置指针将被置于文件末尾
O_ASYNC当指定文件可写或者可读时产生一个信号(默认SIGIO)
O_CREAT当指定的name文件不存在时,将由内核来创建
O_DIRECT打开文件用于直接I/O
O_DIRECTORY如果name不是目录,open()调用将会失败,这个标志用于opendir()内部使用
O_EXCL和O_CREAT一起给出的时候,如果name给定的文件已经存在,则open()调用失败,用来防止文件创建时出现竞争
O_LARGEFILE给定文件打开时使用64位偏移量,这样大于2G的文件也能被打开
O_NOFOLLOW如果name是一个符号链接,open()调用会失败
O_NONBLOCK如果可以,文件将在非堵塞模式下打开
O_SYNC打开文件用于同步IO
O_TRUNC如果文件存在,且为普通文件,并允许写,将文件的长度截断为0

1.3 示例代码

以打开 /etc/services 文件为例

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

int main()
{

	int fd;
	fd = open("/etc/services",O_RDONLY);
  	if(fd == -1)
 	{
  	 cerr << "open() failed";
  	 return 0;
 	}

  else
	{

	cout << "open success" << endl;
	cout << "fd = " << fd << endl;
	}

	close(fd);
	return 0;

}

效果图:
在这里插入图片描述

2.read() 读取文件

2.1.基础知识

ssize_t 其实是有符号整数类型
size_t 是无符号整数类型

2.2.read() 详解

#include <unistd.h>

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

2.2.1.功能:

该系统调用从有fd指向的文件的当前偏移量至多读len个字节到buf中。成功返回写入buf中的字节数。出错则返回-1,并设置errno

2.2.2.返回结果情况:

1. 调用返回一个等于len的值。
所有len个被读取字节存储在buf中。结果和预期一致。

以 读取 /etc/services 文件为例,读取文件中20个字节

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

int main()
{

	int fd;
	
	fd = open("/etc/services",O_RDONLY);  //以只读方式打开文件

	if(fd == -1)		//文件打开失败,并提示错误
	{
		cerr << " open failed ";
		return 0;

	}

	char* buf;
	ssize_t length;
	length = read(fd, buf, 20);
	
	if(length == -1)  //读文件失败
	{
	
		cerr << "read failed" << endl;
		close(fd);
        return 0;

	}

	else
	cout << "读取字节数 = " << length << endl;  //读文件成功
	close(fd);
	return 0;	

}

效果图:
在这里插入图片描述

2.调用返回一个大于0,但是小于len的值。

  • 读数据期间,被一个信号打断了读取的过程。
  • 读取的数据,本身就少于指定的字节数。
  • 读取的过程,出现了一个错误。

测试样例 : 测试文件数据本身少于指定读取字节数的情况。文件本身有abcdef,而指定读取20字节数据。

sample.txt 文件内容

abcdef
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

int main()
{

	int fd;
	
	fd = open("sample.txt",O_RDONLY);  //以只读方式打开文件

	if(fd == -1)		//文件打开失败,并提示错误
	{
		cerr << " open failed ";
		return 0;

	}

	char* buf;
	ssize_t length;
	length = read(fd, buf, 20);
	
	if(length == -1)  //读文件失败
	{
	
		cerr << "read failed" << endl;
		close(fd);
        return 0;

	}

	else
	cout << "读取字节数 = " << length << endl;  //读文件成功

	close(fd);
	return 0;	

}

效果图:
在这里插入图片描述
文件内容为abcdef ,6字节的数据,为什么读取字节数为7?

我们打印一下读取的数据
在这里插入图片描述

内容也正确,是6个字节。为什么显示7? 难道是最后的 ‘\0’ 占了一个字节吗?

3.调用返回0 这标志着EOF。没有可以读入的数据

测试:
读一个空文件。

代码: a.txt 为空文件

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

int main()
{

	int fd;
	
	fd = open("a.txt",O_RDONLY);

	if(fd == -1)
	{
		cerr << " open failed ";
		return 0;

	}

	char* buf;
	ssize_t length;
	length = read(fd, buf, 20);
	
	if(length == -1)
	{
	
		cerr << "read failed" << endl;
		close(fd);
        return 0;

	}

	else
	cout << "读取字节数 = " << length << endl;
    cout << "内容 " << buf << endl;
	close(fd);
	return 0;	

}

效果图:
在这里插入图片描述
4.调用返回-1
在这里插入图片描述

2.3. 读入所有字节

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

using namespace std;

int main()
{

	int fd;
	
	fd = open("/etc/services",O_RDONLY);

	if(fd == -1)
	{
		cerr << " open failed ";
		return 0;

	}

	char* buf;
	ssize_t ret;
	int len = 10240;  //指明读取的大小

	while(len != 0 && (ret = read(fd, buf, len) != 0))
	{
	
		if(ret == -1)
		{
			if(errno == EINTR) 
				continue;

			cerr << "read failed" << endl;
        	break;

		}

		else
		{
			len -= ret;
			buf += ret;
			
		}
		
	}
	close(fd);
	cout << "len=" << len << endl; //测试程序是否真正的读好几次。
	return 0;	

}

效果图:
在这里插入图片描述

3.write() 写文件

3.1. 基础背景知识

一个write() 调用从由文件描述符fd引用文件的当前位置开始,将buf中至多count个字节写入文件中。不支持定位的文件(像字符设备)总是从“开头”开始写。
成功时,返回写入字节数,并更新文件位置。错误时,返回-1,并将errno设置为相应的值。

3.2.write() 详解

#include <unistd.h>

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

3.3.示例代码

测试: 从 /etc/services 文件中读取100字节,并把这100字节写入到a.txt 文件中。

text 1.不以追加模式写

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

using namespace std;

int main()
{

	int fd;
	fd = open("/etc/services", O_RDONLY);
	if(fd == -1)
	{
		cerr << "open()" << endl;
		return 0;
	}

	char* buf;
	int len = read(fd,buf,100);
	if(len == -1)
	{
		cerr << "read()" << endl;
		close(fd);
		return 0;
	}
	
	int fd1;
	fd1 = open("a.txt",O_WRONLY);
    if(fd1 == -1)
	{
	
		cerr <<" open(a.txt)" << endl;
		close(fd);
		return 0;

	}

	int count = write(fd1,buf,len);
	if(count == -1)
	{
		perror("write()");
		close(fd);
		return 0;
	}
	else if(count != len)
	{
		cerr << "write() 写入字数不对";
		close(fd);
		close(fd1);
		return 0;	
	}
	
	cout << "ok" << endl;
	return 0;
}

效果图:
在这里插入图片描述

text2.追加模式进行写,也就是以追加模式打开文件

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

using namespace std;

int main()
{


	const char* buf = "abcdefghijklmnopqrstuvwxyz";
	
	int fd1;
	fd1 = open("a.txt",O_WRONLY |O_APPEND);
    if(fd1 == -1)
	{
	
		cerr <<" open(a.txt)" << endl;
		return 0;

	}

	int count = write(fd1,buf,strlen(buf));
	if(count == -1)
	{
		perror("write()");
		close(fd1);
		return 0;
	}
	else if(count != strlen(buf))
	{
		cerr << "write() 写入字数不对";
		close(fd1);
		return 0;	
	}
	
	cout << "ok" << endl;
	return 0;
}

效果图:
在这里插入图片描述

3.4.注意点

当一个write()调用返回时,内核已将所提供的缓冲区数据复制到了内核缓冲区中。但没有保证数据已写到了目的文件。你可以强制文件缓存写回,甚至可以将所有的写操作同步。

3.4.1.同步IO

1. fsync() 和fdatasync()

最简单的确认数据写入磁盘的方法是使用fsync()系统调用,

#include <unistd.h>

int fsync(int fd);

调用fsync()可以保证fd对应文件的脏数据回写到磁盘上。

该调用回写数据以及建立的时间戳和inode中的其它属性等元数据。在驱动器确认数据已经全部写入之前不会返回。

#include <unistd.h>

int fdatasync(int fd);

这个系统调用完成的事情和fsync()一样,区别在于它仅写入数据。不保证元数据同步到磁盘上。故此可能快一些。一般来说这就够用了。

返回值情况
成功时,两个调用都返回0.失败时,都返回-1.并将errno设置为一下三个值之一:

名称含义
EBADF给定的文件描述符不是一个可以写入的合法描述符
EINVAL给定的文件描述符对应的对象不支持同步
EIO在同步时发生了一个底层I/O错误
2.sync()

sync()系统调用可以用来对磁盘上所有的缓冲区进行同步。尽管效率不高,但仍然被广泛使用。

#include <unistd.h>

void sync();

该函数没有参数,也没有返回值。它总是成功返回,并确保所有的缓冲区(包括数据和元数据)都可以写入磁盘。

4.close() 关闭文件

程序完成对某个文件的操作后,可以使用close()系统调用将文件描述符和对应的文件解除关联。

#include <unistd.h>

//调用成功返回0.错误返回-1,并设置errno为相应值
int close(int fd); 

5.lseek() 查找

lseek()系统调用能够对给定文件描述符引用的文件位置设置指定值。除了更新文件位置,没有其它的行为,并无论如何不初始化任何I/O

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t pos, int origin);

lseek() 的行为依赖于初始参数,可以为一下值之一:

名称含义
SEEK_CUR当前文件位置fd设置为当前值加上pos,pos可以为负值,零或正值
SEEK_END当前文件位置fd设置为当前文件长度加上pos,pos可以为负值,零或正值
SEEK_SET当前文件位置fd设置为pos。

调用成功时返回新文件位置。错误时返回-1,并设置适当的errno值。
在这里插入图片描述

6.定位读写

Linux提供了两种read()和write()的变体来代替lseek(),每个调用都以需要读写的文件位置为参数。完成时,不修改文件位置。

#define _XOPEN_SOURCE 500

#include <unistd.h>

ssize_t pread(int fd, void* buf, size_t count, off_t pos);
ssize_t pwrite(int fd,const void* buf,size_t count, off_t pos);

错误码
成功时,两个调用返回读或写的字节数。出错时,二者均返回-1

6.1代码示例

示例文件 a.txt,内容为

abcdefghigklmnopqrstuvwxyz
ABCDEFGHIGKLMNOPQRSTUVWXYZ

1.测试pread

距文件开始位置5的位置开始读26个字节

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

int main()
{

	int fd;
	
	fd = open("a.txt",O_RDONLY);

	if(fd == -1)
	{
		cerr << " open failed ";
		return 0;

	}

	char* buf;
	ssize_t length;
	length = pread(fd, buf, 26,5);
	
	if(length == -1)
	{
	
		cerr << "pread failed" << endl;
        return 0;

	}

	else
	cout << "读取字节数 = " << length << endl;
	cout << buf << endl;
	return 0;	

}

效果图:
在这里插入图片描述

2.测试pwrite()

从距文件开始位置的26处,写入-Hello,Word-

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

using namespace std;

int main()
{

	const char* buf = "-Hello,Word-";
	
	int fd1;
	fd1 = open("a.txt",O_WRONLY);
    if(fd1 == -1)
	{
	
		cerr <<" open(a.txt)" << endl;
		
		return 0;

	}

	int count = pwrite(fd1,buf,strlen(buf),26);
	if(count == -1)
	{
	
		perror("write()");
		return 0;
	}
	else if(count != strlen(buf))
	{
		cerr << "write() 写入字数不对";
	
		close(fd1);
		return 0;	
	}
	
	cout << "ok" << endl;
	return 0;
}

效果图:
在这里插入图片描述
如果原来位置上有数据,那么写入的数据会把原来的数据覆盖掉

7.截短文件

7.1 代码详解

#include <sys/types.h>

int ftruncate (int fd, off_t len);
#include <unistd.h>
#include <sys/types.h>

int truncate (const char* path, off_t len);

两个系统调用都将文件截短到len指定的长度。
二者都在成功时返回0,错误时返回-1,并设置errno为相应值。

8.I/O多路复用

8.1.基础知识

在这里插入图片描述

I/O多路复用允许应用在多个文件描述符上同时堵塞,并在其中某个可以读写时收到通知。

I/O多路复用的设计遵循以下原则:

  1. 当任何文件描述符准备好I/O时通知我
  2. 在一个或更多文件描述符就绪前始终处于睡眠状态
  3. 唤醒:哪个准备好了?
  4. 在不阻塞的情况下处理所有I/O就绪的文件描述符
  5. 返回第一步,重新开始

8.2.select() 实现同步I/O多路复用

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select (int n, fd_set* readfds, fd_set* writefds,fd_set* exceptfds,
			struct timeval* timeout);


FD_CLR(int fd, fd_set* set);
FD_ISSET(int fd, fd_set* set);
FD_SET(int fd, fd_set* set);
FD_ZERO(fd_set* set);
监测的文件描述符功能
监测 readfds 集合中的文件描述符确认其中是否有可读数据
监测 writefds 集合中的文件描述符确认其中是否有一个写操作可以不阻塞地完成
监测 exceptefds 中的文件描述符确认其中是否出现异常发生或者出现带外数据
指定集合为NULLselect对此不监听

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

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

相关文章

MySQL高可用搭建方案之(MMM)

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 注意&#xff1a;这篇转载文章&#xff0c;非原创 首发博客地址 原文地址 前言 MySQL的高可用有很多种&#xff0c;有我们经常说的MMM架构、MHA架构、…

内网隧道代理技术(二十三)之 DNS隧道反弹Shell

DNS隧道反弹Shell DNS隧道 DNS协议是一种请求、应答协议,也是一种可用于应用层的隧道技术。DNS隧道的工作原理很简单,在进行DNS查询时,如果查询的域名不在DNS服务器本机缓存中,就会访问互联网进行查询,然后返回结果。如果在互联网上有一台定制的服务器,那么依靠DNS协议…

标准C库IO函数和Linux系统IO函数

linux系统的io函数更加偏底层&#xff0c;更加建议使用C库的函数&#xff0c;效率较高&#xff08;有缓冲区&#xff09; 磁盘满了或者手动fflush或者关闭文件才会io一次&#xff0c;效率提高&#xff0c;但是linux没有缓冲区 主要通过file *fp指针操作文件&#xff0c;文件描…

COSCon'23 社区召集令

一年一度的开源盛会&#xff0c;COSCon23 第八届中国开源年会&#xff0c;将于10月28~29日&#xff0c;在四川成都市高新区菁蓉汇召开&#xff01;本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01; 三年新冠疫情没有将我们击垮&#xff0c;开源…

记录socket的使用 | TCP/IP协议下服务器与客户端之间传送数据 | java学习笔记

谨以此篇&#xff0c;记录TCP编程&#xff0c;方便日后查阅笔记 注意&#xff1a;用BufferedWriter write完后&#xff0c;一定要flush&#xff1b;否则字符不会进入流中。去看源码可知&#xff1a;真正将字符写入的不是write()&#xff0c;而是flush()。 服务器端代码&#…

运维学习之部署Alertmanager-0.24.0

参考《监控系统部署prometheus基本功能》先完成prometheus部署。 参考《运维学习之采集器 node_exporter 1.3.1安装并使用》安装node_exporter。 下载 nohup wget https://github.com/prometheus/alertmanager/releases/download/v0.24.0/alertmanager-0.24.0.linux-amd64.ta…

SecureCRT ssh链接服务器

SecureCRT通过密钥进行SSH登录 说明&#xff1a; 一般的密码方式登录容易被密码暴力破解。所以一般我们会将 SSH 的端口设置为默认22以外的端口&#xff0c;或者禁用root账户登录。其实可以通过密钥登录这种方式来更好地保证安全。 密钥形式登录的原理是&#xff1a;利用密钥…

day34 集合总结

集合总结 一、概述 作用&#xff1a;存储对象的容器&#xff0c;代替数组的&#xff0c;使用更加的便捷 所处的位置&#xff1a;java.util 体系结构 二、Collection 内部的每一个元素都得是引用数据类型 常用方法 add(Object o) 添加元素 addAll(Collection c) 将指定集…

【LeetCode周赛】LeetCode第362场周赛

LeetCode第362场周赛 与车相交的点判断能否在给定时间到达单元格将石头分散到网格图的最少移动次数 与车相交的点 给你一个下标从 0 开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i&#xff0c;nums[i] [starti, endi] &#xff0c;其中 starti 是第 i…

讯飞星火认知大模型,多种应用一键体验整合

分享几个可以&#xff0c;直接可以使用的AI应用&#xff0c;依托于讯飞星火大模型实现的&#xff1b; 现在讯飞星火认知大模型&#xff0c;使用已经完全开放&#xff0c;可以直接使用&#xff1b; AI抖音商品种草文案 功能&#xff1a; 通过将商品信息输入到讯飞星火AI大模…

IntelliJ IDEA工具常用插件汇总

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;IntelliJ IDEA 、常用插件☀️每日 一言&#xff1a;人的一生其实都在偏见和走出偏见中度过 文章目录 一、前言二、Plugins1.Key Promoter X2.CodeGlance3.Git Integration&#xff1a;4.Markdow…

SpringBoot整合Mybatis-Plus(含自动配置分析)

目录 1. Mybatis-Plus介绍2. 创建Mysql表和添加测试数据3. 添加pom.xml依赖4. 自动配置分析5. 代码实现5.1 User类实现5.2 指定MapperScan扫描路径5.3 Mapper接口实现5.4 Service实现5.5 UserMapper测试 1. Mybatis-Plus介绍 Mybatis-Plus是一个Mybatis的增强工具&#xff0c;…

Rich Bowen: 无论你在创造什么,最终交付的是信任。

早在开源被我们称之为开源&#xff0c;Rich Bowen 就已经参与其中。作为 Apache 软件基金会的成员&#xff0c;Rich 目前担任董事会成员、会议副总裁。此外&#xff0c;他还是亚马逊云科技的开源策略师。这些多重角色赋予了他对开源的更广泛和深刻的理解。 在他于 2023 年 Com…

远程连接mysql报错“Host xxx is not allowed to connect to this MySQL server“解决办法

在一台服务器上安装了mysql后使用dbeaver远程连接不上报错&#xff1a; 可以看到&#xff0c;报错原因是不许远程连接到mysql服务器 所以&#xff0c;修改访问权限。 首先&#xff0c;进入mysql命令行&#xff0c;查看访问权限&#xff1a; use mysql; select user,host from…

【ProxySql】Mysql如何实现读写分离?看这一篇就够了

&#x1f332;其他工具对比 其实市面上有很多关于读写分离的优秀的工具&#xff0c;例如 工具优势劣势ProxySQL- 高性能的负载均衡和连接池管理- 支持MySQL和MariaDB- 灵活的配置和规则定义- 只支持MySQL和MariaDB数据库- 功能相对专注&#xff0c;适用性可能有限- 学习和配置…

K线学习001-早晨之星1

K线定义 早晨之星&#xff0c;顾名思义&#xff1a;就是在太阳尚未升起的时候&#xff0c;黎明前最黑暗的时刻&#xff0c;一颗明亮的启明星在天边指引着那些走向光明的夜行人&#xff0c;前途当然看好。 早晨之星&#xff0c;即预示着跌势将尽&#xff0c;大盘处于拉升的前夜&…

DGIOT 智慧车间机床设备数据采集

**[小 迪 导读]**&#xff1a;DGiot掌上工厂是一款基于微信小程序的应用&#xff0c;本次主要介绍机床设备数据采集功能&#xff0c;旨在帮助施工员高效地收集车间中每台机床的信息、水平座标轴数据和照片。该小程序提供了简单易用的界面和功能&#xff0c;使施工员能够方便地记…

NameError: name ‘add start docstrings to callable‘ is not defined解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

中国又一款3纳米芯片将量产,高通被前后夹击,难怪要降价求售了

高通作为手机芯片市场的领先者&#xff0c;曾长达十多年位居手机芯片市场的王者地位&#xff0c;不过从2020年以来就已被中国芯片企业超越&#xff0c;至今未能挽回&#xff0c;而近期中国一家手机企业的9000S芯片推出更给予高通重击&#xff0c;可能导致高通在中国手机芯片市场…