Linux文件操作
系统调用和设备驱动程序(系统调用是调用设备驱动程序的)
为了向用户提供一个一致的接口,设备驱动程序封装了所有与硬件相关的特性。
open:打开文件或设备
read:从打开的问价或设备里读数据
wirte:向文件或设备写数据
close:关闭文件或设备
ioctl:把控制信息传递给设备驱动程序
库函数
针对输入输出操作直接使用底层系统调用的一个问题是它们效率非常低
1)使用系统调用会影响系统的性能。与函数调用相比,系统调用的开销要大些,因为在执行系统调用时候,linux必须从运行用户代码切换到内核代码。然后再返回用户代码。减少这种开销的一个号方法是在程序中尽量减少系统调用的次数,并且让每次系统调用完成尽可能多的工作,例如:每次读写大量的数据而不是每次读写一个字符
2)硬件会限制底层系统调用一次所能读写的数据块大小
库函数则在数据满足数据块长度要求长度时候才安排执行底层系统调用,这就极大的降低了系统调用的开销。
底层文件访问:
每个运行中的程序被称为进程(process),它有一些与之关联的文件描述符。这是一些小值整数。可以通过它们访问打开的文件或设备。有多少文件描述符可用取决于系统的配置情况。
每当一个程序开始运行时:它一般会有3个已经打开的文件描述符
0:标准输入
1:标准输出
2:标准错误
open系统调用:
调用成功返回一个可以被read和write和其他系统调用使用的文件描述符(唯一的)。如果两个程序同时打开同一个文件,他们会分别得到两个不同的文件描述符。如果他们都对文件进行写操作。那么用他们会各写各的。调用失败返回一-1,并设置全局变量errno来指明失败原因
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);
path:文件的路径+文件名。若文件路径省略则是在当前目录下
oflags:用于指定打开文件所采取的动作
O_RDONLY以只读方式打开
O_WRONLY以只写方式打开
O_RDWR以读写方式打开 这三个必选一个且只能选一个
O_APPEND:把数据追加在文件的末尾
O_TRUNC:爸文件长度设置为0,丢弃已有的内容
O_CREAT:如果有此选项则采用第二个函数,启用mode参数
O_EXCL:与O_CREAT一起使用,确保调用者创建出文件。使用这个可 选模式可以防止两个程序同时创建同一个文件,如果文件已经存在则 open调用失败。
访问权限的初始值(mode)
S_IRUSR:读权限。文件属主
S_IWUSR:写权限。文件属主
S_IXUSR:执行权限 文件属主
S_IRGRP:读权限 文件属组
S_IWGRP:执行权限 文件属组
S_IXGRP:执行权限 文件属组
S_IROTH:读权限 其他用户
S_IWOTH:执行权限 其他用户
S_IXOTH:执行权限 其他用户
out=open("/root/test/aaa.out",O_WRONLY|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR);
[root@localhost mytest]# ls /root/test/aaa.out -l
-rw------- 1 root root 0 Apr 13 22:38 /root/test/aaa.out
umask:系统变量,是由三位八进制整数组成的一个三位八进制数
三位从高到底以此是 用户,组,其他用户。
0表示允许相应任何权限
4禁止相应的读权限
2禁止相应的写权限
1禁止相应的执行权限
如果umask某一位被置为,则open中的参数将失效。
umask 600
out=open("/root/test/aaa.out",O_WRONLY|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR);
[root@localhost mytest]# ls /root/test/aaa.out -l
---------- 1 root root 0 Apr 13 22:57 /root/test/aaa.out
可以看到创建的文件并没有属主可读和属主可写权限
write系统调用
#include<unistd,h>
size_t write(int filds,const void *buf ,sizeof bnytes)
系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。它返回实际的字节数。返回0表示没有写入任何数据,返回-1表示调用wrrite出现了错误,错误代码保存在全局变量errno里
read系统调用
#include<unistd.h>
size_t read(int fildes,void *buf,size_t bnytes);
从文件描述符fildes相关的文件里读入nbytes个字节的数据并存入数据区buf中,返回实际读入的字节数,返回0表示没有读入任何数据,已达到文件尾部,返回-1表示调用read出现了错误,
close系统调用
终止文件描述符fildes与其对应文件的关联,文件描述符被释放并能够从新使用,成功返回0,出错时返回-1
#include<unistd.h>
int close(int fildes);
一个文件复制测试
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int main()
{
char c;
int in,out;
in=open("file.in",O_RDONLY);
if(in!=-1) write(1,"file.in is opened success!\n",28); out=open("/root/test/mytest/file.out",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR)
if(out!=-1) write(1,"file.out is created success!\n",29);
while(read(in,&c,1)==1) write(out,&c,1);
return 0;
}
[root@localhost mytest]# cat file.in file.out
total 40
-rwxrwxrwx 1 root root 5370 Apr 13 23:54 a.out
-rw-r--r-- 1 root root 308 Apr 13 07:24 copy_system.c
-rw-r--r-- 1 root root 0 Apr 13 23:54 file.in
-rw------- 1 root root 210 Apr 13 23:54 file.out
-rw-r--r-- 1 root root 388 Apr 13 23:54 write.c
total 40
-rwxrwxrwx 1 root root 5370 Apr 13 23:54 a.out
-rw-r--r-- 1 root root 308 Apr 13 07:24 copy_system.c
-rw-r--r-- 1 root root 0 Apr 13 23:54 file.in
-rw------- 1 root root 210 Apr 13 23:54 file.out
-rw-r--r-- 1 root root 388 Apr 13 23:54 write.c
lseek系统调用
lseek系统调用对文件描述符fildes的读写指针进行设置,也就是说可以用它来设这里写代码片
置文件的下一个读写位置。
#include<unistd.h>
#include<sys/types.h>
off_t lseek(int fildes, off_t ,int whence)
fildes:文件描述符
offset:指定位置
whence:取值如下
SEEK_SET:表明offset是一个绝对位置
SEEK_CUR:表明offset是相对于当前位置的一个相对位置
SEEK_END:表明offset是相对于文件尾的一个相对位置
标准I/O库
与底层文件描述符对应的是流(stream),它被实现为指向结构FILE的指针
在启动程序时有三个文件流是自动打开的,stdin,stdout,stderr.分别代表标准输入,标准输出,标准错误输出。他们均在stdio.h头文件中定义 。
fopen函数
主要用于文件和终端的输入输出,如果需要对设备进行明确的控制,最好使用底层系统调用。
在成功调用时返回一个非空的FILE*指针,失败返回NULL
fread函数:
#include<stdio.h>
siez_t fread(void *ptr ,size_t size,size_t nitems,FILE *stream)
数据从文件流strean读到ptr指向的数据 缓冲区里,fread和fwrite都是对数据记录进行操作,size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数,返回值是成功读到数据缓冲区里的记录个数而不是字节数。
fwrite函数
#include<stdio.h>
size_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream)
从指定的数据缓冲区读出数据记录,并写到输出流中,返回值是成功写入的记录个数
fclose
#include<stdio.h>
int fclose(FILE *stream);
关闭指定的文件流stream
fseek函数
#include<stdio.h>
int fseek(FILE *stream,long int offset,int whence);
参数用法与前面说的lseek是一样的,fseek返回一个整数:0成功 -1 失败 并设置errno指出错误
#include<stdio.h>
int fgetc(FILE *stream);//遇到文件尾 返回EOF指针
int getc(FILE *stream);
int getchar();//从标准输入读取一个字符
int fputc(int c,FILE *stream)
int putc(int c,FILE *stream)
int putchar(int c)
注意putchar和getchar都是把字符当做int类型而不是char类型来使用的,这就允许文件尾(EOF)表示取-1.
char *fgets(char *s ,int n,FILE *stream);
char *gets(chr *s);
fgets遇到换行符,已经传输了n-1个字符(最后一个为空字符以结束字符串),到达文件尾部,都会使函数调用终止。如果出现错误返回空指针并设置errno以指示出现错的类型
gets 从标准输入读取 ,并丢弃遇到的换行符,他在接受到的字符串的尾部加上一个null字节。
文件错误流:
为了表明错误许多stdio.h库函数会返回一个超出范围的值,比如空指针或EOF常数。此时错误由外部变量errno之处
#include<errno.h>
extern inr errno;
文件和目录的维护:
chmod
#include<sys/stat.h>
int chmod(comst char *path,mode_t mode);
mode和open中的mode取值一样
S_IRUSR:读权限。文件属主
S_IWUSR:写权限。文件属主
S_IXUSR:执行权限 文件属主
S_IRGRP:读权限 文件属组
S_IWGRP:执行权限 文件属组
S_IXGRP:执行权限 文件属组
S_IROTH:读权限 其他用户
S_IWOTH:执行权限 其他用户
S_IXOTH:执行权限 其他用户
#include<sys/types.h>
#include<sys/stat.h>
int mkdir(const char *path,mode_t mode); mode和open函数中的mode参数取值相同,并且受umask影响
#include<unistd.h>
int mkdir (const char *path);删除的是空目录
#include<unistd.h>
int chdir(const char *path)和cd一样
char *getcwd(char *buf,size_t size);将当前目录写到buf里,如果超过size,则返回NULL,若成功则返回指针buf
扫描目录
opendir函数;
打开一个目录并建立一个目录流,如果成功则返回一个指向DIR结构的指针,该指针用于读取目录数据项。失败则返回空指针。
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name);
readir函数
函数返回一个指针,指针指向的结构里保存着目录流dirp中下一个目录项的有关资料,如果发生错误或者到达目录尾,将返回null.
#include<sys/types.h>
#include<dirent.h>
struct dirent *readdir(DIR *dirp);
telldir函数
返回值记录一个目录流里当前的位置,你可以在随后的seekdir调用中利用这个值来重置目录扫描到当前位置
#include<sys/types.h>
#include<dirent.h>
long int telldir(DIR *dirp);
seekdir函数
seekdir函数的作用是设置目录流dirp的目录项指针,loc的值用来设置指针位置,他应该通过前一个telldir调用获得
void seekdir(DIR *dirp,long int loc);
close函数
关闭一个目录流并释放与之关联的资源,执行成功时返回0,错误时返回-1
下面是一个扫描目录的程序
#include<sys/types.h>
#include<dirent.h>
int closedir(DIR *dirp);
#include<unistd.h>
#include<stdio.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
void printdir(char *dir,int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp=opendir(dir))==NULL)//打开dir指向的目录并建立一个目录流给dp
{
printf("cannot open directory:%s\n",dir);
return ;
}
chdir(dir);//进入这个目录下
while((entry=readdir(dp))!=NULL){//如果当前目录中的下一个目录项不为孔
lstat(entry->d_name,&statbuf);//latat返回该链接指向文件的信息
if(S_ISDIR(statbuf.st_mode)){//如果是目录的话
if(strcmp(".",entry->d_name)==0||
strcmp("..",entry->d_name)==0)
continue;//如果是当前目录项和上一目录项则不打印
printf("%*s%s/\n",depth,"",entry->d_name);//打印目录,先打印depth个空格
printdir(entry->d_name,depth+4);//进入这个目录里
}
else printf("%*s%s\n",depth,"",entry->d_name);//打印文件名
}
chdir("..");//回到上一层目录
closedir(dp);//关闭dir指向的目录建立的目录流
}
int main()
{
printdir("/root/test",0);
return 0;
}
运行结果如下
[root@localhost mytest]# ./a.out
a2/
a2_2
a2_1
printdir.c
write.c
a3/
copy_system.c
file.out
.write.c.swp
file.in
a1/
a1_2
a1_1
a.out
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。