一、整体大纲

二、 系统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函数的更多相关文章

  1. 健壮的网络编程IO函数-RIO包

    RIO包 简介 Rio包即为Robust io函数包.包中函数是对Linux基本I/O函数的封装,使其更加健壮.高效,更适用于网络编程. 分析 Rio包由rio_t结构体和系列函数组成. 首先是两个不 ...

  2. UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

    1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...

  3. UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作

    标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题.   1 ...

  4. linux c编程调用系统的动态库时,要使用dlopen等函数吗?

    同问 linux c编程调用系统的动态库时,要使用dlopen等函数吗? 2012-11-27 21:55 提问者: hnwlxyzhl 我来帮他解答 满意回答 2012-12-07 09:08 li ...

  5. Linux系统编程001--系统IO

    1. 文件系统:用来存储.组织.管理文件的一套方式.协议 2. 文件 文件的属性:i-node唯一表示一个文件的存在与否 文件的内容 3. Linux系统如何实现文件的操作? 点击查看代码 硬件层: ...

  6. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

  7. Linux IO函数的使用和区别

    Linux系统中的IO函数主要有read.write.recv.send.recvmsg.sendmsg.readv.writev,本篇主要介绍他们的使用以及区别. read函数: #include ...

  8. 标准C IO函数和 内核IO函数 效率(时间)比较

    前言 标准C提供的文件相关的IO函数,除标准错误输出是不带缓冲的(可以尽快的将错误消息显示出来)之外,所有与终端相关的都是行缓冲,其余都是全缓冲的. 我们可以使用setbuf,setvbuf改变指定流 ...

  9. Java nio 笔记:系统IO、缓冲区、流IO、socket通道

    一.Java IO 和 系统 IO 不匹配 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM 自身在 I/O 方面 ...

随机推荐

  1. 允许外部访问Windows本机的指定端口

    背景:目前公司有一台公网Windows服务器,有公网IP和内网IP,防火墙已开启 需求:9999端口需要对外开放 方案:在防火墙的入站规则里添加一条规则,使外部能够访问9999端口 问题:添加好规则后 ...

  2. JDK和Spring中的设计模式

    创建型 1)工厂方法 Collection.iterator() 由具体的聚集类来确定使用哪一个Iterator 2)单例模式 Runtime.getRuntime() 3)建造者模式 StringB ...

  3. OpenCVSharp介绍

    OpenCvSharp 是一个OpenCV的.Net wrapper,应用最新的OpenCV库开发,使用习惯比EmguCV更接近原始的OpenCV,有详细的使用样例供参考.该库采用LGPL发行,对商业 ...

  4. 蓝桥杯-PREV45-图形排版

    这是2017年蓝桥杯C组C++的压轴题,拿到之后没什么想法.但是蓝桥杯有部分分.所以直接敲了个大暴力提交上去过了一半的数据.后来想到了DP,但是没能实现出来,感觉还是有问题的.后来看了解题视频发现是预 ...

  5. OpenCV Laplace 算子

    #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #i ...

  6. 2015-09-14-初级string

    标准库string类型 string对象初始化 string s1; string s2(s1); string s3("value"); string s4(n,'c'); st ...

  7. 用ES6和fetch封装网络请求

    导读: fetch: 这个方法是ES2017中新增的特性,这个特性出来后给人一种传统ajax已死的感觉,其实它的作用是替代浏览器原生的XMLHttpRequest异步请求,我们在日常的开发中,基本不会 ...

  8. log4j不输出日志错误分析

    1.rootLogger不输出 代码如下: 配置文件代码: log4j.rootLogger=info, R,userLog log4j.appender.R=org.apache.log4j.Rol ...

  9. Waymo

    技术优势 Waymo在自己的激光雷达系统上投入了大量资金,它认为这项技术对自动驾驶汽车的长期成功至关重要.实际上,该公司声称它已经将专有激光雷达传感器的成本降低了90%,这种传感器以前的制造成本为7. ...

  10. [转]<版本一>写代码的小女孩

    天冷极了,下着雪,又快黑了.这是NOIP的前夜.在这又冷又黑的晚上,一个衣衫破烂的小女孩在机房敲着代码.她从班里逃出来的时候还拿着一本算导,但是有什么用呢?那是一本很破旧的书——那么大,一向是她妈妈垫 ...