01 学习目标
1.掌握stat/lstat函数的使用
2.了解文件属性相关的函数使用
3.了解目录操作相关的函数的使用
4.掌握目录遍历相关函数的使用
5.掌握dup,dup2函数的使用
6.掌握fcntl函数的使用
02 虚拟地址空间

03 打开最大文件数量
openmax.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types>
#include<sys/stat.h>
#include<fcnt1.h>
int main()
{
	int num = 3;//根据文件描述表,前三位是输入输出
	char filename[128]={0};
	while(1)
	{
		sprintf(filename,"temp_%04d",num++);
		if(open(filename,O_RDONLY|O_CREAT,0666)<0)
		{
			perror("open err");
			break;
		}
	}
	printf("num == %d\n",num);
	return 0;
}
makefile
###  xxx.c --->xxx
SrcFiles=$(wildcard *.c)
TargetFiles=$(patsubst %.c,%,$(SrcFiles))
all:$(TargetFiles)
%:%.c
	gcc -o $@ $^
clean:
rm -f  $(TargetFiles)
输出:num == 1025
ls -l temp.*
04 stat函数
inode节点与目录项:
 
 vi+n filename 打开文件,直接显示 第n行
获取文件信息
int stat(const char* pathname,struct stat* buf);
struct timespec
{
	-kernel_time_t tv_sec;  /*seconds*/当前时间到1970.1.1. 0:0:0的秒数
	long tv_nsec; /*nanoseconds*/纳秒
};
1s = 1000 ms 毫秒
1ms = 1000 us 微秒
1us = 1000 ns 纳秒
struct stat  
{   
    dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/  
    ino_t       st_ino;     /* inode number -inode节点号*/    
    mode_t      st_mode;    /* protection -类型与权限*/    
    nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/    
    uid_t       st_uid;     /* user ID of owner -user id*/    
    gid_t       st_gid;     /* group ID of owner - group id*/    
    dev_t       st_rdev;    /* device ID (if special file) -设备号,针对设备文件*/    
    off_t       st_size;    /* total size, in bytes -文件大小,字节为单位*/    
    blksize_t   st_blksize; /* blocksize for filesystem I/O -系统块的大小*/    
    blkcnt_t    st_blocks;  /* number of blocks allocated -文件所占块数*/    
    time_t      st_atime;   /* time of last access -最近存取时间*/    
    time_t      st_mtime;   /* time of last modification -最近修改时间*/    
    time_t      st_ctime;   /* time of last status change - */    
};  

 st_mode:
 0-2 其他用户权限
 3-5 组用户权限
 6-8 用户权限
 9-11 特殊权限位
 12-15 文件类型
stat函数参数
 pathname 文件名
 struct stat *buf 传出参数,定义 struct stat sb; &sb
 返回值:成功返回0,失败返回-1,设置errno
stat.c:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char * argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
	}
	struct stat sb;
	stat(argv[1],&sb);
	return 0;
}
05 stat命令
stat hello

