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