Linux系统编程---文件系统

一、文件存储

一个文件主要由两部分组成,dentry(目录项)inode

inode本质是结构体,存储文件的属性信息,如:权限、类型、大小、时间、用户、盘块位置…

也叫做文件属性管理结构,大多数的inode都存储在磁盘上。

少量常用、近期使用的inode会被缓存到内存中。

所谓的删除文件,就是删除inode,但是数据其实还是在硬盘上,以后会覆盖掉。

 

二、文件操作

1. stat函数:获取文件属性,(从inode结构体中获取)

int stat(const char *path, struct stat *buf);

参数:

        path: 文件路径

        buf:(传出参数) 存放文件属性,inode结构体指针。

返回值:

        成功: 0

        失败: -1 errno

获取文件大小: buf.st_size

获取文件类型: buf.st_mode

获取文件权限: buf.st_mode

符号穿透:stat会。lstat不会。

#include <stdio. h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread .h>
#include <sys/stat.h>int main(int argc,char *argv[])
{struct stat sbuf;int ret = stat(argv[1], &sbuf);if(ret == -1){perror( "stat error" );exit(1);}printf( "file size: %ld\n" , sbuf.st_size);return 0;
}

查看f.c文件大小执行:

./mystat f.c

2. lstat:查看文件类型,用法同stat

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<sys/stat.h>int main(int argc,char *argv[])
{struct stat sbuf;int ret = lstat(argv[1],&sbuf);if (ret == -1){perror("stat error");exit(1);}if (S_ISREG(sbuf.st_mode)){printf("It's a regular file\n");}else if(S_ISDIR(sbuf.st_mode)){printf("It's a dir\n");}else if (S_ISFIFO(sbuf.st_mode)){printf("It's a pipe\n");}else if(S_ISLNK(sbuf.st_mode)){printf("It's a sym link\n");}return 0;
}

stat会拿到符号链接指向那个文件或目录的属性---符号穿透

查看符号链接文件时不想让其穿透则使用lstat函数

首先对test.c创建一个软连接

ln -s test.c test.s/test.soft

查看改文件类型,执行以下代码:

./mystat test.s/test.soft

out:

It's a sym link

如果使用stat函数,则以上输出为:

It's a dir/It's a regular file

3.link和unlink隐式回收

int link(const char *oldpath, const char *newpath);

link函数,可以为已经存在的文件创建目录项(硬链接)

int unlink(const char *pathname);

  • unlink是删除一个文件的目录项dentry,使【硬链接数-1】
  • unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放,要等到所有打开文件的进程关闭该文件,系统才会挑时间将该文件释放掉。

编程实现mv命令的改名操作:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{link(argv[1],argv[2]);unlink(argv[1]);return 0;
}

对tets.c改为tets1.c执行以下命令:

./mymv test.c test1.c

三、目录操作

1. getcwd函数

char *getcwd(char *buf, size_t size);

功能:获取当前进程的工作目录
参数:
        buf :缓冲区,存储当前的工作目录size :缓冲区大小
返回值:
        成功:buf中保存当前进程工作目录位置

        失败:NULL

2. chdir函数

int chdir(cohst char *path);

功能:修改当前进程(应用程序)的路径

参数:
        path:切换的路径

返回值:
        成功:0

        失败: -1

示例:

int main (void)
{int ret = -l;char buf[SIZE] ;//1、获取当前进程的工作目录memset(buf, 0, SIZE);if (NULL =getcwd(buf, SIZE)){perror("getcwd error");return 1;}printf( " buf: %s \n", buf);//2.改变当前进程的工作目录ret = chdir( "/home/deng");if (-1 ==ret){perror ("chdir error");return 1;}//3.获取当前进程的工作目录memset(buf,0,SIZE);if (NULL == getcwd(buf,SIZE)){perror ("getcwd error");return 1;}printf("buf: %s\n" , buf);
}

3. opendir函数

DIR *opendir(const char *name) ;

功能:打开一个目录
参数:
        name:目录名

返回值:
        成功:返回指向该目录结构体指针

        失败:NULL

4. closedir函数

