文件系统
系统调用和标准库函数的区别:
1.缓冲文件系统
缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器而定。
fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等是带缓冲的。
2.非缓冲文件系统
缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。
open, close, read, write, getc, getchar, putc, putchar 等是不带缓冲的。
前者带f的属于高级IO,后者是低级IO。
前者返回一个文件指针,后者返回一个文件描述符(用户程序区的)。
前者有缓冲,后者无缓冲。
高级IO是在低级IO的基础上扩充而来的,在大多数情况下,使用高级IO。
系统调用 返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,
C的库函数,返回的是一个指向文件结构的指针。
文件描述符是linux下的一个概念,linux下的一切设备都是以文件的形式操作.如网络套接字、硬件设备等。当然包括操作文件。
标准c函数。返回文件流而不是linux下文件句柄
设备文件不可以当成流式文件来用,只能用系统调用。
标准库函数是用来操纵正规文件的,并且设有缓冲的,跟系统调用还是有一些区别。
一般用标准库函数打开普通文件,用函数调用打开设备文件。
他们的层次不同。 fopen可移植,open不能
标准库函数和系统调用最主要的区别是标准库函数在用户态下就有了缓存,在进行read和write的时候减少了用户态和内核态的切换,而系统调用则每次都需要进行内核态和用户态的切换;
表现为,如果顺序访问文件,以open举例 fopen系列的函数要比直接调用open系列快;如果随机访问文件open要比 fopen快。
1、Unix/Linux大部分系统功能是通过系统调用实现的,如open/close。
2、Unix/Linux的系统调用已被封装成C函数的形式,但它们并不是标准C的一部分。
3、标准库函数大部分时间运行在用户态,但部分函数偶尔也会调用系统调用,进入内核态,如malloc/free。
4、程序员自己编写的代码也可以调用系统调用,与操作系统内核交互,进入内核态,如brk/sbrk/mmap/munmap。
5、系统调用在内核中实现,其外部接口定义在C库中,该接口的实现借助软中断进入内核。
文件相关系统调用
open - 打开/创建文件
creat - 创建空文件
close - 关闭文件
read - 读取文件
write - 写入文件
lseek - 设置读写位置
fcntl - 修改文件属性
unlink - 删除硬链接
rmdir - 删除空目录
remove - 删除硬链接(unlink)或空目录(rmdir)
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
flags为以下值的位或:
O_RDONLY - 只读。
O_WRONLY - 只写。
O_RDWR - 读写。
O_APPEND - 追加。
O_CREAT - 创建,不存在即创建(已存在即直接打开,并保留原内容,除非有此位mode参数才有效。
O_EXCL - 排斥,已存在即失败。只选一个
O_TRUNC - 清空,已存在即清空 配合O_CREAT使用
O_NOCTTY - 非控,若pathname指向控制终端,则不将该终端作为控制终端。
O_NONBLOCK - 非阻,若pathname指向FIFO/块/字符文件,则该文件的打开及后续操作均为非阻塞模式。
O_SYNC - 同步,write等待数据和属性,被物理地写入底层硬件后再返回。
O_DSYNC - 数同,write等待数据,被物理地写入底层硬件后再返回。
O_RSYNC - 读同,read等待对所访问区域的所有写操作,全部完成后再读取并返回。
O_ASYNC - 异步,当文件描述符可读写时,向调用进程发送SIGIO信号。
open对比
#include <stdio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[]){
if(argc < 2){
printf("%s filename\n",argv[0]);
return -1;
}
//O_EXCL 排斥 结合O_CREAT使用 如果文件存在则打开失败
//如果文件不存在,则创建文件并打开 3
//如果O_CREAT 必须传第三个参数 即mode_t mode 权限
//O_TRUNC 如果文件存在则清空
//int fd = open(argv[1],O_WRONLY|O_CREAT|O_APPEND,0777);//rwxrwxrwx
int fd = open(argv[1],O_WRONLY|O_CREAT,0777);//rwxrwxrwx
if(fd == -1){
printf("%s\n",strerror(errno)); //打开文件失败的原因
return -1;
}
write(fd,"thanks you",10);
close(fd);//关闭
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc<2)
{
printf("%s filename",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"w");
if(fp == NULL)
{
printf("%s failed\n",argv[1]);
return -1;
}
char str[40] = "hello world!";
int i;
for(i = 0;i <strlen(str);i++)
{
fputc(str[i],fp);
}
fclose(fp);
return 0;
}
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 ch;
while(read(sfd,&ch,1)>0){
write(dfd,&ch,1);
}
*/
char str[128] = {};
ssize_t ret;
while((ret = read(sfd,str,128))>0){
//write(dfd,str,128);
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;
}
#include <stdio.h>
#include <string.h>
int copy(const char *srcfile,const char *destfile)
{
FILE *fpsrc = fopen(srcfile,"r");
if(fpsrc == NULL)
{
printf("fopen %s failed",srcfile);
return -1;
}
FILE *fpdest = fopen(destfile,"w");
if(fpdest == NULL)
{
printf("fopen %s failed",destfile);
fclose(fpsrc);
return -1;
}
int ch = 0;
while((ch = fgetc(fpsrc))!=EOF)
{
fputc(ch,fpdest);
}
fclose(fpsrc);
fclose(fpdest);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("%s filename",argv[0]);
}
copy(argv[1],argv[2]);
return 0;
}
lseek
1、每个打开的文件都有一个与其相关的“文件位置”。
2、文件位置通常是一个非负整数,用以度量从文件头开始计算的字节数。
3、读写操作都从当前文件位置开始,并根据所读写的字节数,增加文件位置。
4、打开一个文件时,除非指定了O_APPEND,否则文件位置一律被设为0。
5、lseek函数仅将文件位置记录在内核中,并不引发任何I/O动作。
6、在超越文件尾的文件位置写入数据,将在文件中形成空洞。
7、文件空洞不占用磁盘空间,但被算在文件大小内。
加密对比
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
//加密因子
char salt = 'a';//x^'a'^'a' ~x
int encode(const char *fileName){
int fd = open(fileName,O_RDWR);
if(fd == -1){
perror("open:");
return -1;
}
char ch = '\0';
while(read(fd,&ch,1)>0){
ch = ~ch+salt;
lseek(fd,-1,SEEK_CUR);
write(fd,&ch,1);
}
close(fd);
return 0;
}
int decode(const char *fileName){
int fd = open(fileName,O_RDWR);
if(fd == -1){
perror("open:");
return -1;
}
char ch = '\0';
while(read(fd,&ch,1)>0){
//ch = ~ch+'a';
ch = ~(ch-salt);
lseek(fd,-1,SEEK_CUR);
write(fd,&ch,1);
}
close(fd);
return 0;
}
//code 1|2 file
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s 1|2 file\n",argv[0]);//1加密 2解密
return -1;
}
if(1 ==atoi(argv[1])){//加密
encode(argv[2]);
}else if(2 == atoi(argv[1])){//解密
decode(argv[2]);
}else{
printf("%s 1 or 2 %s\n",argv[0],argv[2]);
}
return 0;
}
#include <stdio.h>
int code(const char *file)
{
FILE *fp = fopen(file,"r+");
if(fp == NULL)
{
return -1;
}
char c = 0;
while(fread(&c,1,1,fp)>0)
{
c=~c;
fseek(fp,-1,SEEK_CUR);
fwrite(&c,1,1,fp);
}
fclose(fp);
}
int main(int argc,char *argv[])
{
if(argc<2)
{
return -1;
}
code(argv[1]);
return 0;
}