Linux 程序设计学习笔记----POSIX 文件及目录管理

转载请注明: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相应的操作.

常用的命令有:

具体各个参数的意义不再赘述,可以自己搜索.

读写文件内容

点击查看详细说明

read()函数

write()函数

文件定位

在对文件进行读写的时候,当打开文件时通常有一个读写位置,通常是指向文件的头部,若是以附加的的方式打开文件,则在文件你尾部.

lseek()函数

下面是一个示例:

#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.打开关闭目录文件

函数opendor()

closedir()函数

2.读写目录内容

readdir()      // 多线程操作中不安全

readdir_r()  // 实现多线程读取目录内容

3.定位目录位置

telldir()类似与ftell()函数,返回目录相关联的当前的位置.

seekdir()类似与文件定位函数fseek()函数

rewinddir()函数类似与文件读写位置重置函数rewind()

4.添加和删除目录

mkdir()

rmdir()

5.当前工作路径操作

getcwd()获取当前路径

get_current_dir_name() 也是

修改当前目录:

chdir()   &&    fchdir()

案例应用

递归文件的目录复制操作

其中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

时间: 2024-10-01 19:22:42

Linux 程序设计学习笔记----POSIX 文件及目录管理的相关文章

Linux System Programming 学习笔记(八) 文件和目录管理

1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是LInux内核通过数据结构表示的实体 inode存储相关联文件的元数据 ls -i 命令获取文件的inode number /* obtaining the metadata of a file */ #include <sys/types.h> #include <sys/stat.h>

CentOS学习笔记--基本命令--文件与目录管理

Linux基本命令--文件与目录管理 本节节选自鸟哥的 Linux 私房菜 -- 基础学习篇目录  第七章.Linux 文件与目录管理  ls(文件与目录的检视) ls命令就是list的缩写,ls可以说是最常用命令,因为我们随时都要知道文件或者是目录的相关属性. [[email protected] ~]# ls [-aAdfFhilnrRSt] 目录名称 [[email protected] ~]# ls [--color={never,auto,always}] 目录名称 [[email p

linux学习笔记之 文件和目录管理

这里列这些常用的命令提醒一下自己,免得老是会忘记那么几个 cd pwd mkdir rmdir ls mv cp rm cat tac nl more less head tail od ls -l file                    <==显示的是默认的mtimels --time={ctime|atime] file  <==显示指定的时间类型ls --full-time                <==显示完整的时间 mtime mtime(modification

Linux 程序设计学习笔记----动手编写makefile文件

Befroe Beginning. 之前定了暑假的plan ,关于Linux的书籍现在在看的是ALP和Linux高级程序设计(杨宗德)第三版.在计划中的是Linux高级环境编程. 现在开始关于Linux程序设计的第一篇学习笔记. 本来打算把名字写成教程,不过觉得自己完全是新手在自学,还是写学习笔记比较负责和适合. 希望可以一起学习进步. 引入 首先我们假设这样一个场景.我们有一个程序包含了三个文件,分别是源码文件main_plus,c和function_plus.c以及头文件mydefine_p

Linux 程序设计学习笔记----ANSI C 文件I/O管理

转载请注明出处:http://blog.csdn.net/suool/article/details/38129201 问题引入 文件的种类 根据数据存储的方式不同,可以将文件分为文本文件和二进制文件.具体的区别和关系如下: 文本文件与二进制文件在计算机文件系统中的物理存储都是二进制的,也就是在物理存储方面没有区别都是01码,这个没有异议,他们的区别主要在逻辑存储上,也就是编码上. 文本文件格式存储时是将值作为字符然后存入其字符编码的二进制,文本文件用'字符'作为单位来表示和存储数据,比如对于1

Linux 程序设计学习笔记----Linux下文件类型和属性管理

转载请注明出处:http://blog.csdn.net/suool/article/details/38318225 部分内容整理自网络,在此感谢各位大神. Linux文件类型和权限 数据表示 文件属性存储结构体Inode的成员变量i_mode存储着该文件的文件类型和权限信息.该变量为short int类型. 这个16位变量的各个位功能划分为: 第0-8位为权限位,为别对应拥有者(user),同组其他用户(group)和其他用户(other)的读R写W和执行X权限. 第9-11位是权限修饰位,

Linux 程序设计学习笔记----文件管理系统

本文部分整理自网络 Linux下文件系统管理 1.VFS文件系统概述 linux采用VFS来管理文件系统,而且linux设计的原则之一就是everything is file.因此文件管理系统是linux设计最核心的体现. VFS的全称是Virtual File System (虚拟文件系统). 总体上说 Linux 下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(Virtual Filesystem Switch),三是挂载到 VFS 中的各实际文件系统,

Linux 程序设计学习笔记----进程管理与程序开发(下)

转载请注明出处:http://blog.csdn.net/suool/article/details/38419983,谢谢! 进程管理及其控制 创建进程 fork()函数 函数说明具体参见:http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html 返回值:Upon successful completion, fork() shall return 0 to the child process and shall re

Linux 程序设计学习笔记----终端及串口编程基础之概念详解

转载请注明出处,谢谢! linux下的终端及串口的相关概念有: tty,控制台,虚拟终端,串口,console(控制台终端)详解 部分内容整理于网络. 终端/控制台 终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念. 1.终端 一台主机,连很多终端,终端为主机提供了人机接口,每个人都通过终端使用主机的资源. 终端有字符哑终端和图形终端两种. 控制台是另一种人机接口, 不通过终端与主机相连, 而是通过显示卡-显示器和键盘接口分别与主机相连, 这是人控制主机的第一人机接口.