int closedir(DIR *dirp);

功能:关闭目录
参数:
        dirp: opendir返回的指针

返回值:
        成功:0

        失败: -1

示例:

//目录打开和关闭
int main(void)
{DIR *dir = NULL;//1.打开日录dir = opendir("test");if (NULL == dir){perror ("opendir error");return l;}closedir(dir);return 0;
}

5. readdir函数

struct dirent *readdir(DIR *dirp);

功能:读取目录
参数:
        dirp: opendir的返回值

返回值:
        成功:目录结构体指针

        失败:NULL

相关结构体说明:

struct dirent{
        ino_t d_ino;        //此目录进入点的inode
        off_t d_off;        //目录文件开头至此目录进入点的位移
        signed short int d_reclen;        // d_name 的长度,不包含NULL字符
        unsigned char d_type;        // d_type所指的文件类型
        char d_name [256];        //文件名

};

实现 ls -a 代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<dirent.h>int main(int argc,char *argv[])
{DIR * dp;dp = opendir(argv[1]);if (dp == NULL){perror("opendir error");exit(1);}struct dirent *sdp;while((sdp = readdir(dp)) != NULL){printf("%s\t",sdp->d_name);}printf("\n");   closedir(dp);return 0;
}

在当前目录下使用命令,执行:./myls ./ 即展示当前目录下的文件及大小

 四、递归遍历目录

任务需求:使用opendir closedir readdir stat实现一个递归遍历目录的程序

输入一个指定目录,默认为当前目录。递归列出目录中的文件,同时显示文件大小。

思路分析

递归遍历目录:ls-R.c

1. 判断命令行参数,获取用户要查询的目录名。 int argc, char *argv[1]

                argc == 1 --> ./

2. 判断用户指定的是否是目录。 stat S_ISDIR(); --> 封装函数 isFile() { }

3. 读目录: read_dir() {

                   opendir(dir)

                   while (readdir()){

                        普通文件,直接打印

                        目录:

                                拼接目录访问绝对路径。sprintf(path, "%s/%s", dir, d_name)

                                递归调用自己。--> opendir(path) readdir closedir

                     }

                     closedir()

                     }

        read_dir() --> isFile() ---> read_dir()

代码示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<dirent.h>
#include<stdio.h>
#include<sys/stat.h>#define PATH_LEN 256
//dir=/homeitgan/linux  fcn=isfilevoid fetchdir(const char *dir,void (*fcn)(char *))
{char name[PATH_LEN];struct dirent *sdp;DIR *dp;if ((dp = opendir(dir)) == NULL){  //打开目录失败//perror("fetchdir can't open");fprintf(stderr,"fetchdir:can't open %s\n",dir);return;}while ((sdp = readdir(dp)) != NULL){if (strcmp(sdp->d_name,".") == 0  || strcmp(sdp->d_name,"..") == 0){       //防止出现无限递归continue;}if (strlen(dir)+strlen(sdp->d_name)+2>sizeof(name)){fprintf(stderr,"fetchdir:name %s %s too long\n",dir,sdp->d_name);}else{sprintf(name,"%s/%s",dir,sdp->d_name);(*fcn)(name);}}closedir(dp);
}void isfile(char *name)
{struct stat sbuf;if(stat(name,&sbuf) == -1){fprintf(stderr,"isfile:can't access %s\n",name);exit(1);}if ((sbuf.st_mode & S_IFMT) == S_IFDIR){fetchdir(name,isfile);}printf("%8ld %s\n",sbuf.st_size,name);
}int main(int argc,char *argv[])
{if(argc == 1)isfile(".");elsewhile (--argc > 0) //可一次查询多个目录isfile(*++argv);//循环调用该函数处理各个命令行传入的目录return 0;
}

执行 ./ls_R test.c 可查看test.c的文件大小

执行 ./ls_R 可查看当前目录下所有文件的大小

五、文件描述符复制

1. dup和dup2

用来做重定向,本质就是复制文件描述符

重定向:

cat myls.c > out        将myls.c的东西读出再写到out文件中

cat myls.c >> out      将myls.c的东西读出再追加到out文件中

dup()和dup2()用来做重定向,本质就是复制文件描述符,使新的文件描述符也标识旧的文件描述符所标识的文件;

这个过程类似于现实生活中的配钥匙,一把钥匙对应一把锁,然后我们又去配了一把新钥匙,此时两把钥匙都可以打开锁,而dup()和dup2()也一样,原来的文件描述符和新复制出来的文件描述符都指向同一个文件,我们操作这两个文件描述符的任何一个 都能操作它所对应的文件

dup函数

int dup(int oldfd);

功能:
        通过oldfd复制出一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符,最终oldfd和新的文件描述符都指向同一个文件。
参数:
        oldfd :需要复制的文件描述符oldfd

返回值:
        成功:新文件描述符

        失败:-1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc,char *argv[])
{int fd = open("./out",O_RDONLY);//012 ---3int newfd = dup(fd);//4printf("newfd = %d\n",newfd);return 0;
}

