1、什么是文件描述符?
1、非负的整数。
2、表示一个打开的文件。
3、由系统调用(open)返回,被内核空间(后续系统调用)引用。
4、内核缺省为每个进程打开三个文件描述符:
- stdin 0 - 标准输入
- stdout 1 - 标准输出
- stderr 2 - 标准出错
这三个描述符也可以被修改 后面会讲
文件描述符的范围介于0到OPEN_MAX之间,传统Unix中OPEN_MAX宏被定义为63,现代Linux使用更大的上限。
2、文件描述符的打开关闭
#include <fcntl.h>
int open (//创建文件与读取文件 两项功能
const char* pathname, // 路径
int flags, // 模式
mode_t mode // 权限(仅创建文件有效)
);
int open (
const char* pathname, // 路径
int flags // 模式
}; // 常用于读写文件
/*int creat (//创建文件 一项功能 一般用不到
const char* pathname, // 路径
mode_t mode // 权限
); // 常用于创建文件*/
以上成功返回尽可能小的文件描述符,失败返回-1。
#include <unistd.h>
int close (
int fd // 文件描述符
);//关闭文件描述符
成功返回0,失败返回-1。
flags为以下值的位或:
O_RDONLY - 只读。\
O_WRONLY - 只写。 > 只选一个
O_RDWR - 读写。 |
O_APPEND - 追加。/
//当使用O_CREATE时 mode位参数才能被填写 如:0666
O_CREAT - 创建,不存在即创建(已存在即直接打开,
并保留原内容,除非...),
有此位mode参数才有效。
//这俩是搭配O_CREAT的
O_EXCL - 排斥,已存在即失败。\
> 只选一个,
O_TRUNC - 清空,已存在即清空 / 配合O_CREAT使用
(有O_WRONLY/O_RDWR)。
这里就不得不说一下mod位的八进制权限码
- mod位的权限由八进制表示
- 系统可以用权限掩码屏蔽进程设置的部分权限
0666 (rw-rw-rw-) & ~0022 = 0644 (rw-r--r--)(因为是八进制前面0不能省略 表示八进制)
- 6=110 4=100 2=010 以此类推每一个八进制数对应三位二进制码
- mod所设置的一共是三种用户的权限
1-3位是属主权限
4-6位是和属主同组人的权限
7-9位是其他用户权限
其实真实的权限前面还有一位代表文件格式分别是
d 文件夹 | 链接 b 块设备文件 p管道文件
- 普通文件 c字符设备文件 s套接口文件 - r 读 w写 x执行
3、文件读写
write
#include <unistd.h>
ssize_t write (
int fd, // 文件描述符
const void* buf, // 缓冲区
size_t count // 期望写入的字节数 无符号
);
成功返回实际写入的字节数,失败返回-1。
read
#include <unistd.h>
ssize_t read (
int fd, // 文件描述符
void* buf, // 缓冲区
size_t count // 期望读取的字节数
);
成功返回实际读取的字节数,失败返回-1
实现一个copy
#include <stdio.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int copy(const char *srcFileName,const char *destFileName){
int sfd = open(srcFileName,O_RDONLY);
if(sfd == -1){
printf("%s\n",strerror(errno));
return -1;
}
int dfd = open(destFileName,O_WRONLY|O_CREAT|O_EXCL,0644);
if(dfd == -1){//access stat fstat
if(errno == EEXIST){//该文件存在,是否要覆盖
printf("%s 文件存在!\n",destFileName);
}else{
printf("%s\n",strerror(errno));
}
close(sfd);
return -1;
}
char str[128] = {};
ssize_t ret;
while((ret = read(sfd,str,128))>0){
write(dfd,str,ret);
}
close(sfd);
close(dfd);
return 0;
}
//cp
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s srcfile destfile\n",argv[0]);
return -1;
}
copy(argv[1],argv[2]);
return 0;
}
4、标准库IO
1、当系统调用函数被执行时,需要切换用户态和内核态,频繁调用会导致性能损失。
2、标准库做了必要的优化,内部维护一个缓冲区,只在满足特定条件时才将缓冲区与系统内核同步,借此降低执行系统调用的频率,减少进程在用户态和内核态之间来回切换的次数,提高运行性能。 (fread 和 read 的区别在于 read会调用指定大小的内存内容 但fread会调用比指定内容更多的内容 从而降低了切换时间)
#include <stdio.h>
int main ()
{
FILE* fp = fopen ("stdio.dat", "wb");
if (! fp)
{
perror ("fopen");
return -1;
}
unsigned int i;
for (i = 0; i < 100000; i++)
fwrite (&i, sizeof (i), 1, fp);
fclose (fp);
return 0;
}
5、lseek
改变文件读取位置
#include <stdio.h> #include <string.h>
#include <unistd.h> #include <sys/fcntl.h>
//SEEK_SET 开头
//SEEK_CUR 当前
//SEEK_END 结尾
int main(){
int fd1 = open("a.txt",O_WRONLY|O_APPEND);
int fd2 = open("a.txt",O_WRONLY|O_APPEND);
int fd3 = dup(fd1);
lseek(fd1,0,SEEK_END); //末尾
printf("%ld\n",lseek(fd1,0,SEEK_CUR));//返回位置
printf("%ld\n",lseek(fd2,0,SEEK_CUR));
printf("%ld\n",lseek(fd3,0,SEEK_CUR));
char str[100] = "Hello wrold";
write(fd1,str,strlen(str));//在末尾写入
lseek(fd1,0,SEEK_END);
lseek(fd2,0,SEEK_END);
lseek(fd3,0,SEEK_END);
printf("%ld\n",lseek(fd1,0,SEEK_CUR));
printf("%ld\n",lseek(fd2,0,SEEK_CUR));
printf("%ld\n",lseek(fd3,0,SEEK_CUR));
return 0;
}