yuchuan_Linux_C 编程之七系统IO函数
一、整体大纲
二、 系统IO函数
1. 一些概念
文件描述符
PCB
C库函的IO缓冲区
1) 文件描述符
int 类型
一个进程最多可打开多少文件
2) pcb
进程控制块
在其中有一个文件描述符表 -- 数组[1024]
C库IO函数工作流程:
pcb和文件描述符:
2. 虚拟地址空间
虚拟地址空间就是程序启动起来之后从硬盘上会有一块虚拟内存分配出来。
cpu 为什么要使用虚拟地址空间与物理地址空间映射?解决了什么样的问题?
1)方便编译器和操作系统安排程序的地址分布。
程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。通过虚拟地址空间与物理地址空间映射解决不连续的缓冲区的问题。
2)方便进程之间隔离
不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。
3)方便OS使用你那可怜的内存。
程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,
内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
虚拟地址空间的布局如下:
0-3G是用户空间 3-4G是内核空间
用户区 内核区
代码段
已经初始化的全局变量
未被初始化的全局变量
堆 -- 从下往上
共享库
栈 - 从上往下
环境变量
内核区
3. C库函数与系统函数的关系
FD:文件描述符 FP_POS:文件指针 BUFFER:缓冲区
write对0-3G的用户空间进行操作 sys_write()对3-4G的内核空间进行操作
4. IO函数介绍
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 读写
可选项:
O_APPEND 追加
O_CREAT 创建文件
O_EXCL和O_CREATE一起使用,如果文件存在则报错
O_NONBLOCK 非阻塞
Mode 权限位,最终(mode&~umask)
- 返回值:
成功:返回最小的可用文件描述符
失败:返回-1,并且设置errno
- open函数中的errno:
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n")
- return -
- }
- int fd = open(argv[], O_CREAT|O_TRUNC|O_WRONLY, );
- close(fd);
- return ;
- }
- 使用open实现一个touch功能
使用open实现一个touch功能
- #include<stdio.h>
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<strings.h>
- int main(int argc, char *argv[])
- {
- int num = ;
- char filename[] = {};
- while()
- {
- sprintf(filename, "temp_%04d", num++);
- if (open(filename, O_RDONLY|O_CREAT, ) < )
- {
- perror("open err:");
- break;
- }
- }
- printf("num == %d\n", num);
- return ;
- }
- 一个进程打开的最大文件数()
一个进程打开的最大文件数(1024)
2)close
- 作用:关闭文件描述符
- 头文件:
- #include <unistd.h>
- 函数原型:
- int close(int fd);
- 参数说明:
fd文件描述符
- 返回值:
成功:返回0
失败:返回-1,并且设置errno
3)read读
- 头文件
- #include <unistd.h>
- 函数原型
- ssize_t read(int fd, void *buf, size_t count);
- 参数说明
fd 文件描述符
buf缓冲区
count缓冲区大小
- 返回值
失败:返回-1,设置errno
成功:返回读到的字节数
0代表读到文件末尾
非阻塞的情况下read返回-1,但是此时需要判断error的值。
4)write写
- 头文件
- #include <unistd.h>
- 函数原型
- ssize_t write(int fd, const void *buf, size_t count);
- 参数说明:
fd文件描述符
buf缓冲区
count缓冲区大小
- 返回值
失败:返回-1,设置errno
成功:返回写入的字节数
0代表未写入
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n")
- return -
- }
- int fd = open(argv[], O_RDONLY);
- char buf[] = {};
- int ret = ;
- while ((ret = read(fd, buf, ziseof(buf))) != )
- {
- if (ret == -)
- {
- perror("read err:");
- return -;
- }
- else
- {
- write(STDOUT_FILENO, buf, ret);
- }
- }
- close(fd);
- return ;
- }
- 实现一个cat功能
实现一个cat功能
需求:给一个文件中写入内容,写完之后打开该文件再读取写入的内容?
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n");
- return -;
- }
- int fd = open(argv[], O_RDWR|O_CREAT, );
- char data[] = "hello world";
- write(fd, data, sizeof(data));
- char buf[] = {};
- int ret = ;
- while ((ret = read(fd, buf, sizeof(buf))) != )
- {
- if (ret == -)
- {
- perror("read err:");
- return -;
- }
- else
- {
- write(STDOUT_FILENO, buf, ret); //STDIN_FILENO, STDERR_FILENO
- }
- }
- close(fd);
- return ;
- }
- bug版本
bug版本
结果:内容写入到文件中,但是并未按预期输出到屏幕上。
原因:是由于write完成之后,fd到了文件末尾,因此read时到了文件末尾,无法读取文件数据
解决方法:写完之后将文件指针设置到文件开头,使用请看下文要介绍的lseek函数。
5)lseek写
- 头文件
- #include <sys/types.h>
- #include <unistd.h>
- 函数原型
- off_t lseek(int fd, off_t offset, int whence);
- 参数说明
fd文件描述符
offset偏移量
whence:
SEEK_SET 文件开始位置
SEEK_CUR 文件当前位置
SEEK_END 文件结尾
- 返回值
失败:返回-1,设置errno
成功:返回当前位置到文件开头的长度
- lseek作用
移动文件读写位置
计算文件大小
拓展文件
示例:
a. 移动文件读写位置
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n");
- return -;
- }
- int fd = open(argv[], O_RDWR|O_CREAT, );
- char data[] = "hello world";
- write(fd, data, sizeof(data));
- //文件读写位置此时在末尾
- //需要移动读写位置
- lseek(fd, , SEEK_SET); //将fd移动到文件头
- char buf[] = {};
- int ret = ;
- while ((ret = read(fd, buf, sizeof(buf))) != )
- {
- if (ret == -)
- {
- perror("read err:");
- return -;
- }
- else
- {
- write(STDOUT_FILENO, buf, ret); //STDIN_FILENO, STDERR_FILENO
- }
- }
- close(fd);
- return ;
- }
- 修改上例的bug(写入文件内容并读取文件内容打印到屏幕)
修改上例的bug(写入文件内容并读取文件内容打印到屏幕)
b. 计算文件大小
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n");
- return -;
- }
- int fd = open(argv[], O_RDONLY);
- int ret = lseek(fd, , SEEK_END); //将fd移动到文件头
- printf("file size is %d\n", ret); //注意实际读到的文件大小为ret-1
- close(fd);
- return ;
- }
- 计算文件大小(输出文件字节数)
计算文件大小(输出文件字节数)
c. 拓展文件
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- if (argc != )
- {
- printf("./a.out filename\n");
- return -;
- }
- int fd = open(argv[], O_WRONLY|O_CREAT, );
- //拓展文件
- int ret = lseek(fd, , SEEK_END); //将fd移动到文件头
- //需要至少写一次,否则不能保存
- write(fd, "a", );
- printf("file size is %d\n", ret);
- close(fd);
- return ;
- }
- 拓展文件
拓展文件
阻塞的概念:
read函数在读设备或者读管道,或者读网络的时候。
输入输出设备对应的/dev/tty。
6)fcntl
- 头文件
- #include <unistd.h>
- #include <fcntl.h>
- 函数原型
- int fcntl(int fd, int cmd, ... /* arg */ );
- 参数说明:
fd文件描述符
cmd 命令
- 返回值
不同的cmd返回值不同
- #include<stdio.h>
- #include<fcntl.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<unistd.h>
- int main(int argc, char *argv[])
- {
- //O_NONBLOCK设置为非阻塞
- int fd = open("/dev/tty", O_RDWR);
- //fcntl()函数,设置非阻塞
- int flags = fcntl(fd, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(fd, F_SETFL, flags);
- char buf[] = {};
- int ret = ;
- while()
- {
- //如果没有设置O_NONBLOCK
- ret = read(fd, buf, sizeof(buf));
- if (ret < )
- {
- perror("read err:");
- printf("ret is %d\n", ret);
- }
- if (ret)
- {
- printf("buf is %s\n", buf);
- }
- printf("haha\n");
- sleep();
- }
- close(fd);
- return ;
- }
- 使用fcntl函数实现读非阻塞
使用fcntl函数实现读非阻塞
yuchuan_Linux_C 编程之七系统IO函数的更多相关文章
- 健壮的网络编程IO函数-RIO包
RIO包 简介 Rio包即为Robust io函数包.包中函数是对Linux基本I/O函数的封装,使其更加健壮.高效,更适用于网络编程. 分析 Rio包由rio_t结构体和系列函数组成. 首先是两个不 ...
- UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流
1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...
- UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作
标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题. 1 ...
- linux c编程调用系统的动态库时,要使用dlopen等函数吗?
同问 linux c编程调用系统的动态库时,要使用dlopen等函数吗? 2012-11-27 21:55 提问者: hnwlxyzhl 我来帮他解答 满意回答 2012-12-07 09:08 li ...
- Linux系统编程001--系统IO
1. 文件系统:用来存储.组织.管理文件的一套方式.协议 2. 文件 文件的属性:i-node唯一表示一个文件的存在与否 文件的内容 3. Linux系统如何实现文件的操作? 点击查看代码 硬件层: ...
- UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数
UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数 gethostname() getppername() getsockname() gethostbyname() ...
- Linux IO函数的使用和区别
Linux系统中的IO函数主要有read.write.recv.send.recvmsg.sendmsg.readv.writev,本篇主要介绍他们的使用以及区别. read函数: #include ...
- 标准C IO函数和 内核IO函数 效率(时间)比较
前言 标准C提供的文件相关的IO函数,除标准错误输出是不带缓冲的(可以尽快的将错误消息显示出来)之外,所有与终端相关的都是行缓冲,其余都是全缓冲的. 我们可以使用setbuf,setvbuf改变指定流 ...
- Java nio 笔记:系统IO、缓冲区、流IO、socket通道
一.Java IO 和 系统 IO 不匹配 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM 自身在 I/O 方面 ...
随机推荐
- open 管道用法|Getopt::Long
#!/usr/bin/perl use strict; use warnings; use Getopt::Long; my ($number,$in,$out); GetOptions( " ...
- Linux的iptables菜鸟初学
什么是iptables? iptables是linux下的命令行工具,操控的是linux的防火墙,这个防火墙叫netfilter.通俗的说应该是用户通过iptables把安全设定设置给netfilte ...
- Socket设置超时时间
主要有以下两种方式,我们来看一下方式1: Socket s=new Socket(); s.connect(new InetSocketAddress(host,port),10000); 方式2: ...
- 动态添加checkbox
<!--动态添加 checkbox--> <script type="text/javascript"> var data = new Array(); & ...
- rxjava2 dependency
<dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxjava</ ...
- 吴裕雄--天生自然python学习笔记:Python MongoDB
MongoDB 是目前最流行的 NoSQL 数据库之一,使用的数据类型 BSON(类似 JSON). PyMongo Python 要连接 MongoDB 需要 MongoDB 驱动,这里我们使用 P ...
- highcharts 柱状图在柱子顶部显示y轴数据
var plotOptions={ column:{ //borderColor: "#CCCC66",//边框 shadow: true, //阴影 dataLabels:{ / ...
- Java判断字符串是否为空
我们常常在实际开发调用一些类库,或者本身框架里面有类库去实现判断字符串空的操作,但是某些场景下上不是很方便去引入外部库的,所以需要我们自己去做这个工作,事实上判断空的操作不是很复杂,所以做个记录. 空 ...
- CSA|EI
信息检索 CSA是学科特色的包含相关学科的内容,其网址是https://search.proquest.com/ 可以使用命令行检索: 分类的限制检索: 寻找检索线索可使用百科全书 EI是工程领域最全 ...
- python3的数据类型转换问题
问题描述:在自我学习的过程中,写了个登陆,在input处,希望能够对数据类型进行判断,但是因为python3的输入的数据会被系统默认为字符串,也就是1,1.2,a.都会被系统默认为字符串,这个心塞啊, ...