make之后执行输出为:newfd = 4

dup2函数:

int dup2(int oldfd,int newfd);

功能:
        通过oldfd 复制出一个新的文件描述符newfd,如果成功,newfd和函数返回值是同一个返回值,最终oldfd和新的文件描述符newfd都指向同一个文件。
参数:
        oldfd :需要复制的文件描述符
        newfd :新的文件描述符,这个描述符可以人为指定一个合法数字(0 - 1023),如果指定的数字已经被占用(和某个文件有关联),此函数会自动关闭close()断开这个数字和某个文件的关联,再来使用这个合法数字。
返回值:
        成功:返回newfd

        失败:返回-1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc,char *argv[])
{int fd1 = open(argv[1],O_RDWR);//0123---3int fd2 = open(argv[2],O_RDWR);//0123---4int fdret = dup2(fd1,fd2);//返回新文件描述符fd2printf("fdret = %d\n",fdret);//写入fd1指向的文件int ret = write(fd2,"1234567",7);//将屏幕输入重定向给fd1所指向的文件printf("ret = %d\n",ret);dup2(fd1,STDOUT_FILENO);printf("-------------------886\n");return 0;
}

首先新建2个空白文件out和out1

make之后执行以下命令:

./dup2 out out1

首先1234567会写入out文件中,其次再将-------------------886写入out文件

int fdret = dup2(fd1,fd2);        其中fd1 = 3,fd2 = 4

把3拷贝给4,即把4指向3(out文件)

dup2(fd1,STDOUT_FILENO);        STDOUT_FILENO = 1

把3拷贝给1,即把1指向3(out文件)

此时指向out的文件描述符有三个

2. fcntl函数

  • fcntl用来改变一个【已经打开】的文件的 访问控制属性
  • 重点掌握两个参数的使用, F_GETFL,F_SETFL

int (int fd, int cmd, ...)

参数:

        fd 文件描述符

        cmd 命令,决定了后续参数个数

        获取文件状态: F_GETFL

        设置文件状态: F_SETFL

返回值:

        int flgs = fcntl(fd, F_GETFL);

        flgs |= O_NONBLOCK

        fcntl(fd, F_SETFL, flgs);

终端文件默认是阻塞读的,这里用fcntl将其更改为非阻塞读:

#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  #define MSG_TRY "try again\n"  int main(void)  
{  char buf[10];  int flags, n;  flags = fcntl(STDIN_FILENO, F_GETFL); //获取stdin属性信息  if(flags == -1){  perror("fcntl error");  exit(1);  }  flags |= O_NONBLOCK;  int ret = fcntl(STDIN_FILENO, F_SETFL, flags);  if(ret == -1){  perror("fcntl error");  exit(1);  }  tryagain:  n = read(STDIN_FILENO, buf, 10);  if(n < 0){  if(errno != EAGAIN){          perror("read /dev/tty");  exit(1);  }  sleep(3);  write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));  goto tryagain;  }  write(STDOUT_FILENO, buf, n);  return 0;  
}  

 fcntl实现dup描述符:

int fcntl(int fd, int cmd, ....);

cmd: F_DUPFD

