当前位置: 代码迷 >> 综合 >> 文件/目录管理(2)---dup()/dup2()/lseek()/fcntl()
  详细解决方案

文件/目录管理(2)---dup()/dup2()/lseek()/fcntl()

热度:7   发布时间:2023-12-15 11:07:40.0

1 dup()/dup2()

dup()和dup2()都可以复制文件描述符,区别是:
  dup()返回的是系统帮忙查找的未使用的最小值
  dup2()返回的是第二个参数,如果该值已经被使用,会先关闭然后再使用。

dup() dup2() 复制文件描述符,但不复制对应的文件表。
这里写图片描述

  使用文件描述符时,内存中对应一个文件表,在文件表中,会记录关于内存中文件表的信息和硬盘上的文件的信息,其中,i节点是文件在硬盘上的地址。

dup.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR|O_TRUNC|O_CREAT,0666);if(fd==-1) perror("open"),exit(-1);int fd2 = dup(fd);printf("fd=%d,fd2=%d\n",fd,fd2);write(fd,"1",1);write(fd2,"2",1);int fd3 = open("b.txt",O_RDWR|O_CREAT,0666);int fd4 = dup2(fd,fd3);printf("fd3=%d,fd4=%d\n",fd3,fd4);write(fd4,"Hehe",4);close(fd); close(fd3);close(fd4);
}

2 lseek()函数

lseek() 是fseek()的底层实现,是系统调用。
第一个参数是fd
第二个参数是偏移量
第三个参数是偏移的起始位置,有3种选择:
  SEEK_SET - 从文件头开始
  SEEK_CUR - 从当前位置开始
  SEEK_END - 从文件尾开始
开发中,更多使用 SEEK_SET。

lseek.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR);char ch;read(fd,&ch,1); printf("ch=%c\n",ch);//aread(fd,&ch,1); printf("ch=%c\n",ch);//blseek(fd,3,SEEK_CUR);read(fd,&ch,1); printf("ch=%c\n",ch);//flseek(fd,0,SEEK_SET);write(fd,"1",1);//a->1lseek(fd,3,SEEK_SET);write(fd,"2",1);//d->2 lseek(fd,2,SEEK_CUR);write(fd,"3",1);//g->3lseek(fd,-2,SEEK_CUR);write(fd,"4",1);//f->4/*文件最后用Vi编辑,自动加有"\n",还有文件结束符EOF*/lseek(fd,-3,SEEK_END);write(fd,"5",1);//s->5close(fd);
}

  在向文件中写时,有缓冲区,并不是我们写了,马上磁盘上就有相应的数据。有相应的函数来设置同步。

writefile.c#include <stdio.h>int main(){int i;FILE* file = fopen("c.txt","w");for(i=0;i<10;i++){fprintf(file,"%d",i);//fflush(file);}sleep(20);fclose(file);printf("over\n");
}

3 fcntl()函数

fcntl(int fd,int cmd,…)
  1 可以复制文件描述符 (cmd取F_DUPFD)
  2 可以获取/设置文件的状态(cmd取F_GETFL/SETFL)
  3 实现文件锁(cmd取F_SETLK/F_GETLK/F_SETLKW)

当复制文件描述符时,和dup2类似,不同点:
  a 不会强制关闭已经使用的描述符
  b 返回值是大于等于第三个参数

3.1 获取/设置文件的状态

  获取/设置文件状态时,创建文件的标识不能获取也不能设置(O_CREAT/O_TRUNC/O_EXCL),文件的访问权限标识不能修改。能设置或者修改APPEND标识。

fcntl.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDONLY|O_CREAT|O_APPEND,0666);if(fd==-1) perror("open"),exit(-1);int fd2 = fcntl(fd,F_DUPFD,5);int fd3 = fcntl(fd,F_DUPFD,5);printf("fd2=%d,fd3=%d\n",fd2,fd3);//复制int flags = fcntl(fd,F_GETFL);printf("flags=%d\n",flags);//没有创建标识if(flags & O_APPEND) printf("APPEND\n");//权限标识如何判断?if((flags&3)==O_RDONLY) printf("RDONLY\n");fcntl(fd,F_SETFL,O_RDWR);//设置flags = fcntl(fd,F_GETFL);//再次获取if(flags & O_APPEND) printf("append\n");if((flags&3)==O_RDONLY) printf("rdonly\n");if((flags&3)==O_RDWR) printf("rdwr\n");close(fd);close(fd2); close(fd3);
}

经验:
  位与运算经常用于取某一位二进制的值和取某几位二进制的值。比如:取a的最后8位二进制:a&0xFF
  最大int:0x7FFFFFFF,最小int:0x80000000
  至于为何最小int是0x80000000,见(http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html)
这里写图片描述

3.2 文件锁

文件锁 - 用来锁定对文件的读写操作
现在主流的锁都是读写锁,由两种锁组成:
  1 读锁 是共享锁,锁定其他进程的写操作,允许其他进程读。
  2 写锁 是独占锁,锁定其他进程的操作(无论读写)

