Linux06文件对象&io多路复用

Posted by 川川的博客 on May 10, 2025

文件对象

不带缓冲的文件IO

image-20250616172305374

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));
    

image-20250616194429820

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大一点可以减少用户态到内核态切换次数

image-20250616201145673

ftruncate(fd,length)

文件截断

  • 大到小截断后面,小到大后面补0

  • 会产生文件空洞(抢地盘)

int ret = ftruncate(fd,40960);

内存映射mmap

在用户态内存分配空间,该空间直接和外设建立映射,使用[]/*读写内存(读写文件)

image-20250616203425113

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偏移文件开头

image-20250616205332193

文件流底层使用了文件对象

fileno(fp);//返回文件流文件描述符

image-20250616210906130

write(1,"hello",5);
write(STDOUT_FILENO,"hello",5);

重定向

close(STDOUT_FILENO);
int fd = open(argv[1],O_RDWR);
printf("fd=%d\n",fd);

image-20250617102036442

文件描述符的复制

dup(oldfd)

  • newfd和oldfd共享文件对象的ptr
int newfd = dup(oldfd);

image-20250617103607037

dup2(oldfd,newfd)

  • 如果newfd是存在文件,就自动关闭newfd,同时指向oldfd

  • oldfd和newfd指向同一个文件对象

    int savefd = 5;
    dup2(STDOUT_FILENO,savefd);
    dup2(oldfd,STDOUT_FILENO);
    dup2(savefd,STDOUT_FILENO);
    

image-20250617104202174

有名管道

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;
}


死锁问题

image-20250617110640276

解决:

image-20250617110726548

###

IO多路复用

image-20250617111848698

思路:image-20250617112257842

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

image-20250617144435641

image-20250617145024047

一方关闭了聊天

image-20250617143950139

select超时

  • timeout 也是传入传出,每次循环开始要设置timeout

  • tv_sec秒 tv_usec微妙

image-20250617162443814

管道

image-20250617164623501

同时监听读和写

ulimit -a

image-20250617164950254

select实现原理

  • fd_set 监听集合 底层是0~1023位图,
  • FD_ISSET() 去就绪集合找是否就绪

劣势:

  • 监听集合和就绪集合在一起
  • 文件数量固定
  • 大量用户态到内核态的拷贝
  • 找就绪浪费时间(轮询)少量就绪,大量询问

image-20250617171602292