参3:         

        被占用的,返回最小可用的。

        未被占用的, 返回=该值的文件描述符。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<pthread.h>int main(int argc, char *argv[])
{int fd1 = open(argv[1],O_RDWR);printf("fd1 = %d\n",fd1);int newfd = fcntl(fd1,F_DUPFD,0);//0被占用,fcntl使用文件描述符表中可用的最小文件描述符返回printf("newfd = %d\n",newfd);int newfd2 = fcntl(fd1,F_DUPFD,7);//7未被占用,返回>=7的文件描述符printf("newfd2 = %d\n",newfd2);int ret = write(newfd2,"YYYYYYYYYYYYY",7);//只能写入7个printf("ret = %d\n",ret);return 0;
}

 make之后执行:

./fcntl_dup out

out:

fd1 = 3
newfd = 4
newfd2 = 7
ret = 7

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

XWX-SX三箱社交箱

简单介绍&#xff1a; 动物行为学是一门研究动物行为的科学&#xff0c;它包括观察动物在自然环境中的行为&#xff0c;以及在控制环境中的实验行为。三箱社交实验是其中一种常见的实验方法&#xff0c;用于评估动物的社交行为和决策制定能力。这种实验在许多领域都有应用&…

bugku-web-需要管理员

页面源码 <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <title>404 Not Found</title> </head> <body> <div idmain><i> <h2>Something error:</h2…

QT、ffmpeg视频监控分屏

1、支持分屏&#xff08;4&#xff0c;6&#xff0c;8&#xff0c;9&#xff0c;13&#xff0c;16&#xff0c;25&#xff0c;32&#xff0c;64&#xff09;切换 2、支持拖拽效果 3、支持播放mp4&#xff0c;rtmp等 4、本人亲测支持播放32路&#xff0c;64路没做测试 5、支持读…

【C++成长记】C++入门 | 类和对象(中) |类的6个默认成员函数、构造函数、析构函数

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、类的6个默认成员函数 二、构造函数 1、概念 2、特性 三、析构函数 1、概念 2、特性 一、…

R语言计算:t分布及t检验

t分布理论基础 t分布也称Student’s t-distribution&#xff0c;主要出现在小样本统计推断中&#xff0c;特别是当样本量较小且总体标准差未知时&#xff0c;用于估计正态分布的均值。其定义基于正态分布和 X 2 X^{2} X2分布&#xff08;卡方分布&#xff09;。如果随机变量X服…

springCloudAlibaba集成seata实战(分布式事物详解)

一、分布式事务 1. 事务介绍 1.1 基础概念 事务&#xff1a;保证我们多个数据库操作的原子性&#xff0c;多个操作要么都成功要么都不成功 事务ACID原则 A&#xff08;Atomic&#xff09;原子性&#xff1a;构成事务的所有操作&#xff0c;要么都执行完成&#xff0c;要么全部…

三次握手与四次挥手到底是怎么回事?

三次握手和四次挥手是TCP/IP协议中建立和断开连接的关键步骤&#xff0c;它们是保证可靠通信的重要机制。这里将探讨这两个概念&#xff0c;并解释它们背后的原理。 三次握手 三次握手用于建立TCP连接&#xff0c;它由客户端和服务器之间发送的三个报文组成&#xff1a; 第一次…

市场份额第一:SmartX 领跑 23全年中国超融合软件市场

日前&#xff0c;IDC 发布《中国软件定义存储&#xff08;SDS&#xff09;及超融合存储系统&#xff08;HCI&#xff09;市场季度跟踪报告&#xff0c;2023 年第四季度》&#xff0c;详解中国区超融合发展趋势、市场份额规模以及厂商占比。 IDC 数据显示&#xff0c;2023 年全…

rust使用print控制台打印输出五颜六色的彩色红色字体

想要在控制台打印输出彩色的字体&#xff0c;可以使用一些已经封装好的依赖库&#xff0c;比如ansi_term这个依赖库&#xff0c;官方依赖库地址&#xff1a;https://crates.io/crates/ansi_term 安装依赖&#xff1a; cargo add ansi_term 或者在Cargo.toml文件中加入&#…