06 实现ls -l 命令
需要使用struct passwd *getpwuid(uid_t uid);//获取用户名,需要传入uid,
 struct passwd 
{ 
    char * pw_name; /* Username, POSIX.1 */ 
    char * pw_passwd; /* Password */ 
    __uid_t pw_uid; /* User ID, POSIX.1 */ 
    __gid_t pw_gid; /* Group ID, POSIX.1 */ 
    char * pw_gecos; /* Real Name or Comment field */ 
    char * pw_dir; /* Home directory, POSIX.1 */ 
    char * pw_shell; /* Shell Program, POSIX.1 */ 
}; 
struct group *getgrgid(gid_t gid);//获得组名
获取本地时间
struct tm *localtime(const time_t *timep);
传入参数timep,对应stat函数得到的结构体的秒数(time_t类型)
返回tm结构
struct tm
{
  int tm_sec;                   //分后的秒(0~61)
  int tm_min;                   //小时后的分(0~59)
  int tm_hour;                  //小时(0~23)
  int tm_mday;                  //一个月天数(0~31)
  int tm_mon;                   //一个后的月数(0~11),需要+1
  int tm_year;                  //1900年后的年数 Year - 1900.  
  int tm_wday;                  //星期日开始的天数(0~6)
  int tm_yday;                  //从1月1日开始的时间(0~365)
  int tm_isdst;                 //夏令时标志(大于0说明夏令时有效,等于0说明无效,小于0说明信息不可用)
};
// -rwxrwxr-x 2 itheima itheima 22 5 月 20 10:19 hello
#include<stdio.h>
#include<unistd.h>
#inlude<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
#include<string.h>
#include<time.h>
#include<pwd.h>
#include<grp.h>
int main(int argc,char * argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
		return -1;
	}
	//调用stat,得到文件属性信息
	struct stat sb;
	stat(argv[1],&sb);
	
	//解析属性信息,st_mode,uid,gid,time
	//st_mode
	char stmode[11]={0};
	memset(stmode,'-',sizeof(stmode));
	if(S_ISREG(sb.stmode)) stmode[0]='-';//普通文件
	if(S_ISDIR(sb.stmode)) stmode[0]='d';
	if(S_ISCHR(sb.stmode)) stmode[0]='c';
	if(S_ISBLK(sb.stmode)) stmode[0]='b';
	if(S_ISFIFO(sb.stmode)) stmode[0]='p';
	if(S_ISLINK(sb.stmode)) stmode[0]='l';
	if(S_ISSOCK(sb.stmode)) stmode[0]='s';
	
	//解析权限
	if(sb.st_mode & S_IRUSR) stmode[1]='r';
	if(sb.st_mode & S_IWUSR) stmode[2]='w;
	if(sb.st_mode & S_IXUSR) stmode[3]='x';
	
	if(sb.st_mode & S_IRGRP) stmode[4]='r';
	if(sb.st_mode & S_IWGRP) stmode[5]='w';
	if(sb.st_mode & S_IXGRPR) stmode[6]='x';
	if(sb.st_mode & S_IROTH) stmode[7]='r';
	if(sb.st_mode & S_IWOTH) stmode[8]='w';
	if(sb.st_mode & S_IXOTH) stmode[9]='x';
	
	//分析 用户名,组名可以通过函数获得 getpwuid,getgrgid
	//时间获取
	struct tm *filename = localtime(&sb.st_atim.tv_sec);
	char timebuf[20]={0};
	sprintf(timebuf,"%d月 %d %02d:%02d",filetm->tm_mon+1,filetm->tm_mday,filetm->tm_hour,filetm->tm_min);
	
	printf("%s %d %s %s %ld %s %s\n",stmode,sb.st_nlink,getpwuid(sb.st_uid)->pw_name,getgrgid(sb.st_gid)->gr_name,sb.st_size,timebuf,argv[1]);
	return 0;
}
通过gdb调试:
 p查看结构体信息
 p/o 按照8进制显示数据
07 stat与lstat的区别
注意stat与lstat的区别:
 stat碰到链接,会追溯到源文件,穿透!!lstat并不会穿透。
 通过stat也可以计算文件大小:st_size
08 access,chmod与truncate
access判断文件的权限和是否存在
int access(const char* pathname,int mode);
- pathname 文件
- mode 具体权限
 R_OK
 W_OK
 X_OK
 F_OK
- 返回值:
 如果有权限或者文件存在,对应返回0.
access.c:
#include<stdio.h>
#include<unistd.h>
int main(int argc,char * argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
		return -1;
	}
	if(access(argv[1],R_OK)==0) printf("%s read ok!\n);
	if(access(argv[1],W_OK)==0) printf("%s write ok!\n);
	if(access(argv[1],X_OK)==0) printf("%s exe ok!\n);
	if(access(argv[1],F_OK)==0) printf("%s file exists!\n);
	return 0;
}
chmod修改文件权限
int chmod(const char *path,mode_t mode);
int fchmod(int fs,mode_t mode);
- 返回值
 成功返回0,
 失败返回-1,设置errno.
truncate 截断文件
int truncate(const char* path,off_t length);
- path 文件名
- length 长度如果大于源文件,直接拓展,如果小于源文件,截断为length长度。
- 返回值:
09 readlink_unlink
创建硬链接
int link(const char* oldpath,const char* newpath);
- oldpath 源文件
- newpath 硬链接文件
创建软链接
int symlink(const char* target,const char* linkpath);
读取软链接(读取不了硬链接)
ssize_t readlink(const char* pathname,char* buf,size_t bufsize);
- pathname 连接名
- buf 缓冲区
- bufsize 缓冲区大小
- 返回值:成功返回buf填充的大小,失败返回-1
int main()
{
	char buf[32]={0};
	readlink("hello.soft1",buf,sizeof(buf));
	printf("buf is %s\n",buf);
	
	//unlink("hello.soft1");
	//unlink("hello.hard1");
	unlink("hello");
	return 0;
}
unlink 删除软硬链接
int unlink(const char* pathname);
- pathname 对应的链接名字,文件也可以
unlink.c:
#include<unistd.h>
#include<stdlib.h>
#include<fcnt1.h>
#include<string.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
	int fd = open("world",O_WRONLY|O_CREAT,0666);
	unlink("world");
	
	int ret = write(fd,"hello",5);
	if(ret>0)
	{
		printf("write ok!%d\n",ret);
	}
	if(ret<0)
	{
		perror("write err");
	}
	close(fd);
	return 0;
}
输出:writer ok!5,但没有hello文档
 unlink使用:发现文档在使用时,并不会删除文档,结束后会删除,所有有上述输出效果。视频软件就是使用此技术:先缓冲视频,当看完后,会将缓存的视频删除。
10 chown与rename
chown 改变用户和组
int chown(const  *pathname,uid_t owner,gid_t group)
- pathname 文件名
- owner 用户ID,/etc/group
- group 组ID,/etc/group
rename 重命名文件
int rename(const char* oldpath,const char* newpath)
- oldpath 旧文件
- newpath 新文件
11 chdir_getcwd切换目录和获得工作路径
目录相关函数
 getcwd 获得当前工作路径
char *getcwd(char *buf,size_t size)
- buf 传出参数,路径
- size 缓冲区大小
- 返回值:
 成功返回 路径的指针
 失败返回NULL
chdir 改变工作路径-注意属于进程独有
int chdir(const char* path)
- path 对应的目标工作路径
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int main()
{
	//先切换工作目录
	
	//留下点痕迹
	int fd = open("temp",O_WRONLY|O_CREAT,0666);
	write(fd,"daociyiyou",10);
	close(fd);
	//显示当前工作目录
	char buf[256];
	getcwd(buf,sizeof(buf));
	
	printf("buf is [%s]\n",buf);
	return 0;
}
12 mkdir创建目录
mkdir 创建目录
int mkdir(const char *pathname,mode_t mode)
- pathname 路径
- mode mode& ~umask&0777注意权限,如果目录没有可执行权限,不可进入。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char * argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
		return -1;
	}
	mkdir(argv[1],0777);
	return 0;
}
13 读目录相关函数介绍
统计一下指定目录下普通文件的个数,要求子目录递归
 itheima@ubantu:~/linux/code/file_dir$find ./ -type f |wc -l
