Linux开发入门笔记——文件处理
虚拟文件系统(VFS)
什么是VFS(Virtual File System)?
异构文件系统之上的软件粘合层。通过VFS可以为访问文件系统的系统调用提供统一的抽象接口,实现无缝使用多个不同类型的文件系统
虚拟文件系统的特点
- 不同文件系统不是通过设备标识符访问,而是连成一个单一树型结构,用一个根目录表示文件系统;
- 文件系统按需挂载在Linux虚拟文件系统;
- 采用标准的UNIX(POSIX)系统调用读、写位于不同物理介质上的文件系统;
- 使用户很容易地在不同文件系统之间进行数据交换和管理,并在Linux上任意挂载多种不同类型文件系统。
虚拟文件系统举例
例1:cdrom在linux中的挂载
mount –t iso9660 /dev/cdrom /mnt/cdrom
例2:nfs在linux中的挂载
mount –t nfs 127.0.0.1:/mnt/nfs /mnt/nfs
文件的访问方式
-
系统调用——非缓冲文件操作
基于POSIX(Portable Operating System Interface)标准,采用C语言,实现了为应用程序提供与操作系统内核通信的服务的API
为用户提供有关操作系统的设备管理、I/O系统、文件系统和进程控制、通信及存储管理等方面的功能。 -
ANSI C库函数——缓冲文件操作
程序员在编程时,利用C库让操作系统为其提供文件访问等服务。
系统调用、C库的联系
POSIX 文件访问调用
- 文件流和文件描述符
- 文件流:stdin、stdout、stderr
功能1:实现不同输入输出格式转换。
功能2:缓冲功能,将数据读写集中,减少系统调用次数。(查看—isbuff.c、设置—setbuff.c)
全缓冲:在缓冲区满或者调用刷新函数后,进行I/O系统调用。
行缓冲:遇到换行符或缓冲区满,才I/O系统调用。
无缓冲:例如标准出错流。 - 文件描述符:0、1、2
文件描述符和文件流的相互转换—fileno
- 函数名:fileno
- 所在头文件:stdio.h
- 原型声明: int fileno(FILE *stream);
- 作用: 获取文件流底层文件描述符。
- 形参: stream :文件流
- 返回值: 调用成功,返回文件流所使用的文件描述符;不成功,返回-1。
- 相关例子:fileno.c
文件描述符和文件流的相互转换—fdopen
-
函数名:fdopen
-
所在头文件:stdio.h
-
原型声明: FILE *fdopen(int fildes, const char *mode);
-
作用: 在一个已打开的文件描述符的基础打开一个文件流。
-
形参: fildes :文件描述符
mode:打开文件流的模式与fopen一样。 -
返回值: 调用成功,返回一个新的文件流指针;不成功,返回NULL。
-
相关例子:fdopen.c
ANSI C标准文件I/O操作
fopen、fclose、fflush函数
fgetc、getc和getchar函数
fputc、putc和putchar函数
fgets和gets函数
fputs和puts函数
fread、fwrite函数
feof、ferror函数
fseek、ftell函数
格式化处理
printf、fprintf和sprintf函数
scanf、fscanf和sscanf函数
POSIX下文件的系统调用
open系统调用
close系统调用
write系统调用
read系统调用
fcntl系统调用
lseek系统调用
lstat、fstat和stat系统调用
dup、dup2系统调用
open函数
-
系统调用名:open
-
头文件:unistd.h
-
功能:打开path所指的文件。
int open(const char *path,int oflags)
int open(const char *path,int oflags,mode_t mode) -
参数
path:文件所在路径
oflags :设置open文件操作类型
mode :设置open文件模式 -
返回值:打开成功,返回一个唯一的文件描述符;不成功,返回-1,并设置相应的errno变量以指明出错的原因。
oflags的说明
mode的说明
文件类型宏定义和宏测试
文件类型宏测试定义
文件类型掩码定义
close函数
- 系统调用名:close
- 头文件:unistd.h
- 功能:将与fildes文件描述符相关联的文件断开。
int close(int fildes)
fildes:文件描述符 - 返回值:成功,返回值为0;不成功,返回值为-1。
fcntl函数
-
头文件: #include <fcntl.h>
-
原型声明:int fcntl(int fildes, int cmd, …);
-
作用:修改某个文件描述符的特性
-
形参:fd—需要修改属性的文件描述符;
cmd—常用操作
F_DUPFD:复制文件描述符
F_GETFD、F_SETFD:获取或者设置与文件描述符关联的close-on-exec标志。
F_GETFL、F_SETFL:获取或者设置文件状态标志和访问模式 -
fcntl函数举例
例1、如何使用?
fcntl(fildes, F_DUPFD, newfd);
fcntl(fildes, F_GETFD);
fcntl(fildes, F_SETFD, flags);
fcntl(fildes, F_GETFL);
fcntl(fildes, F_SETFL, flags);
例2、实际应用
fcntl_dup.c
fcntl_getfl.c
write函数
- 系统调用名:write
- 头文件:unistd.h
- 功能:将buf中的前nbytes个字节的数据写入到与fildes相关联的文件描述符中。
size_t write(int files,const void *buf,size_t nbytes) - 参数:
files:文件描述符
buf:数据的来源
nbytes:数据的长度(以字节为单位) - 返回值:写入成功,返回值的范围0~nbytes;不成功,返回-1,对应错误代码保存在全局变量 errno中.
- write系统调用举例
read函数
- 系统调用名:read
- 头文件:unistd.h
- 功能:将与fildes文件描述符相关联的文件的前nbytes个字节的数据读入buf中 size_t read(int
fildes,const void *buf,size_t nbytes) - 参数:
fildes:文件描述符
buf:数据的来源
nbytes :数据的长度(以字节为单位) - 返回值:读取成功,返回值的范围0~nbytes;不成功,返回-1,对应错误代码保存在全局变量errno中。
- read函数举例
lseek函数
- 头文件:unistd.h
- 功能:设置与文件描述符fildes相关的文件或设备的读写指针位置 off_t lseek(int fildes,off_t
offset,int whence) - 参数:
fildes:文件描述符
offset:设置的参照位置
SEEK_SET:文件开头位置;
SEEK_CUR:当前位置;
SEEK_END:文件末尾位置;
whence:相对于参照位置的偏移量 - 返回值:成功,返回值为从文件头到设置的位置的偏移量(以字节为单位);不成功,返回值为-1。
文件属性管理
读取文件属性:stat、lstat、fstat
用户名与UID转换:getpwuid、getpwnam
组名与GID转换:getgrgid、getgrnam
读取软链接源文件:readlink
内存映射:mmap、munmap
*stat函数
- 系统调用名—fstat/stat/lstat
- 所在头文件—unistd.h、sys/stat.h、sys/types.h
- 作用—获取文件的相关状态信息。
int fstat(int fildes,struct stat *buf) int
stat(const char *path,struct stat *buf) int lstat(const char
*path,struct stat *buf) - 参数:
fildes:文件描述符
path:文件的路径
buf:结构体struct stat的大小 - struct stat的内容
读取文件属性举例
读取文件类型
lstat_example.c
读取文件访问权限
stat_example.c
用户名与UID的转换
-
头文件
#include <pwd.h>
#include <sys/types.h> -
声明 struct passwd *getpwnam(const char *name); struct passwd
*getpwuid(uid_t uid); -
struct passwd {
char *pw_name;
char *pw_passwd;
uid_t pw_uid;
gid_t pw_gid;
char *pw_gecos;
char *pw_dir; char *pw_shell;
}; -
用户名与UID的转换举例
#include<stdio.h>
#include<pwd.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
struct passwd *ptr;
uid_t uid;
uid=atoi(argv[1]);
ptr=getpwuid(uid);
printf("name:%s\n",ptr->pw_name);
printf("passwd:%s\n",ptr->pw_passwd);
printf("home_dir:%s\n",ptr->pw_dir);
}
组名与GID的转换
- 声明
struct group* getgrgid(_gid_t _gid);
struct group* getgrnam(_const_char *_name); - struct group{
char *gr_name;
char *gr_passwd;
_gid_t gr_gid;
Char **gr_mem;
}
获取符号连接文件的源文件名
- 头文件: #include <unistd.h>
- 定义函数:int readlink(const char *path, char *buf, size_t bufsiz);
- 函数说明:readlink()会将参数path的符号链接内容到参数buf所指的内存空间,返回的内容不是以NULL作字符串结尾,但会将字符串的字符数返回。若参数bufsiz小于符号连接的内容长度,过长的内容会被截断。
- 返回值 :执行成功则传符号连接所指的文件路径字符串,失败返回-1, 错误代码存于errno
- 举例:symlink_exp.c
mmap、munmap函数
- 头文件:#include <sys/mman.h>
- 原型
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
int munmap(void *addr, size_t len); - 功能
mmap函数的作用是将虚拟地址映射到物理地址,或者映射到文件和文件位置。
munmap函数的作用是释放内存段。 - 参数
addr:内存起始地址。
len:内存分配长度。
prot:用来设置内存段的访问权限。
flags:控制着程序对该段内存的访问方式。
fildes:文件描述符。 - 举例
#include <stdio.h>
#define NUM (10)
typedef struct
{
int no;
char data[30];
}RECORD;
int main()
{
RECORD record;
int i;
FILE *fp = fopen("records.dat","w+");
if(fp == NULL)
{
printf("Can't open the file : records.dat\n");
return 1;
}
for(i=0; i < NUM; i++)
{
record.no = i;
sprintf(record.data, "Record-%d\n",i);
fwrite(&record, sizeof(record), 1, fp);
}
fclose(fp);
return 0;
}
#include <stdio.h>
typedef struct
{
int no;
char data[30];
}RECORD;
int main()
{
RECORD record;
FILE *fp = fopen("records.dat", "r+");
if(fp == NULL)
{
printf("Can't open the file : records.dat\n");
return 1;
}
fseek(fp, 5*sizeof(record), 1, SEEK_SET);
fread(&record, sizeof(record), 1, fp);
record.no = 2008;
sprintf(record.data, "Record-%d\n", record.no);
fseek(fp, 5*sizeof(record), 1, SEEK_SET);
fwrite(&record, sizeof(record), 1, fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#include <unistd.h> /*close函数*/
#include <sys/mman.h>
#include <fcntl.h> /*open函数*/
#define NUM (10)
typedef struct
{
int no;
char data[30];
}RECORD;
int main()
{
RECORD *mapped;
int filde = open("records.dat", O_RDWR);
mapped = (RECORD *)mmap( 0,
NUM * sizeof(RECORD),
PROT_READ | PROT_WRITE,
MAP_SHARED,
filde,
0);
mapped[5].no = 8888;
sprintf(mapped[5].data, "Record-%d\n", mapped[5].no);
msync((void*)mapped, NUM * sizeof(RECORD), MS_ASYNC);
munmap((void*)mapped, NUM * sizeof(RECORD));
close(filde);
return 0;
}
POSIX 目录管理
opendir函数
closedir函数
readdir函数
telldir函数、seekdir函数
mkdir函数、rmdir函数
opendir函数
- 函数名:opendir
- 所在头文件:sys/types.h dirent.h
- DIR *opendir(const char *name);
- 作用:打开一个子目录并建立一个子目录流。如果成功,它将返回一个指向DIR结构体的指针,以后对目录数据项的读操作将通过这个指针来完成的。
- 形参 name :子目录名
- 返回值: 调用成功返回打开的子目录流指针,不成功返回NULL。 相关函数:closedir
closedir函数
- 函数名:closedir
- 所在头文件:sys/types.h dirent.h
- 原型声明: int closedir(DIR *dirp);
- 作用: 关闭一个子目录流,并释放与之关联的资源。
- 形参: dirp :子目录流指针
- 返回值: 调用成功返回0,不成功返回-1。
- 相关函数:opendir
readdir函数
- 函数名:readdir
- 所在头文件:sys/types.h dirent.h
- struct dirent *readdir(DIR *dirp)
- 作用:返回一个指针,该指针指向的结构保存着子目录流dirp中下一个目录数据项的有关资料。后续的readdir返回的是后续的目录数据项。如果发生错误或达到子目录尾,readdir将返回NULL。POSIX兼容的系统在到达子目录尾时,只返回NULL,但不改变errno的值;当发生错误的时侯才修改errno的值。
- 形参 dirp :子目录流指针
- 返回值: 调用成功返回目录数据项指针,不成功返回NULL
- 相关函数:telldir seekdir
- 结构
telldir函数
- 函数名:telldir
- 所在头文件:sys/types.h dirent.h
- 原型声明: long int telldir(DIR *dirp);
- 作用: 获取当前子目录流中的当前位置。
- 形参: dirp :子目录流指针
- 返回值: 返回当前子目录流中的当前位置
- 相关函数:seekdir
seekdir函数
- 函数名:seekdir
- 所在头文件:sys/types.h dirent.h
- 原型声明: void seekdir(DIR *dirp, long int loc);
- 作用: 改变子目录流中的目录数据项指针的指向,通过loc来指定目录数据项指针所指向的位置。这个位置可通过telldir函数来获取。
- 形参: dirp :子目录流指针
loc:设置目录数据项指针的位置 - 返回值: 无
- 相关函数:telldir
mkdir函数
- 函数名:mkdir
- 所在头文件:sys/stat.h
- 原型声明: int mkdir(const char *path, mode_t mode);
- 作用: 将创建一个子目录,子目录名字将由path指出。
- 形参: path :需要创建的子目录名
mode :与open系统调用中使用O_CREAT时的设置一样 - 返回值: 调用成功返回0,不成功返回-1。
- 相关函数:rmdir
rmdir函数
- 函数名:rmdir
- 所在头文件:sys/stat.h
- 原型声明: int rmdir(const char *path);
- 作用: 删除子目录,但要求子目录为空。
- 形参: path :需要删除的子目录名
- 返回值: 调用成功返回0,不成功返回-1。
- 相关函数:mkdir
chdir函数
- 函数名:chdir
- 所在头文件:unistd.h
- 原型声明: int chdir(const char *path);
- 作用: 改变当前工作目录。
- 形参: path :需要切换的工作目录。
- 返回值: 调用成功返回0,不成功返回-1。
- 相关函数:getcwd
getcwd函数
-
函数名:getcwd
-
所在头文件:unistd.h
-
原型声明: char *getcwd(char *buf, size_t size);
-
作用: 把当前子目录名字写到指定的缓冲区buf中。
-
形参: buf :保存绝对路径的缓冲区。
size:至少比返回值大1. -
返回值: 调用成功返回指针buf,不成功返回NULL。
-
相关函数:chdir
-
举例
ls功能模拟
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
struct dirent *pDirent;
struct stat Stat;
void output_type_perm(mode_t mode)
{
char type[7]={'p','c','d','b','-','l','s'};
int index=((mode>>12)&0xF)/2;
printf("%c",type[index]);
char *perm[8]={"---","--x","-w-","-wx","r--","r-x","rw-","rwx"};
printf("%s",perm[mode>>6 &07]);
printf("%s",perm[mode>>3 &07]);
printf("%s",perm[mode>>0 &07]);
}
int wenjian_kezhix(mode_t mode)
{
unsigned int i,mask=0700;
int flag=0;
char *perm[8] = {"---","--x","-w-","-wx","r--","r-x","rw-","rwx"};
for(i=3;i;--i)
{
char *x=NULL;
x=perm[(mode&mask)>>(i-1)*3];
if(x == "-wx" || x == "r-x" || x == "rwx")
flag++;
else flag=0;
mask>>=3;
}
return flag;
}
void output_user_group(uid_t uid,gid_t gid)
{
struct passwd *user;
user = getpwuid(uid);
printf(" %s",user->pw_name);
struct group *group;
group= getgrgid(gid);
printf(" %s",group->gr_name);
}
void output_mtime(time_t time)
{
char buf[256];
memset(buf,'\0',256);
ctime_r(&time,buf);
buf[strlen(buf)-1]='\0';
printf(" ");
int j=4;
while(j<strlen(buf)-8)
{
printf("%c",buf[j]);
j++;
}
/* char buf[256];
memset(buf,'\0',256);
ctime_r(&time,buf);
buf[strlen(buf)-1]='\0';
//memcpy(buf,ctime(time),strlen(ctime(time))-1);
printf(" %s",buf); */
}
void show(void)
{
output_type_perm(Stat.st_mode);
printf(" %4d",Stat.st_nlink);
output_user_group(Stat.st_uid,Stat.st_gid);
printf(" %4ld",Stat.st_size);
output_mtime(Stat.st_mtime);
}
void block(step)
{
int i;
for(i=0;i<step;i++)
{
printf("##");
}
}
void printdir(char dir[256], int step)
{
DIR *pDir = opendir(dir);
char type[3]={'-','d','l'};
char buff[128];
if(pDir == NULL)
{
printf("Can't open the directory.\n");
}
chdir(dir);
while(pDirent = readdir(pDir))
{
if(lstat(pDirent->d_name,&Stat) == -1)
{
printf("lstat error\n");
}
if(S_ISREG(Stat.st_mode))
{
int a;
block(step);
printf("%c",type[0]);
show();
a=wenjian_kezhix(Stat.st_mode);
if(a>0)
printf("\033[22;32m"" %s""\033[0m",pDirent->d_name);
else
printf(" %s",pDirent->d_name);
printf("\n");
}
else if(S_ISDIR(Stat.st_mode))
{
if(strcmp(".",pDirent->d_name) == 0 || strcmp("..",pDirent->d_name) == 0)
continue;
block(step);
printf("%c",type[1]);
show();
printf("\033[22;34m"" %s""\033[0m",pDirent->d_name);
printf("\n");
printdir(pDirent->d_name, step+1);
}
else
{
memset(buff,'\0',128);
block(step);
printf("%c",type[2]);
show();
printf("\033[22;36m"" %s""\033[0m",pDirent->d_name);
printf(" -> ");
readlink(pDirent->d_name,buff,128);
printf("%s\n",buff);
}
}
step--;
chdir("..");
closedir(pDir);
}
int main(int argc,char *argv[])
{
printdir(argv[1], 0);
return 0;
}