一 open函数
1.输入 shift+k(大写K),可以跳转到man中查询函数的文档
2.供用户能打开的文件个数为 3~1023, 前3个(0~2)默认总是被打开(STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO),文件描述符存在内核区进程管理PCB控制块中
3.C库函数与系统函数的关系,如下图所示:
image-20210401110838358.png
man 2 open #查看open文档
// open 打开文件
//pathname:文件名
//flags:访问模式,O_RDONLY,O_WRONLY,ORDWR,O_CREAT,O_EXCL,O_TRUNC
//mode:权限,例如777
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
#include<sys/types.h> //open
#include<sys/stat.h> //open
#include<fcntl.h> //open
#include<stdio.h> //perror
#include<stdlib.h> //exit
#include<unistd.h> //close
int main(){
//打开文件
int fd;
fd=open("xxx",O_RDWR); //打开不存在的文件
printf("fd: %d\n",fd); // -1
if(fd == -1){
perror("open file"); //open file: No such file or directory
exit(1);
}
//关闭文件
int ret;
ret = close(fd);
printf("ret: %d\n",ret);
if(ret == -1){
perror("close file");
exit(1);
}
return 0;
}
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
//打开文件
int fd;
fd=open("openTest.cpp",O_RDWR); //打开已经存在的文件
printf("fd: %d\n",fd); // 3
if(fd == -1){
perror("open file");
exit(1);
}
//关闭文件
int ret;
ret = close(fd);
printf("ret: %d\n",ret); // 0
if(ret == -1){
perror("close file");
exit(1);
}
return 0;
}
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
//打开文件
int fd;
//创建文件后发现权限不是777,因为O_CREAT创建文件时文件权限内含一个本地掩码,文件的实际权限=给定权限与(&)取反(~)本地掩码,实际权限为775,这是操作系统对文件的保护机制
可以通过umask命令得到本地掩码,然后计算出实际权限
fd=open("newFile",O_RDWR|O_CREAT,0777);
printf("fd: %d\n",fd); // 3
if(fd == -1){
perror("open file");
exit(1);
}
//关闭文件
int ret;
ret = close(fd);
printf("ret: %d\n",ret); // 0
if(ret == -1){
perror("close file");
exit(1);
}
return 0;
}
- 文件截断(即文件原本有内容,执行完后清空(截断)为0)
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
//打开文件
int fd;
fd=open("newFile",O_RDWR|O_TRUNC);
printf("fd: %d\n",fd); // 3
if(fd == -1){
perror("open file");
exit(1);
}
//关闭文件
int ret;
ret = close(fd);
printf("ret: %d\n",ret); // 0
if(ret == -1){
perror("close file");
exit(1);
}
return 0;
}
二 read和write函数
// read write 读写文件
//1. man 2 read 或者 在vim中,2 shift+k 查看read函数的使用
//2. 参数
//fd:文件描述符
//buf(buffer):用户给定缓冲区
//count:缓冲区大小
//3. 返回值
//size_t是int类型无符号返回值,ssize_t是有符号,有符号需要返回-1
// 1) -1 ———读文件失败
// 2) 0 ———文件已读完
// 3) >0 ——— 读取的字节数,fread返回值也是字节数
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
- 使用read和write函数完成从文件中读取数据保存到另一个新文件中
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h> //read write
int main(){
//打开一个已经存在的文件
int fd1 = open("rawFile",O_RDONLY);
if(fd1 == -1){
perror("open file fd1");
exit(1);
}
//创建一个新文件 --写操作
int fd2 = open("newFile",O_WRONLY|O_CREAT,0664);
if(fd2 == -1){
perror("open file fd2");
exit(1);
}
//缓冲区
char buf[2048] = {0};
//read file
int count = read(fd1,buf,sizeof(buf));
if(count == -1){
perror("read");
exit(1);
}
while(count){
//将读出的数据写入另一个文件中
int ret = write(fd2,buf,count);
printf("write bytes: %d\n",count);
if(ret == -1){
perror("write");
exit(1);
}
//continue read file
count = read(fd1,buf,sizeof(buf));
}
//close file
close(fd1);
close(fd2);
}
三 lseek函数
// lseek 修改文件的读写指针
off_t lseek(int fd, off_t offset, int whence);
- lseek使用,获取文件大小和生成空洞文件,空洞文件使用案例:迅雷下载,首先生成10G大小的空洞文件,再使用并行的方式对不同位置进行并行下载写入。若不生成空洞文件则无法做到
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
int fd = open("file",O_RDWR);
if(fd == -1){
perror("open file");
exit(1);
}
/*测得文件大小*/
int ret = lseek(fd,0,SEEK_END);
printf("size: %d\n",ret);
/*生成空洞文件*/
ret = lseek(fd,2000,SEEK_END);
printf("new size: %d\n",ret);
/*再写入一个字符,保证生成成功*/
write(fd,"a",1);
close(fd);
return 0;
}
四 stat函数
// stat 获取文件的一些参数描述信息
int stat(const char *pathname, struct stat *buf);
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
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 512B blocks allocated */
};
st_mode:
1) 该变量占2byte共16位,整型
2) 掩码的使用st_mode &(与运算)掩码(8进制)(获得全部权限需要&掩码,如果是单一权限则直接用前面的即可)
3) 其他人权限(0-2bit)(掩码:S_IRWXO 00007过滤st_mode中除其他人权限以外的信息)
a) S_IROTH 00004
b) S_IWOTH 00002
c) S_IXOTH 00001
4) 所属组权限(3-5bit)(掩码:S_IRWXG 00070过滤st_mode中除所属组权限以外的信息)
a) S_IRGRP 00040
b) S_IWGRP 00020
c) S_IXGRP 00010
5) 文件所有者权限(6-8bit)(掩码:S_IRWXU 00700过滤st_mode中除所有者权限以外的信息)
a) S_IRUSR 00400
b) S_IWUSR 00200
c) S_IXUSR 00100
6) 特殊权限位(9-11bit)(很少用)
a) S_ISUID 0004000 设置用户ID
b) S_ISGID 0002000 设置组ID
c) S_ISVTX 0001000 黏着位
7) 文件类型(12-15bit)(掩码:S_IFMT 0170000过滤st_mode中除文件类型以外的信息)
与掩码做与运算过滤掉除文件类型以外的信息得到文件类型的八进制码与下面七个文件类型比对即可找出是哪个文件类型
a) S_IFSOCK 0140000 套接字
b) S_IFLINK 0120000 符号链接(软链接)
c) S_IFREG 0100000普通文件
d) S_IFBLK 0060000 块设备
e) S_IFDIR 0040000目录
f) S_IFCHR 0020000字符设备
g) S_IFIFO 0010000 管道
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdlib.h>
#include<pwd.h>
#include<grp.h>
#include<unistd.h>
#include<time.h>
int main(int argc, char* argv[]){ //argc参数个数,argv数组
if(argc < 2){ /传两个
printf("./a.out filename\n");
exit(1);
}
struct stat st;
int ret = stat(argv[1],&st);
if(ret == -1){
perror("stat");
exit(1);
}
//存储文件类型和访问权限
char perms[11] = {0};
//判断文件类型
switch(st.st_mode & S_IFMT){
case S_IFREG:
perms[0] = '-';
break;
case S_IFLNK:
perms[0] = '1';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFIFO:
perms[0] = 'P';
break;
case S_IFCHR:
perms[0] = 'c';
break;
default:
perms[0] = '?';
break;
}
//判断文件的访问权限
//文件所有者
perms[1] = (st.st_mode & S_IRUSR)?'r':'-';
perms[2] = (st.st_mode & S_IWUSR)?'w':'-';
perms[3] = (st.st_mode & S_IXUSR)?'x':'-';
//文件所属组
perms[4] = (st.st_mode & S_IRGRP)?'r':'-';
perms[5] = (st.st_mode & S_IWGRP)?'w':'-';
perms[6] = (st.st_mode & S_IXGRP)?'x':'-';
//其他人
perms[7] = (st.st_mode & S_IROTH)?'r':'-';
perms[8] = (st.st_mode & S_IWOTH)?'w':'-';
perms[9] = (st.st_mode & S_IXOTH)?'x':'-';
//硬链接计数
int linkNum = st.st_nlink;
//文件所有者
char* fileUser = getpwuid(st.st_uid)->pw_name;
//文件所属组
char* fileGrp = getgrgid(st.st_gid)->gr_name;
//文件大小
int filesize = (int) st.st_size;
//修改时间
char* time = ctime(&st.st_mtime);
char mtime[512] = {0};
strncpy(mtime,time,strlen(time)-1);
char buf[1024];
sprintf(buf,"%s %d %s %s %d %s %s",perms,linkNum,fileUser,fileGrp,filesize,mtime,argv[1]);
printf("%s\n",buf);
return 0;
}
# result
-rw-rw-r-- 1 ypw ypw 38567 Thu Apr 1 11:44:51 2021 rawFile
1. state函数:穿透(追踪)函数 ——软连接,即根据软连接追踪到所需执行的文件查看大小
2. lstate函数:不穿透(不追踪)函数 ——直接读软连接文件的大小
3. 区别就是操作软连接时是否追踪,
4. 其他非穿透命令 ls -l, rm ; 其他穿透命令 vim
五 文件函数
5.1 access 函数
// access 判定文件是否有指定的权限
int access(const char *pathname, int mode);
F_OK tests for the existence of the file. R_OK, W_OK, and X_OK test whether the file exists and grants read, write, and execute permissions
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]){
if(argc < 2){
printf("./a.out filename\n");
exit(1);
}
int ret = access(argv[1],W_OK);
if(ret == -1){
perror("access");
exit(1);
}
printf("you have write permission!\n");
return 0;
}
5.2 chmod 函数
// chmod 修改文件的权限
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
//传入 文件名,权限值
int main(int argc, char* argv[]){
if(argc < 3){
printf("./a.out filename pmode\n");
exit(1);
}
// 字符串转为指定进制的整数类型
int pmode = strtol(argv[2],NULL,8);
int ret = chmod(argv[1],pmode);
if(ret == -1){
perror("chmod");
exit(1);
}
return 0;
}
5.3 chown 函数
// chown 修改文件的拥有者和所属组
int chown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group); //fchown文件操作
int lchown(const char *pathname, uid_t owner, gid_t group); //lchown不穿透
5.4 truncate 函数
// truncate 修改文件到指定的长度,若指定的长度小于原文件,则截取;若大于原文件,则产生空洞作为占位
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
// 传入文件名和指定的文件的大小
int main(int argc, char* argv[]){
if(argc < 3){
printf("./a.out filename len\n");
exit(1);
}
int len = strtol(argv[2],NULL,10);
int ret = truncate(argv[1],len);
if(ret == -1){
perror("truncate");
exit(1);
}
return 0;
}
5.5 链接函数
1. link函数:作用:创建一个硬链接
int link(const char *oldpath,const char *newpath);
2. symlink函数:作用:创建一个软链接(符号链接)
int symlink(const char *oldpath,const char *newpath);
3. readlink函数:作用:读软连接对应的文件名,不是读内容。只能读软链接,buf读出来的是软链接所指文件的绝对路径。
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
4. unlink函数:作用:删除一个文件的目录项并减少它的链接数,若成功返回0,失败返回-1,错误信息存与errno。
使用:
a) 如果想要通过调用这个函数来成功删除文件,你必须拥有这个文件的所属目录的写(w)和执行(x)权限
b) 如果是符号链接,删除符号链接
c) 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode
d) 如果文件硬链接为0,但有进程已打开该文件,并持有文件描述符,则等进程关闭该文件时,kernel才真正去删除该文件,利用该特性创建临时文件,先open或creat创建一个文件,马上unlink此文件
int unlink(const char *pathname)
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main(){
//创建临时文件
int fd = open("tmpfile",O_CREAT|O_RDWR,0664);
if(fd == -1)
{
perror("open");
exit(1);
}
//删除临时文件
int ret = unlink("tmpfile");
//write file
write(fd,"world\n",6);
//重置指针
lseek(fd,0,SEEK_SET);
//read file
char buf[24] = {0};
int len = read(fd,buf,sizeof(buf));
//将读出的内容写在屏幕上
write(1,buf,len);
//关闭文件
close(fd);
return 0;
}
5.6 rename 函数
// 文件重命名
int rename(const char *oldpath,const char *newpath)
六 目录操作函数
6.1 chdir 函数
//修改当前进程的路径
int chdir(const char *path);
6.2 getcwd 函数
//获取当前进程工作目录
char* getcwd(char *buf,size_t size);
6.3 mkdir 函数
//创建目录(创建的目录需要有执行权限,否则无法进入目录)
int mkdir(const char *pathname,mode_t mode);
6.4 opendir readdir closedir 函数
// 打开一个目录
DIR*opendir(const *name);
// 读目录
struct dirent *readdir(DIR *dirp);
// 关闭目录
int closedir(DIR *dirp);
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
d_type:
DT_BLK This is a block device.
DT_CHR This is a character device.
DT_DIR This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK This is a symbolic link.
DT_REG This is a regular file.
DT_SOCK This is a UNIX domain socket.
DT_UNKNOWN The file type is unknown.
- opendir readdir closedir 使用,实现递归获取目录中文件的数码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<dirent.h>
#include<string.h>
//定义一个函数实现递归功能
int getFileNum(char* root){
//打开目录
DIR* dir = NULL;
dir = opendir(root);
int total = 0;
if (dir == NULL){ //如果目录能打开,返回的肯定是非0值
perror("opendir");
exit(1);
}
//遍历当前打开的目录
struct dirent* ptr = NULL;
char path[1024] = {0};
while((ptr = readdir(dir))!=NULL){
//过滤掉. 和 ..
if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0){
continue;
}
//递归 读目录
if(ptr->d_type == DT_DIR){
sprintf(path,"%s/%s",root,ptr->d_name);
total += getFileNum(path);
}
//如果是普通文件
if(ptr->d_type == DT_REG){
total++;
}
}
//关闭目录
closedir(dir);
return total;
}
int main(int argc, char* argv[]){
if(argc < 2){
printf("./a.out dir");
exit(1);
}
int total = getFileNum(argv[1]);
printf("%s has file numbers: %d\n",argv[1],total);
return 0;
}
七 dup和dup2 函数
//复制现有的文件描述符(重定向文件描述符)
int dup(int oldfd); //返回值是文件描述符中没有被占用的最小的文件描述符。
int dup2(int oldfd,int newfd); //将old复制给new,如果new是一个被打开的文件描述符,在拷贝之前先关掉new,如果old和new是同一个文件描述符,则不会关闭而是返回同一个文件描述符old。
- dup 使用,复制文件描述符,实现向同一个文件写入数据
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
int main(){
int fd = open("a.txt",O_RDWR);
if(fd == -1){
perror("open");
exit(1);
}
printf("current fd: %d\n",fd); //3
//找到进程文件描述表中 第一个可用的文件描述符
//将参数指定的文件复制到该描述符后,返回这个描述符
int ret = dup(fd);
if(ret==-1)
{
perror("dup");
exit(1);
}
printf("new fd: %d\n",ret); //4
char* buf = "Hello World\n";
char* buf2 = "Hello Vim\n";
write(fd,buf,strlen(buf));
write(ret,buf2,strlen(buf2));
close(fd);
close(ret);
return 0;
}
八 fcntl 函数
//改变已经打开的文件的属性,例如打开文件的时候(文件为只读),修改文件的时候,如果要添加追加O_APPEND,可使用fcntl在文件打开时修改。
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
- fcntl 使用,使用只写打开文件,在打开文件中修改为追加的方式,测试是否成功改变写入方式
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
//最后功能为:一大段文本内容中,第一句话覆盖掉了文本中的字符,第二段话追加到文本最末尾处。
int main(){
//只写的方式打开文件
int fd = open("a.txt",O_WRONLY);
if(fd == -1){
perror("open");
exit(1);
}
//输入新的内容,该部分会覆盖原来的旧内容
char* buf = "Hello";
int ret = write(fd,buf,strlen(buf));
if(ret == -1){
perror("write");
exit(1);
}
//使用F_GETFL命令得到文件状态标志
int flag = fcntl(fd,F_GETFL,0);
if(flag == -1){
perror("fcntl get");
exit(1);
}
//将文件状态标准添加“追加些”选项,追加写就是文件指针在文件末尾写
flag |= O_APPEND;
//将文件状态修改为追加写
ret = fcntl(fd,F_SETFL,flag);
if(ret == -1){
perror("fcntl set");
exit(1);
}
//再次输入新内容,该内容会追加到就内容后面
char* buf2 = "modify to append model";
ret = write(fd,buf2,strlen(buf2));
if(ret == -1){
perror("write");
exit(1);
}
//关闭文件
close(fd);
return 0;
}
网友评论