aka 系统调用 io
文件描述符(fd) 是 在文件IO中贯穿始终的类型
- 文件描述符的概念
- 文件Io操作: open,close,read.write.lseek
- 标准io依赖于文件io。读取相关的函数 ,文件指针相关的函数
- 文件io与标准io进行区别
[!两者转换]
int fileno(FILE *stream)FILE *fdopen(int fd,const char *mode);
- IO的效率问题
- 习题:将mycpy.c 程序进行更改,将BUFSIZE的值放大,并观察进程所消耗的时间,注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出问题 读1024写1024 mycopy.c文件 bufsize 从128翻上去到16g 性能最佳拐点
time ./mycpy /etc/services tmp/outreal/user/sys
- 习题:将mycpy.c 程序进行更改,将BUFSIZE的值放大,并观察进程所消耗的时间,注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出问题 读1024写1024 mycopy.c文件 bufsize 从128翻上去到16g 性能最佳拐点
- 文件共享:多个任务共同操作一个文件或者协同完成任务
- 面试: 写程序 删除一个文件的第10行,数组覆盖,
- 补充函数:
{c} int truncate(const char *path,off_t length);int ftruncate(int fd,off_t length)把一个未打开的文件截断到多长 或者 已打开的文件
- 原子操作: 不可分割的操作
- 原子:不可分割的最小单位
- 作用:解决竞争和冲突 应用:多进程与多线程并发
tmpnam给文件名 操作不原子,名字 创建 两步 ;tmpfile 直接创建一个文件。创建临时文件 多种方法。
- 程序中的重定向:dup,dup2
dup.c
- 同步:与设备相关。 系统开发,中间层 sync,fsync,fdatasync
- fcntl();管家级函数。管理文件描述符
- 文件描述符所变的魔术几乎都来源于该函数。功能杂。
- ioctl();同上;另外一个管家设备相关的内容。
- 一切皆文件的设计原理好不好?简化操作 5个;但是有些设备不仅仅是读写。声卡的放音与录音。读写。篇幅短。没人说自己是Ioctl专家……。基于1.3.27内核 ioctl-list古董文物。光环下的垃圾堆。
- /dev/fd/目录:虚目录,显示的是当前进程的文件描述符信息。ls -l /dev/fd/ 谁看就是谁,是ls的文件描述符 照镜子 link链接文件
1.
人身份证 文件
- 打开一个文件 会得到一个结构体
- 实质是一个整形数 数组下标
- 0 1 2 关联的是stdin stdout stderr会提前打开三个设备
- 一说 stream 三个 stdin stdout stderr
- 一说 fd 0 1 2