opendir 打开目录
DIR * opendir(const char* name)
- name 打开的目录
- 返回值,返回DIR* 的指针,指向目录项的信息
readdir 读目录
struct dirent *readdir(DIR * dirp);
- dirp 传入参数,opendir返回的指针
- 返回值:
 NULL代表读到末尾或者有错误
 读到目录项的内容
closedir 关闭目录
int closedir(DIR * dirp)
- dirp opendir得到的DIR指针
rewinddir 把目录指针恢复到起始位置
void rewinddir(DIR * dirp)
telldir 获取目录读写位置
long telldir(DIR *dirp)
- 返回值:
 成功返回 当前在目录中的位置
 失败返回-1,设置errno
seekdir 修改目录读写位置
void seekdir(DIR *dirp,long loc)
14 递归子目录统计普通文件个数
#include<stdio.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/types.h>
#include<string.h>
int count = 0;//定义一个全局的计数
int DirCount(char * dirname)
{
	printf("%s\n",dirname);
	//打开目录
	struct DIR *dirp=opendir(dirname);
	if(dirp == NULL)
	{
		perror("opendir err");
		return -1;
	}
	//循环读目录,如果是普通文件,count++,如果是目录,继续调用DirCount
	struct dirent *dentp = NULL;
	while((dentp = readdir(drip))!=NULL)//如果为NULL,代表读到目录末尾
	{
		//printf(“dirname:%s,dtype:%d\n”,dentp->d_name,dentp->d_type);
		if(dentp->d_type ==DT_DIR)//如果是目录
		{
			if(strcmp(".",dentp->d_name)==0||strcmp("..",dentp->d_name)==0)
			{
				continue;//如果是.或者..,直接跳过
			}
			//注意进程的工作路径,不能直接打开子目录
			//使用dirname拼接下一级子目录
			char newdirname[256]={0};
			sprintf(newdirname,"%s/%s",dirname,dentp->d_name);
		}
		if(dentp->d_type == DT_REG)
		{
			//普通文件,开始计数
			count++;
			printf("dname:%s\n",dentp->d_name);
	}
	//关闭目录
	closedir(drip);
	reuturn 0;
}
int main(int argc,char * argv[])
{
	if(argc!=2)
	{
		printf("./a.out filename\n");
		return -1;
	}
	DirCount(argv[1]);
	return 0;
}
此程序输出比命令的多,因为此程序也会生成文件。
15 errno说明
errno 输出函数
char *strerror(int errnum);
16 dup2和dup
dup2 重定向
int dup2(int oldfd,int newfd);
- 关闭newfd对应的文件描述符
- 将newfd重新指向为oldfd对应的文件
dup 复制文件描述符
int dup(int oldfd)
- 新返回一个文件描述符指向oldfd对应的文件
实现突发奇想的功能,在代码中执行2次print(“hello world\n”);一次输出到hello文件中,后一次输出到屏幕上。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int main()
{
	//先备份现场
	int outfd = dup(1);
	//先做重定向
	int fd = open("world",O_WRONLY|O_CREAT,0666);
	dup2(fd,1);//将标准输出重定向到fd对应的文件
	printf("hello worlf\n");
	//需要来一次刷新下
	fflush(stdout);
	
	//需要恢复,重新对应 标准输出
	dup2(outfd,1);
	printf("hello worlf\n");
	close(fd);
	return 0;
}



