带洞平面三角分割结果的逆向算法

先标不重复点&#xff0c;按最近逐个插入。 只说原理。 不带洞的 1 2 4 2 3 4 两个三角形 结果 1 2 3 4 无重复 无洞 1 2 6 1 2 3 6 1 2 3 7 6 1 2 3 4 7 6 1 2 3 4 5 7 6 1 2 3 4 1 5 7 6 1 2 3 4 1 6 5 7 6 最终结果 1 2 3 4 1 6 5 7 6 按重复分割 1 2 3…

自定义滚动条样式:前端实现跨浏览器兼容

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【mac】【python】新建项目虚拟环境后,使用命令pip出现错误:zsh: command not found: pip

【mac】【python】新建项目虚拟环境后&#xff0c;使用命令pip出现错误&#xff1a;zsh: command not found: pip 问题描述&#xff1a; 拉取或者创建新的python项目时&#xff0c;为项目添加了新的解释器&#xff0c;创建啦虚拟环境&#xff0c;但是执行pip命令的时候找不到命…

LeetCode 面试经典150题 202.快乐数

题目&#xff1a; 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结…

《前端面试题》- JS基础 - 伪数组

第一次听说伪数组这个概念&#xff0c;听到的时候还以为是说CSS的伪类呢&#xff0c;网上一查&#xff0c;这东西原来还是个很常见的家伙。 何为伪数组 伪数组有两个特点&#xff1a; 具有length属性&#xff0c;其他属性&#xff08;索引&#xff09;为非负整数但是却不具备…

C ++ 和 C语言的优缺点分别是什么?

C语言&#xff0c;它简直就是编程世界的一块磐石。简洁、直接&#xff0c;让人一眼就能明白它想干嘛。它的运行速度快&#xff0c;接近硬件操作&#xff0c;特别适合那些需要直接与硬件打交道的场景。但就是因为这种接近硬件的特性&#xff0c;C语言在抽象层次上就显得有点捉襟…

【MCU开发规范】:MCU的性能测试

MCU的性能测试 前序性能评判方法MIPSCoreMark EEMBC其他参考 前序 我们平时做MCU开发时&#xff0c;前期硬件选型&#xff08;选那颗MCU&#xff09;基本由硬件工程师和架构决定&#xff0c;到软件开发时只是被动的开发一些具体功能&#xff0c;因此很少参与MCU的选型。 大部分…

linux(ub)-redis环境部署

1.下载redis包 wget http://download.redis.io/releases/redis-7.0.5.tar.gz 2.解压缩&#xff1a; tar -zxvf redis-7.0.5.tar.gz 3.安装gcc&#xff1a;sudo apt-get install gcc 4. 编译&#xff1a;cd redis-7.0.5 make make make install 5. cd /usr/local/bin/ 6. mkdir …

SSL证书和IP证书的区别

SSL证书是一种用于保障网络传输安全的数字证书&#xff0c;它通过为网站提供加密服务&#xff0c;确保用户与网站之间交换的数据不被第三方截取或篡改。IP证书是一种用于验证和保护IP地址的数字证书。它是通过将IP地址与其所有者的身份信息相联系&#xff0c;从而确保IP地址的真…

哪种裤子比较百搭?显高显瘦的男生裤子分享

选到合适的裤子才能穿得好看以及舒服。可是市面上也出现了不少各种裤子质量达不到标准的负面新闻&#xff0c;为了能够选到合适的裤子&#xff0c;我自费购买了多个品牌的裤子测评。之后我知道很多网红品牌为了压低成本&#xff0c;用料和做工都很差&#xff0c;于是我总结了五…

Springboot框架入门介绍——1.快速搭建启动程序框架

如果使用过spring可能会为繁琐复杂的配置项感到头疼&#xff0c;而springboot内嵌了tomcat和jetty容器&#xff0c;简化了maven配置&#xff0c;基于注解的0配置思想&#xff0c;同时可以和各种其他框架无缝整合&#xff0c;实现快速开发spring应用框架。 这里需要记住一句话&a…