Linux开发入门笔记——文件处理

虚拟文件系统(VFS)

什么是VFS(Virtual File System)?

异构文件系统之上的软件粘合层。通过VFS可以为访问文件系统的系统调用提供统一的抽象接口,实现无缝使用多个不同类型的文件系统

Linux开发入门笔记——文件处理

虚拟文件系统的特点

  • 不同文件系统不是通过设备标识符访问,而是连成一个单一树型结构,用一个根目录表示文件系统;
  • 文件系统按需挂载在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库的联系
Linux开发入门笔记——文件处理

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的说明
Linux开发入门笔记——文件处理
mode的说明
Linux开发入门笔记——文件处理

文件类型宏定义和宏测试
Linux开发入门笔记——文件处理
文件类型宏测试定义
Linux开发入门笔记——文件处理

文件类型掩码定义
Linux开发入门笔记——文件处理

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系统调用举例
    Linux开发入门笔记——文件处理

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函数举例
    Linux开发入门笔记——文件处理

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的内容
    Linux开发入门笔记——文件处理

读取文件属性举例
读取文件类型
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
  • 结构
    Linux开发入门笔记——文件处理

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

  • 举例
    Linux开发入门笔记——文件处理

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;
}