文件对象
不带缓冲的文件IO
open(path,flags,mode_t)
- open(path,flags)文件存在就不用设定权限
- 返回文件描述符int
-
flags [O_RDONLY O_WRONLY O_RDWR O_CREAT O_TRUNC O_APPEND]
man 2 open
int fd = open(path,"O_RDWR|O_CREAT|O_TRUNC");
read(fd,buf,count)
-
返回值读的字节数sszie_t >0 =0 -1
-
count应当是申请内存的大小
-
read前清空buf
ssize_t ret = read(fd,buf,sizeof(buf));
write(fd,buf,count)
- 如果是文本文件字节数count就用strlen(),二进制就用sizeof()
- 怎么读就怎么写,读多少写多少
write(fd,buf,ret);//ret代表读多少写多少字节
实现拷贝
./copyFile.c file1 file2
int main(){
ARGS_CHECK(argc,3);
int fdr = open(argv[1],"O_RDONLY");
ERROR_CHECK(fd,NULL,"open");
int fdw = open(argv[2],"O_WRONLY|O_CREAT|O_TRUNC",0666);
ERROR_CHECK(fd,NULL,"open");
char buf[4096] = {0};//这里用char是因为它是一字节
while(1){
memset(buf,0,sizeof(buf));
ssize_t ret = read(fdr,buf,sizeof(buf)
if(ret == 0){
break;
}
write(fdw,buf,ret);
}
close(fdw);
close(fdr);
return 0;
}
性能问题
使用文件流fopen fread fwrite 先把文件拷贝到用户态文件缓冲,然后一起拷贝到内核态
使用不带缓冲的文件IO open read write 尽量让buf大一点可以减少用户态到内核态切换次数
ftruncate(fd,length)
文件截断
-
大到小截断后面,小到大后面补0
-
会产生文件空洞(抢地盘)
int ret = ftruncate(fd,40960);
内存映射mmap
在用户态内存分配空间,该空间直接和外设建立映射,使用[]/*读写内存(读写文件)
mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)
- 返回值void*
- 文件大小是固定的 ftruncate()
- 只能是磁盘文件
- 建立映射之前先open()
- munmap释放映射
char* p = (char*)mmap(NULL,5,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)
ERROR_CHECK(p,MAP_FAILED,"mmap");//注意坑
for(int i = 0;i<5;i++){printf("%c",p[i]);}
munmap(p,5)
lseek(fd,0,SEEK_SET)
ptr偏移文件开头
文件流底层使用了文件对象
fileno(fp);//返回文件流文件描述符
write(1,"hello",5);
write(STDOUT_FILENO,"hello",5);
重定向
close(STDOUT_FILENO);
int fd = open(argv[1],O_RDWR);
printf("fd=%d\n",fd);
文件描述符的复制
dup(oldfd)
- newfd和oldfd共享文件对象的ptr
int newfd = dup(oldfd);
dup2(oldfd,newfd)
-
如果newfd是存在文件,就自动关闭newfd,同时指向oldfd
-
oldfd和newfd指向同一个文件对象
int savefd = 5; dup2(STDOUT_FILENO,savefd); dup2(oldfd,STDOUT_FILENO); dup2(savefd,STDOUT_FILENO);
有名管道
name pipe /FIFO
- 管道大小是0,不能存放数据,用来通信
- 半双工通信 全双工通信(2管道)
- 管道在open时阻塞 O_WRONLY O_RDONLY
mkfifo 创建管道
mkfifo 1.pipe
系统调用操作管道
./read 1.pipe
int main(){
int fdr = open(argv[1],"O_RDONLY");
ERROR_CHECK(fdr,-1,"open");
char buf[128]={0};
read(fdr,buf,sizeof(buf));
return 0;
}
./write 1.pipe
int main(){
int fdw = open(argv[1],"O_WRONLY");
ERROR_CHECK(fdw,-1,"open");
char buf[128]="hello world";
write(fdw,buf,strlen(buf));
return 0;
}
死锁问题
解决:
###
IO多路复用
思路:
select(nfds, *readfds, *writefds, *exceptfds, *timeout)
- FD_ZERO(fd_set)
- FD_SET(fd,fd_set)
- FD_ISSET(fd,fd_set)
- FD_CLR(fd,fd_set)
- 参数nfds 取所有文件描述符最大的+1
一方关闭了聊天
select超时
-
timeout 也是传入传出,每次循环开始要设置timeout
-
tv_sec秒 tv_usec微妙
管道
同时监听读和写
ulimit -a
select实现原理
- fd_set 监听集合 底层是0~1023位图,
- FD_ISSET() 去就绪集合找是否就绪
劣势:
- 监听集合和就绪集合在一起
- 文件数量固定
- 大量用户态到内核态的拷贝
- 找就绪浪费时间(轮询)少量就绪,大量询问