文件的操作
文件的操作中,一共涉及到5个system call:
open(2) read(2) close(2) lseek(2) close(2)
前一天的笔记,整理了open和close,这里着重介绍read,close,lseek
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从指定文件中读取文件
参数:
fd:指定文件描述符,从该文件描述符对应的文件中读取数据
buf:存储读取数据的空间的首地址
count:指定该函数最多读取的字节数
返回值:
error:-1,errno被设置
success:返回读取到的字节数,零代表文件末尾,有可能读取到的字节数小于请求读取的字节数。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:写数据到文件描述符
参数:
fd:指定了文件描述符,向这个文件写入数据
buf:要写入文件的数据存放在该buf空间中
count:要写入文件的最大字节数,用户的请求数据
返回值:
error:-1,errno被设置
success:返回写入的字节数,0代表什么都没有写入,有可能写入的字节数小于用户请求的数量,可能是因为磁盘写满
下面通过一个Demo,来使用上述几个system call。Demo主要实现linux中cat指令的基本功能,代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int agrc,char *agrv[]){
//设置文件的打开方式为只读
int flag = O_RDONLY;
//记录read函数的返回值
ssize_t f_read = 0;
//打开文件
int file = open(agrv[1],flag);
if(file == -1){
perror("open file error");
return -1;
}
//申请一个缓存区,用于存放从文件读取的数据
void *buf = malloc(128);
if(buf == NULL) return -1;
//通过循环读取文件中的内容,如果读取到的字节数等于0,说明已经达到文件末尾,退出循环
do{
f_read = read(file,buf,128);
if(f_read == -1){
perror("read file error");
return -1;
}
write(1,buf,f_read);
}while((f_read = read(file,buf,128)) > 0);
//释放缓存区
free(buf);
//指针置空
buf = NULL;
//关闭文件
close(file);
return 0;
}
lseek函数,用于设置文件的访问位置,介绍如下:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:重新定位文件的读写位置
参数:
fd:指定了文件的描述符
offset:偏移量
whence:
SEEK_SET:将读写位置,定位在offset字节上
SEEK_CUR:当前位置加上offset字节
SEEK_END:文件的大小加上offset字节
返回值:
error:返回-1,errno被设置
success:返回现在位置相对于文件开头的位置的偏移量
Demo实现从文件开始的第二个下标开始读,Demo如下:
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int agrc,char *agrv[]){
int flags = O_RDONLY;
int fd = open(agrv[1],flags);
if(fd == -1){
perror("open file error");
return -1;
}
void *buf = malloc(128);
off_t offset = lseek(fd,2,SEEK_CUR);
read(fd,buf,128);
write(1,buf,128);
close(fd);
return 0;
}
补充:
值-结果 参数:将参数的地址作为实参传入被调函数,在被调函数中修改变量的值。
阻塞状态:scanf等待用户输入的状态就是一种阻塞状态。
使用mmap将文件映射到虚拟内存空间
上述对文件的操作,都是通过system call 来操作文件。这种方式操作文件比较安全,但是不利于对一个文件进行频繁的访问。例如,当需要修改文件中一个字符串中间的一个值的时候,就免不了频繁调用system call来实现。system call 的开销是很大的。
现在,我们通过将文件先加载到物理内存,然后通过mmap将虚拟地址空间映射到物理地址空间。通过操作内存来操作文件,方便。
其实,还是mmap函数的操作。
在之前的学习中,我们用mmap映射了一块物理内存地址用于操作,当时选择mmap的flags参数为MAP_PRIVATE,这个参数的说明里,表示在这种模式下,是不会把虚拟地址空间中的改动显示到其他映射到这个虚拟地址空间上的进程,且不会把更新同步到文件,fd的参数是失效的状态;
这里我们将flags的参数设置为 MAP_SHARED ,这种模式下,可以将程序对虚拟地址空间的更新同步到打开的文件中。
Demo如下:
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[]){
//设置打开方式为读写
int prot = PROT_READ|PROT_WRITE;
int flags = MAP_SHARED;
//打开文件
int fd = open(argv[1],O_RDWR);
//映射文件到虚拟地址空间
void *p_mmap = mmap(NULL,6,prot,flags,fd,0);
if(p_mmap == MAP_FAILED){
perror("mmap error");
return -1;
}
//对文件的内容进行操作
*((int *)p_mmap) = 0x31323334;
//关闭文件
close(fd);
//解除映射
munmap(p_mmap,6);
return 0;
}
-----------------------------------------------
linxin@ubuntu:~/UC/day06$ t_cat hello
hello
linxin@ubuntu:~/UC/day06$ mmap_file hello
linxin@ubuntu:~/UC/day06$ t_cat hello
4321o
补充:
计算机存储数据的数据叫做主机字节序,有两种。
小端:变量的高位字节存放在计算机的高位地址。
大端:变量的高位字节存放在计算机的低位地址。
一般计算机的主机字节序是小端。
网络字节序是大端!
可以通过以下程序来检测计算机的主机字节序:
#include<stdio.h>
typedef union{
short sh;
char ch;
}test;
int main(void){
test te;
te.sh = 0x0001;
if(te.ch == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
文件描述符的复制
介绍了两个system call :
dup(2) dup2(2)
#include <unistd.h>
int dup(int oldfd);
功能:复制一个文件描述符
参数:
oldfd:源文件描述符
返回值:
success:返回新的文件描述符,是这个进程没有使用的最小的文件描述符。
error:-1,errno被设置
int dup2(int oldfd, int newfd);
功能:复制一个文件描述符,
参数:
olfd:源文件描述符
newfd:指定了目标文件描述符,将这个参数的值,设置为新产生的文件描述符
返回值:
success:返回新的文件描述符,是这个进程没有使用的最小的文件描述符。
error:-1,errno被设置
dup和dup2的区别就在于dup2可以指定新的文件描述符,而dup则由系统分配。
下面,我们通过dup和dup2来实现,用一个文件代替标准输出,输出的所有内容,都记录到文件中。
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int agrc,char *argv[]){
char buf[128] = "hello,beijing,hangzhou\n";
//将文件用只读方式打开,如果文件不存在,创建他,如果文件存在则清空文件内容。
int flags = O_WRONLY|O_CREAT|O_TRUNC;
//创建文件时,权限0644
int oldfd = open(argv[1],flags,0644);
if(oldfd == -1){
perror("open file error");
return -1;
}
//保存标准输出文件描述符
int sfd = dup(1);
dup2(oldfd,1);
write(1,buf,128);
close(oldfd);
//恢复现场
dup2(sfd,1);
//关闭暂存的文件描述符
close(sfd);
return 0;
}
--------------------------------------------------
linxin@ubuntu:~/UC/day06$ ls
big_s day06.txt hello.txt lseek.c mmap_file.c redirect.c t_cat.c
big_s.c hello lseek mmap_file redirect t_cat
linxin@ubuntu:~/UC/day06$ redirect test
linxin@ubuntu:~/UC/day06$ t_cat test
hello,beijing,hangzhou
linxin@ubuntu:~/UC/day06$ ls
big_s day06.txt hello.txt lseek.c mmap_file.c redirect.c t_cat.c
big_s.c hello lseek mmap_file redirect t_cat test