- 1024数组大小 ulimit 可以更改大小
- 左右不同的实现 FILE 类型指针 操作 结构体中有fd
- 文件描述符优先使用当前可用范围最小的
- 该数组存在于一个进程空间。非共用。不同进程 拿到同一文件 不同结构体
- 一个结构体 被多个数组下标所指
2. 文件io操作相关函数:
-
首先 获得文件描述符:
man 2 open -
{c} int open(const char *pathname,int flags);;第二个参数 位图- 返回值 文件描述符如果成功; 失败返回-1 errno:文件描述符→数组下标→不可能为负数
- flags: 选项 O_RDONLY,O_WRONLY,O_RDWR;
- O_DIRECT: cache 读的缓冲区/加速机制;buffer 写的缓冲区
- O_NOATIME:偷摸读别人的文件 但不改文件修改时间
- O_NONBLOCK: 阻塞 打印机 读 读 等 等 。。。
-
标准io过渡到系统调用io 按位或
-
//只能对已有文件操作 r-> O_RONLY r+->O_RDWR w-> O_WRONLY|O_CREAT|O_TRUNC //读与写 有则清空 无则创建 w+->O_RDWR|O_TRUNC|O_CREAT- open 函数 有 两个函数原型。重载?非 printf如何实现?变参函数
-
int open(const char *pathname,int flags); int open(const char *pathname,int flags,mode_t mode);
-
重载是定参。
{c} printf("%d%d",a,b); -
CFLAGS=-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -
man 2 read:读一个文件描述符 -
{c} ssize_t read(int fd, void *buf,size_t count);,buf 读到的位置 -
man 2 write
-
{c} ssize_t write(int fd, const void *buf,size_t count);返回值为0说明没有东西写进去 -
lseek:
{c} off_t lseek(int fd, off_t offset,int whence);offset:偏移量,相对偏移位置。将fseek和ftell进行综合。
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define BUFSIZE 1024
//源文件和目标文件由终端来决定 arg
int main(int argc,char **argv){
int sfd,dfd; //source file describe
char buf[BUFSIZE];
int len,ret,pos;
//判断命令行传参是否合法
if(argc<3){
//报错结束
fprintf(stderr,"Usage...\n");
exit(1);
}
//两次open 打开源和目标文件
std=open(argv[1],O_RDONLY); //打开的文件 打开的方式 返回值 文件描述符
//如果打开失败 结束当前进程
if(sfd<0){
perror("open()");
exit(1);
}
//不存在:创建 存在 :清空 给一个权限
dfd=open(arg[2],O_WRONLY|O_CREAT,O_TRUNC,0600);
if(dfd<0){
//防止产生内存泄漏
close(sfd);
perror("open()");
exit(1);
}
//中间读一块 写一块 该过程放在循环当中
while(1){
len=read(sfd,buf,BUFSIZE);//read的返回值读到的真正的字节数
if(len<0){
perror("read()");
break;//为了执行关闭函数
}
if(len == 0)
break;
pos=0
while(len>0){
//要写10个字节 却只写了3个
ret = write (dfd,buf+pos,len);
if(ret <0){
perror("write()");
exit(1);//小的内存泄漏
}
pos +=ret;
len -=ret;
}
}
close(dfd);
close(sfd);
exit(0);
}
- make mycpy
- ./mycpy /etc/services /tmp/out
- diff 上面两个文件
3.
FILE *fp;
fputc(fp) -> pos++
fputc(fp) -> pos++
//没有写到磁盘上,缓冲区 word写好后 点 是否保存 保存 将缓冲区刷新
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
putchar('a');//std
write(1,"b",1);//sys
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
exit(0);
}- 输出会是?
- bbbaaa
- 命令
strace ./ab跟踪 getchar放到输出缓冲区。三句putchar相当于一句write来实现
4
while(){
lseek 11 +read +lseek 10 +write}
定位读定位写
需要4个系统调用
/**********************************/
//同一个文件在一个进程中打开两次 简化while循环中的系统调用
1-> open r -> fd1 -> lseek 11只读打开
2-> open r+ -> fd2 -> lseek 10读写打开 r+确定文件一定存在
while()
{
1->fd1->read
2->fd2->write
}
/*************************************/
process1->open->r
process2->open->r+
p1->read->p2->write
4.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out"
//hello world并不简单 支持多国语言输出
int main(){
int fd;
//文件描述符优先使用当前描述符中最小的
close(1);
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
//如果打开失败了,报错结束
if(fd<0){
perror("open()");
exit(1);
}
/*************************/
puts("hello!!");
exit(0);
}
没有简单的程序,只有头脑简单的程序员……
-
如何删除程序呢? 把不需要的mv到一个文件夹 再定期清理它 这不就跟回收站一样么
-
./dup; `cat /tmp/out -
dup 文件描述符复制一份指向同一份空间 结构体
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out"
//hello world并不简单 支持多国语言输出
int main(){
int fd;
//文件描述符优先使用当前描述符中最小的
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
//如果打开失败了,报错结束
if(fd<0){
perror("open()");
exit(1);
}
// close(1);
// dup(fd);
dup2(fd,1);
if(fd != 1){
close(fd);
}
//相当于输出重定向
/*************************/
puts("hello!!");
exit(0);
} - 有些程序天生没有占用1号描述符。情况一:fd本身是1;不可贸然关闭fd,如果本身不是1号那么可以关掉
- 根本原因:多进程非原子操作close和dup;原子操作:dup2 关掉一号,把fd副本放到1号
- 微观 宏观编程思想 只有main函数?搭框架 封装好的接口 码驴……蒙着眼睛
- 看成小模块 不改变 打印后 还原 。内存泄漏 ;越界;看成模块;安全性
5.
- sync(2): 牵涉到设备 同步内核层面的buffer和cache ;关机的时候;解除设备挂载时 正在 buffer中还未同步时刷新
- fsync:刷文件 fdatasync 只刷数据 不刷亚数据:文件最后的修改时间、属性
6
int fcntl(int fd,int cmd, .../*arg*/):命令不同,传的参数不同;命令不同,函数返回值不同;
- dup 和dup2 是由该函数变相进行封装的
Footnotes
-
下午os课程也有讲到欸 吞吐量和延迟!梦幻联动! ↩