文件锁对应一个结构,内容:
struct flock{
  short l_type; //锁的类型
  short l_whence;//锁的开始位置的参照点
  int l_start;//锁的开始位置的偏移量
  int l_len;//锁定的长度,字节为单位
  pid_t l_pid;//加锁的进程ID
};
l_type包括三种:F_RDLCK 读锁  F_WRLCK 写 锁   F_UNLCK 释放锁
l_whence和l_start 联合决定锁定的开始位置,比如:l_whence = SEEK_SET l_start=10;从文件开始第10个字节上锁。
l_pid只在F_GETLK时有效,其他时候置 -1 即可。

lock1.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR|O_CREAT,0666);if(fd==-1) perror("open"),exit(-1);struct flock lock;lock.l_type = F_WRLCK/*F_RDLCK*/;//读锁lock.l_whence = SEEK_SET;lock.l_start = 10;lock.l_len = 20;lock.l_pid = -1;//一般给-1int res = fcntl(fd,F_SETLK,&lock);if(res == -1) perror("lock");else printf("lock ok\n");sleep(20);//lock2不需要sleepclose(fd);
}
lock2.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR);if(fd==-1) perror("open"),exit(-1);struct flock lock;lock.l_type = F_WRLCK;//写锁lock.l_whence = SEEK_SET;lock.l_start = 10;lock.l_len = 20;lock.l_pid = -1;//一般给-1int res = fcntl(fd,F_SETLK,&lock);if(res == -1) perror("lock");else printf("lock ok\n");close(fd);
}

  编译上面两个程序:gcc lock1.c -oa,gcc lock2.c -ob,先运行a,加上读锁,再运行b,可以加上读锁,但是加上写锁就不行。
  上面的两个程序都是程序结束后,自动释放锁,下面在程序中来实现解锁。

lock3.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR|O_CREAT,0666);if(fd==-1) perror("open"),exit(-1);struct flock lock;lock.l_type = F_RDLCK;//读锁lock.l_whence = SEEK_SET;lock.l_start = 10;lock.l_len = 20;lock.l_pid = -1;//一般给-1int res = fcntl(fd,F_SETLK,&lock);//加锁if(res == -1) perror("lock");else printf("lock ok\n");sleep(20);//lock2不需要sleepprintf("文件读完了\n");lock.l_type = F_UNLCK;res = fcntl(fd,F_SETLK,&lock);//释放锁if(res == -1) perror("release");else printf("release ok\n");sleep(15);//模拟读完文件后作的事情close(fd);
}

  文件锁不能锁定硬盘上的文件,不能锁定read/write函数,只能阻止其他进程的上锁行为。
  文件锁的正确用法:在调用读函数read()之前应该上读锁,在调用写函数之前上写锁。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR);if(fd==-1) perror("open"),exit(-1);struct flock wlock;wlock.l_type = F_WRLCK;wlock.l_whence = SEEK_SET;wlock.l_start = 0;wlock.l_len = 20;wlock.l_pid = -1;int res = fcntl(fd,F_SETLK,&wlock);//先上锁if(res==-1) perror("lock");else//再调写函数write(fd,"hello worldabcdefg",18);close(fd);
}

  使用文件锁时,其他进程默认是加不上锁会返回错误,可以用F_SETLKW实现加不上锁继续等待的效果。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR);if(fd==-1) perror("open"),exit(-1);struct flock wlock;wlock.l_type = F_WRLCK;wlock.l_whence = SEEK_SET;wlock.l_start = 0;wlock.l_len = 20;wlock.l_pid = -1;int res = fcntl(fd,F_SETLKW,&wlock);//先上锁if(res==-1) perror("lock");else//再调写函数write(fd,"hello worldabcdefg",18);close(fd);
}

  fcntl的cmd为F_GETLK时,不是获取锁,而是测试某个锁能否加上,不会真正的加锁。
如果锁可以加,调用F_GETLK的结果:
  struct flock的其他数据不变,锁的类型l_type变为F_UNLCK。
如果锁不可以加,调用F_GETLK的结果:
  struct flock的数据都会发生改变,锁的类型、锁的l_whence、l_start、l_len变成 当前正在起作用的锁的相关信息,l_pid 变成当前正在加锁的进程ID。(不是测试的进程)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(){int fd = open("a.txt",O_RDWR);if(fd==-1) perror("open"),exit(-1);struct flock rlock;rlock.l_type = F_RDLCK;rlock.l_whence = SEEK_SET;rlock.l_start = 0;rlock.l_len = 30; rlock.l_pid = -1;int res = fcntl(fd,F_GETLK,&rlock);//测试锁printf("%d,%d,%d\n",F_RDLCK,F_WRLCK,F_UNLCK);if(res==-1) perror("rlock"),exit(-1);elseprintf("type=%d,start=%d,l_pid=%d\n",rlock.l_type,rlock.l_start,rlock.l_pid);rlock.l_type = F_WRLCK;//写锁res = fcntl(fd,F_GETLK,&rlock);//测试锁if(res==-1) perror("rlock"),exit(-1);//else{printf("type=%d,start=%d,l_pid=%d\n",rlock.l_type,rlock.l_start,rlock.l_pid);}
}