转载请注明:http://blog.csdn.net/suool/article/details/38141047
问题引入
文件流和文件描述符的区别
上节讲到ANSI C 库函数的实现在用户态,流的相应资源也在用户空间,但无论如何实现最终都需要通过内核实现对文件的读写控制.因此fopen函数必然调用了对OS的系统调用.这一调用在LINUX下即为open, close, read, write等函数.这些都遵循POSIX标准.
so,在linux系统中是如何通过POSIX标准实现对文件的操作和目录的管理的呢?
问题解析
linux系统对文件结构的定义如下:fs.h头文件
查看完整文件,请访问:http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/include/linux/fs.h
struct file { 906 /* 907 * fu_list becomes invalid after file_free is called and queued via 908 * fu_rcuhead for RCU freeing 909 */ 910 union { 911 struct list_head fu_list; 912 struct rcu_head fu_rcuhead; 913 } f_u; 914 struct path f_path; 915 #define f_dentry f_path.dentry 916 #define f_vfsmnt f_path.mnt 917 const struct file_operations *f_op; 918 spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */ 919 atomic_long_t f_count; 920 unsigned int f_flags; 921 fmode_t f_mode; 922 loff_t f_pos; 923 struct fown_struct f_owner; 924 const struct cred *f_cred; 925 struct file_ra_state f_ra; 926 927 u64 f_version; 928 #ifdef CONFIG_SECURITY 929 void *f_security; 930 #endif 931 /* needed for tty driver, and maybe others */ 932 void *private_data; 933 934 #ifdef CONFIG_EPOLL 935 /* Used by fs/eventpoll.c to link all the hooks to this file */ 936 struct list_head f_ep_links; 937 #endif /* #ifdef CONFIG_EPOLL */ 938 struct address_space *f_mapping; 939 #ifdef CONFIG_DEBUG_WRITECOUNT 940 unsigned long f_mnt_write_state; 941 #endif 942 };
文件描述符&文件结构
而对于用户空间来说,任何打开的文件都将内分配一个非负数,用于表示这个被打开的文件,该值是文件描述符,因此任何进程在运行时候默认打开的三个流对象都有自己的文件描述符,对应为0,1,2.
标准文件的文件描述符定义如下:
下图是进程打开文件的内核数据结构:
文件流与文件描述符的转换
Linux为用户层提供了函数fileno()从文件流中读取其文件描述符,即获取struct FILE的_fileno成员,而fdopen()函数实现某个流与一个文件描述符相接.
函数声明如下:
POSIX标准下文件I/O 管理
首先看OS源代码对文件操作的定义:
打开文件,使用系统的open函数.
#include <fcntl.h> int open(const char *path, int oflag, ... );
该函数的详细说明,参见:pubs.opengroup.org/onlinepubs/009695399/functions/open.html
该函数的第一个参数是要打开文件的路径,第二个参数是打开文件的方式,各个方式的定义如下:
关闭文件使用close函数
#include <unistd.h> int close(int fildes);
详情参见:http://pubs.opengroup.org/onlinepubs/009695399/functions/close.html
创建文件
除了在open函数中的参数O_CREATE,还可以显式的使用create函数,
#include <fcntl.h> int creat(const char *path, mode_t mode);
第一个参数是创建文件的路径.,第二个参数是mode和umask一起决定的文件权限.成功返回创建的文件标识符,错误返回-1.
下面是一个示例程序.
#include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> int main(int argc,char *argv[]) { int fd_open,fd_open_create,fd_create; if((fd_open=open("/bin/ls",O_RDONLY))==-1) // open a file existed { perror("open"); exit(EXIT_FAILURE); } printf("the file's descriptor is:%d\n",fd_open); // open a file ,if not existed then create one which named temp1 if((fd_open_create=open("./tmp1",O_CREAT|O_EXCL,0644))==-1) { perror("open"); // exit(EXIT_FAILURE); } printf("the tmp1 file descriptor is:%d\n",fd_open_create); if((fd_create=creat("./tmp2",0644))==-1) // create temp2 { perror("create"); exit(EXIT_FAILURE); } printf("the tmp2 file descriptor is:%d\n",fd_create); close(fd_open); close(fd_create); close(fd_open_create); // close file return 0; }
结果如下:(第二个文件创建失败)......
文件控制fcntl
#include <fcntl.h> int fcntl(int fildes, int cmd, ...);
第一个参数是要修改的文件描述符,第二个是cmd相应的操作.
常用的命令有:
具体
各个参数的意义不再赘述,可以自己搜索.
读写文件内容
点击查看详细说明
文件定位
在对文件进行读写的时候,当打开文件时通常有一个读写位置,通常是指向文件的头部,若是以附加的的方式打开文件,则在文件你尾部.
下面是一个示例:
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> char buf1[] = "1234567890\n"; // 临时需要写入的信息 char buf2[] = "ABCDEFGHIJ\n"; char buf3[] = "abcdefghij\n"; int main(int argc,char *argv[]) { int fd; if ( (fd = creat("/tmp/test",0644 )) < 0) // 创建文件 { perror("creat"); exit(EXIT_FAILURE); } if (write(fd, buf1, 10) != 10) // 从文件开始写 { perror("write"); exit(EXIT_FAILURE); } if(lseek(fd, 20, SEEK_SET) == -1) // 从20位置写 { perror("lseek"); exit(EXIT_FAILURE); } if (write(fd, buf2, 10) != 10) { perror("write"); exit(EXIT_FAILURE); } if(lseek(fd, 10, SEEK_SET) == -1) // 从10开始写 { perror("lseek"); exit(EXIT_FAILURE); } if (write(fd, buf3, 10) != 10) { perror("write"); exit(EXIT_FAILURE); } return 0; }
result:
锁定解锁文件
flock()函数和fcntl()都可以对文件进行锁定和解锁.但是前者只能锁定整个文件,而后者可以提供任意位置的文件锁定.
下面的示例程序是fcntl对文件锁定操作,在某个进程锁定的时期,另一个进程无法操作文件.
其中一个进程的代码如下:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <time.h> struct flock* file_lock(short type, short whence) { static struct flock ret; ret.l_type = type ; ret.l_start = 0; ret.l_whence = whence; ret.l_len = 0; ret.l_pid = getpid(); return &ret; } int main(int argc,char *argv[]) { int fd = open(argv[1], O_WRONLY|O_APPEND); int i; time_t now; for(i=0; i<1000; ++i) { fcntl(fd, F_SETLKW, file_lock(F_WRLCK, SEEK_SET)); time(&now); printf("%s\t%s F_SETLKW lock file %s for 5s\n",ctime(&now),argv[0],argv[1]); char buf[1024] = {0}; sprintf(buf, "hello world %d\n", i); int len = strlen(buf); if(write(fd, buf, len)) printf("%s\t%s write file sccess\n",ctime(&now),argv[0],argv[1]); sleep(5); fcntl(fd, F_SETLKW, file_lock(F_UNLCK, SEEK_SET)); sleep(1); } close(fd); }
另一个代码与此类似,差别如下:
在一个终端运行一个进程如下:
目录流基本操作
本部分提到的函数的用法和上面的函数用法差不多,具体的需要请查阅源代码或文档使用.
1.打开关闭目录文件
2.读写目录内容
readdir() // 多线程操作中不安全
readdir_r() // 实现多线程读取目录内容
3.定位目录位置
telldir()类似与ftell()函数,返回目录相关联的当前的位置.
seekdir()类似与文件定位函数fseek()函数
rewinddir()函数类似与文件读写位置重置函数rewind()
4.添加和删除目录
5.当前工作路径操作
getcwd()获取当前路径
get_current_dir_name() 也是
修改当前目录:
案例应用
递归文件的目录复制操作
其中mian函数的流程图如下
程序的难点在于递归子目录,下面是递归子目录的流程图:
源代码如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <dirent.h> #include <linux/types.h> #include <fcntl.h> #include <errno.h> /*cp the link_file's src to dst*/ int cp_file(const char *src, const char *dst,mode_t mode) { int fd_src,fd_dst; if(-1 == (fd_src =open(src,O_RDONLY))) { perror("open src");exit(EXIT_FAILURE); } if(-1 == (fd_dst =open(dst,O_WRONLY|O_TRUNC|O_CREAT,mode))) { perror("open dst");exit(EXIT_FAILURE); } int len=0; do { char buf[1024]; len=read(fd_src,buf,1024); write(fd_dst,buf,len); }while(len>0); close(fd_src); close(fd_dst); } int cp_dir(const char *src,const char *dst) { DIR *dirp = NULL; if(NULL== (dirp=opendir(src))) { perror("opendir");exit(EXIT_FAILURE); } struct dirent *entp = NULL; while ( NULL != (entp =readdir(dirp))) { if (strcmp(entp->d_name, "..")==0 || strcmp(entp->d_name, ".")==0) //ignore ./ ../ { continue; } char *name_src =(char *) malloc( strlen(src) + 1 + strlen(entp->d_name) + 1 ); sprintf(name_src,"%s/%s\0",src,entp->d_name); char *name_dst =(char *) malloc( strlen(dst) + 1 + strlen(entp->d_name) + 1 ); sprintf(name_dst,"%s/%s\0",dst,entp->d_name); struct stat stat_src; if (stat(name_src, &stat_src) == -1) { fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno)); exit(EXIT_FAILURE); } if (S_ISREG(stat_src.st_mode)) //regular file { cp_file(name_src,name_dst,stat_src.st_mode); free(name_src); free(name_dst); } else if ( S_ISDIR( stat_src.st_mode )) { if( -1 ==mkdir(name_dst,stat_src.st_mode)) { perror("mkdir");exit(EXIT_FAILURE); } cp_dir( name_src, name_dst); free(name_src); free(name_dst); } } } int main(int argc,char *argv[]) { if (argc < 3) { fprintf(stderr,"usage %s src_dir dst_src\n",argv[0]);exit(EXIT_FAILURE); } struct stat stat_src; if (stat(argv[1], &stat_src) != 0) { fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno)); exit(EXIT_FAILURE); } umask(0000); if (S_ISREG(stat_src.st_mode)) //regular file { struct stat stat_dst; if (stat(argv[2], &stat_dst) == -1) { if(errno != ENOENT) //if errno not cause by file/dir not exist { fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno)); exit(EXIT_FAILURE); } else //if dst_flie not exist. { cp_file(argv[1],argv[2],stat_src.st_mode); } } else //dst file exist. { if(S_ISDIR(stat_dst.st_mode)) //cp a file to a exist dir { char *ptr=(char *)malloc(strlen(argv[2])+1+strlen(argv[1])+1); sprintf(ptr,"%s/%s\0",argv[2],argv[1]); cp_file(argv[1],ptr,stat_src.st_mode); } else //cp file to a exist file { printf("file %s exist, do you want overwrite it[y/n]:",argv[2]); char ch; while(!scanf("%c",&ch)) { getchar(); } if(ch =='Y' || ch == 'y' ) { unlink(argv[2]); cp_file(argv[1],argv[2],stat_src.st_mode); } else return 1; } } } else if (S_ISDIR(stat_src.st_mode)) //dir { struct stat stat_dst; if (stat(argv[2], &stat_dst) == -1) { if(errno != ENOENT) //if errno not cause by file/dir not exist { fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno)); exit(EXIT_FAILURE); } else //file/dir not exist { errno=0; if(-1 == mkdir(argv[2],stat_src.st_mode)) { perror("mkdir");exit(EXIT_FAILURE); } cp_dir(argv[1],argv[2]); } } else if(S_ISREG(stat_dst.st_mode)) //can't copy a dir to a file { fprintf(stderr,"can't copy a dir to a file\n");exit(EXIT_FAILURE); } else //copy a dir to a exsit dir , { char *ptr=(char *)malloc(strlen(argv[1])+1+strlen(argv[2])+1); sprintf(ptr,"%s/%s\0",argv[2],argv[1]); if(-1 == mkdir(ptr,stat_src.st_mode)) { perror("mkdir");exit(EXIT_FAILURE); } cp_dir(argv[1],ptr); free(ptr); } } }
运行结果如下:
显示成功复制.
Next:
普通文件/链接文件/目录文件---属性管理
终端以及串口编程
转载请注明:http://blog.csdn.net/suool/article/details/38141047
Linux 程序设计学习笔记----POSIX 文件及目录管理,布布扣,bubuko.com