3.Linux文件系统

文件系统:磁盘、分区的组织形式
cd /在这里插入图片描述
/home:用户目录,存放普通用户相关文件
/proc:进程,此目录的数据在内存中,包含进程等信息,如系统核心,外部设备,网络状态等
/mnt: Mount,用于挂载各种文件系统,如: U盘、SD卡等
/tmp: Temp,存放临时文件。一般用户或正在执行的程序临时存放文件的目录,任何人都可以访问,不能存放重要的数据
/etc: 系统所有的配置文件都在该目录下
/usr: User,应用程序存放目录
/lib: Library,存放系统的函数库
/bin:Binary,存放常用的系统命令。
/sbin: System Binary,存放系统管理员 root 使用的命令
来源
cd /dev:设备相关信息
在这里插入图片描述
lp:打印池,一个打印池可以对应一个打印机
磁盘设备:sda、sda1、sda2,磁盘插入后就会出现
stderr:标准错误设备,文件标识符为2
stdin:标准输入设备,文件标识符为0
stdout:标准输出设备,文件标识符为1

01.Linux文件系统

磁盘(中分区)组织文件的方法

001.创建文件系统

  • 识别硬盘
  1. 双击虚拟机参数中的硬盘→添加→…→开机
    在这里插入图片描述
  2. 硬盘位置:
    /dev
    硬盘命名规则:
    – hd(IDE),比如hda,hdb;
    – sd(SCSI),比如sda,sdb,sdc
    在这里插入图片描述
  3. 查看磁盘基本信息:fdisk -l
    在这里插入图片描述
    分区命名规则:
    – sda1:主分区,引导分区
    – sda2:扩展分区
    – sda5~:逻辑分区
  • 给硬盘分区:fdisk <磁盘标志符>
    fdisk /dev/sdb
    在这里插入图片描述
    – 输入m获得帮助
    在这里插入图片描述
    在这里插入图片描述
    – 输入l获得分区类型介绍
    在这里插入图片描述
    – 输入n添加新分区:
    在这里插入图片描述
    可以看到当前硬盘MBR主分区表空闲
    – 选p,一路回车,使整块硬盘为一个主分区
    在这里插入图片描述
    – 写入更改并退出
    在这里插入图片描述
    – 查看/dev
    在这里插入图片描述
  • 给分区格式化,创建文件系统:mkfs(make file system)
    mkfs -t <文件系统类型> <分区名称>
    Linux常见分区格式:
    – ext2 支持反删除
    – ext3 开始支持大文件(32768G,一个块8K才支持),支持日志(log)文件(有挂载开机自检)
    – ext4 ext3的改进,没有日志文件了。不支持反删除
     
    mkfs -t ext4 /dev/sdb1 对第二块硬盘的第一个分区创建文件系统
    在这里插入图片描述
  • 绑定(挂载)目录
    Linux中的根目录以外的文件要想被访问,需要将其“关联”到根目录下的某个目录来实现,这种关联操作就是“挂载”,这个目录就是“挂载点”,解除次关联关系的过程称之为“卸载”
    – mkdir /disk_2
    在这里插入图片描述
    mount /dev/sdb1 /disk_2
    – cd /disk_2、ls -al
    在这里插入图片描述
    – fdisk -l
    在这里插入图片描述
    – 查看磁盘基本信息df -l
    在这里插入图片描述
    可以看到每一个分区对应一个挂载点,分区表占了一点空间
    – 卸载(解挂载):umount <挂载点>,然后目录和磁盘就解除关系了

002.文件与目录操作

  • 权限位10位
    ls -l | more
    在这里插入图片描述
  1. 第一位:描述文件类型
    - 普通文件
    c char,字符文件(串行)
    b block,块文件(并行)
    l link,链接文件,一种特殊的快捷方式
    p pipe,管道文件
    s socket,套接字文件
    d directory,文件夹(目录)
  2. 第二~十位
    2~4:属主(owner)权限rwx
    5~7:属组(group)权限
    8~10:非同组用户(other)权限
    关于用户权限:
    – 可执行权限全部去掉,属主和root用户都无法执行
    在这里插入图片描述
    – 读写权限全部去掉,文件属主无权读写,root用户依然可以
    在这里插入图片描述
  • 用户权限设定(由文件属主或root用户设定)
    chmod <权限取值> <文件>
    按权限取值分类:
    • 3位8进制数,直接设置权限,每位对应一类用户权限,用1表示有权限,0表示无权限
      比如chmod 755 abc表示abc权限位为rwx r-x r-x
    • u/g/o +/- r/w/x ,增加减少权限,u:user;g:group;o:other
      在这里插入图片描述
      可以看到不可执行的文件被授予可执行权限后也被标记为绿色,但依然不可执行
    • 文件的链接
      • 硬链接:ln <原文件> <链接文件>,本质上是原文件的复制,原文件不存在硬链接文件依然有效在这里插入图片描述
      • 软连接:ln -s <原文件> <链接文件>,本质上就是快捷方式,原文件不在原位置后链接文件会报错,除非恢复原文件或重新创建快捷方式。作用就是可以把链接文件放到文件系统中的任意位置在这里插入图片描述

02.文件操作相关命令

  • 路径
    • 绝对路径:从根到文件名称 /…/…/…/filename
    • 相对路径:相对于当前路径的路径
      • ./ 当前路径 ./open open.c
      • . ./ 当前父路径 
        在这里插入图片描述
        其中的cp ./file/open open1就是在/home/sonya/code目录拷贝子目录file下的open文件到当前目录,为open1
    • $HOME 相当于Windows下的PATH路径参数,是当前用户家目录的默认值,即/home/txp在这里插入图片描述
  • 查看文件
    ls -l 以长格式显示 -a显示隐藏文件
    在这里插入图片描述
    分别是①文件属性、②节点(子目录)个数(如果一个文件不是目录,此时这一字段表示这个文件所具有的硬链接数)、③文件(目录)拥有者、④文件(目录)拥有者所在的组、⑤文件所占用的空间(以字节为单位)、⑥文件(目录)最近访问(修改)时间、⑦文件名
    ls -la
    在这里插入图片描述
    vim .bash_history
    在这里插入图片描述
    可以看到记录的是从系统登录以来以root身份执行的命令,用history命令可以查看包括普通用户的命令
    vim .bash_profile
    在这里插入图片描述
    vim .bashrc
    在这里插入图片描述
    指定了系统执行命令默认的查找路径
  • 路径相关
    cd
    mkdir
    rmdir:删除目录(目录必须为空)
    rm:删除文件
    rm -r:删除一个非空的目录,rm -r /是最危险的命令
  • 环境变量
    PATH:指定一些默认目录,其下的命令可以直接执行,不用指定路径,系统会自动去这些目录找
    显示环境变量echo $PATH
    在这里插入图片描述
    which:查看命令位置
    在这里插入图片描述
  • 文件操作
    cp,mv,rm(不支持反删除)
    通配符:*指一切内容,?是单一符号的替换
  • 可执行文件
    Linux没有像Windows那样的文件类型,后缀只是方便识别,能否执行要看权限
  • 查看文本文件
    • 用软件查看,vi编辑器、gedit等
    • 用cat,head,tail等命令查看
    1. 创建文本文件
      echo “abcabcabc”>test.log 覆盖写,>为定向符
      在这里插入图片描述
      echo “xyzxyzxyz”>>test.log 追加写
    2. 查看
      1. cat
        cat /var/log/messages
        在这里插入图片描述
        cat -n messages
        在这里插入图片描述
        cat messages | more分页显示
      2. head messages
      3. tail messages
    • 掩码umask
      – root用户为022,普通用户为002
      – 可执行文件最高权限位默认为777,文本文件为666
      在这里插入图片描述
  • 查找文件
    – which:求文件的绝对路径(在$PATH路径范围内查找)
    – whereis:和which相比多了通配符查找
    在这里插入图片描述
    – find:find <查找位置> -name <文件名> 能指定查找位置
    在这里插入图片描述

03.相关程序设计

  1. 文件读写
    系统函数:open/creat,read,write,close

    • open/creat函数:返回文件句柄(用来读写、关闭)
      – man open
      在这里插入图片描述
      flags如果只是读写、追加的形式,不需要第三个参数,如果是创立的形式,则要第三个参数;mode_t是创立的文件的权限说明。open三参数和creat函数效果等同
      flags(打开模式):
      — O_RDONLY只读
      — O_WRONLY只写
      — O_RDWR可读写
      — O_APPEND追加写
      — O_CREAT创建
      — O_TRUNC截断(以前的内容全部放弃)
      mode_t(权限模式):S_I<权限><对象> S_I是引导词,其后占4位;或者用3个八进制数的权限位代替
      — 权限:R,W,X,RWX
      — 对象:USR,GRP,OTH,U,G,O
      — 比如说在这里插入图片描述
      在这里插入图片描述
      0是八进制标志,10是权限引导
    • close函数
      close(句柄)
      – 代码
      #include <unistd.h>	//Linux标准头文件
      #include <stdio.h>	//标准输入输出
      #include <stdlib.h>	//标准库文件
      #include <fcntl.h>	//文件控制头文件
      #include <errno.h>	//出错处理头文件
      #include <string.h>	//将错误信息转换为字符串输出
      #include <sys/types.h>	//在此头文件中定义了一种转换,将int等常见类型转换为有字面含义的类型,这种类型方便理解
      
      int bail(const char*str)
      {
      	perror(str);	//程序出错时打印用户提供的错误说明,一定要有
      	printf("%s\n",strerror(errno));	//打印错误代码 当系统出错时会自动生成整数errno错误说明,strerror函数返回错误代码
      	exit(-1);
      }
      int main(int argc,char** argv)	//可以有0~3个参数。argc记录当前命令的参数个数,argv是命令的字符串内容(char*表示不定长字符串,char**表示不定长字符串数组,比如说cp abc.txt test.log命令,argv[1]=abc.txt)
      {
      	int fd=-1;	//文件句柄
      	fd=open(argv[1],O_RDONLY);	//系统函数int open(const char *pathname, int flags);以只读模式打开文件
      	if(fd<0)
      		bail("Open file error.\n");
      	printf("fd=%d\n",fd);
      	close(fd);
      	return 0;
      }
      
    1. read函数
      在C中,有fgets函数,文件读取结束标志EOF;而read函数只有读操作,先读到缓冲区中,在对缓冲区中的内容进行操作、分析,行结束判断只能靠用户编码实现
      – man 2 read(read函数说明,而不是命令)
      在这里插入图片描述
      int read(int fd,void* buf,int len);
      返回值:

      • -1:读取失败
      • 0:没有读取到数据
      • n(1~len):读取到的字节数,若恰好为len,不一定读完;若在0到len-1之间,则读完了

      – 代码:

      #define LEN 512		//定义一次要读取的字节数
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	int fd=-1;
      	char buf[LEN]={0};	//定义一个缓冲区,保存读取的内容
      	int rdret=-1;		//read的返回值
      
      	fd=open(argv[1],O_RDONLY);	
      	if(fd<0)
      		bail("Open file error.\n");
      	printf("fd=%d\n",fd);
      
      	rdret=read(fd,buf,sizeof(buf));
      	while(rdret>0)
      	{
      		printf("%s",buf);
      
      		memset(buf,0,sizeof(buf));	//读到最后有可能少于LEN,缓冲区覆盖不全,出现乱码。所以字符数组用了就清空
      
      		rdret=read(fd,buf,sizeof(buf));
      	}
      	if(rdret<0)
      	{
      		close(fd);	//打开的句柄不关掉不能退出的
      		bail("read file error.");
      	}
      	close(fd);	//rdret=0直接退出
      	return 0;
      }
      
    2. write函数
      将缓冲区的内容写到句柄中
      – man 2 write
      在这里插入图片描述
      ssize_t write(int fd, const void *buf, size_t count);
      — const:防止缓冲区被用户操作改变,增加程序的安全性
      — size_t:期望写入的字节(ascii)个数
      — ssize_t:实际写入的长度,0表示没有写,-1表示写入失败,0~count表示实际写入的字符个数。有没有写完不是靠返回值,而是看用户是否给输入了,read读完了就结束了
      – 代码:将从键盘中键入的内容写入文件中,键入完毕文件也自动关闭

      #define LEN 512		//定义一次要读取的字节数
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	int fd=-1;
      	char buf[LEN]={0};	//定义一个缓冲区,保存读取的内容
      	int wtret=-1;		//read的返回值
      
      	fd=open(argv[1],O_WRONLY | O_APPEND);	//句柄为附加写,针对已经存在的文件
      	//针对要写入的文件不存在
      	//fd=open(argv[1],O_CREAT | O_RDWR,S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
      
      	if(fd<0)
      		bail("Open file error.\n");
      	printf("fd=%d\n",fd);
      
      	printf(">");	//表示这是一个输入
      
      	gets(buf);	//从键盘得到一个字符串输入到缓冲区
      	while(strcmp(buf,"quit")!=0)	//判断输入是否结束
      	{
      		wtret=write(fd,buf,strlen(buf));	//输入都以字符接收的,所以用strlen计算长度
      		if(wtret<0)
      		{
      			close(fd);
      			bail("Write file error.\n");
      		}
      		//往下为下一次循环作准备
      		memset(buf,0,sizeof(buf));	//字符串操作前缓冲区要清空
      		printf(">");
      		gets(buf);
      	}
      	close(fd);	//rdret=0直接退出
      	return 0;
      }
      

      – 代码:自己写个copy命令
      — 打开源文件
      — 创建目标文件
      — 读取源文件
      — 写入目标文件
      — 关闭句柄,退出

      #define LEN 512
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	int rdfd=-1;
      	int wtfd=-1;
      	char buf[LEN]={0};
      	int rdret=-1;	
      	int wtret=-1;	
      
      	//打开源文件
      	rdfd=open(argv[1],O_RDONLY);
      	if(rdfd<0)
      		bail("Open file error.\n");
      	//创建目标文件
      	wtfd=open(argv[2],O_CREAT | O_WRONLY,010755);//有可执行权限,一般以二进制形式处理
      	if(wtfd<0)
      	{
      		close(rdfd);
      		bail("Create file error.");
      	}
      	//rdfd、wdfd都有了
      	//读取源文件
      	rdret=read(rdfd,buf,sizeof(buf));//从读句柄中读
      	while(rdret>0)
      	{
      		//写入目标文件
      		wtret=write(wtfd,buf,rdret);//将缓冲区中的实际内容写入句柄,而不是以缓冲区的大小或strlen(以/0结束计算,有多个字符串只计算第一个)
      		if(wtret<rdret)
      		{
      			close(rdfd);
      			close(wtfd);
      			bail("Write file error.");
      		}
      		memset(buf,0,sizeof(buf));
      		rdret=read(rdfd,buf,sizeof(buf));
      	}
      	if(rdret<0)
      	{
      		close(rdfd);
      		close(wtfd);
      		bail("read file error.");
      	}
      	//rdret=0,关闭句柄,退出
      	close(rdfd);
      	close(wtfd);
      	return 0;
      }
      

      ./copy open myopen
      在这里插入图片描述
      问题:权限不一样,要读取权限;读文件不存在、写文件存在、读文件没有读权限等

  2. 文件控制

    • access函数:访问函数,看文件是否存在、获得文件的读写权限
      man 2 access
      在这里插入图片描述
      int access(const char *pathname, int mode);
      – 返回值:0为测试成功,-1为失败
      – pathname:文件名
      – mode:测试权限,多个条件都要满足用 | 符号连接
      — R_OK:读可以
      — W_OK:写可以
      — X_OK:执行可以
      — F_OK:文件存在
      vimx access.c

      #define LEN 512		//定义一次要读取的字节数
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	int ret=-1;
      	ret=access(argv[1],F_OK);//文件名由参数argv[1]决定
      	if(ret<0)
      		printf("File not exist.\n");
      	else
      		printf("File exist.\n");
      	return 0;
      }
      

      读:

      ret=access(argv[1],R_OK);
      if(ret==0)
      	fd=open();
      

      写:

      ret=access(argv[1],F_OK);
      if(ret<0)
      	fd=creat();
      else
      	printf("overwrite?");
      
    • stat函数:读取权限位
      man 2 stat
      在这里插入图片描述
      – sys/types.h中类型的宏定义转换,比如uid_t转为int
      – sys/stat.h中定义了stat结构体
      – int stat(const char *pathname, struct stat *statbuf);
      将文件名为pathname的文件的状态读入结构体中。引用结构体用的是指针,在调用时要用取地址符&
      在这里插入图片描述
      struct stat st;
      ret=stat(文件名,&st);
      返回值:-1,失败
      st返回值:
      – st.st_size 文件大小
      – st.st_mode 文件类型、权限
      – st.st_?time 时间相关参数(秒为单位,用ctime转换)
      – st.mode:

      1. st.mode&S_IFMT
      2. 宏函数
        S_ISDIR
        S_ISREG
        S_ISLNK
        S_IFIFO
        S_ISCHR
        S_ISOCK

      代码:复制源文件权限到目标文件

      #define LEN 512		//定义一次要读取的字节数
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/stat.h>//stat头文件
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	int ret=-1;
      	int sfd=-1;
      	int dfd=-1;
      	struct stat bs;
      
      	memset(&bs,0,sizeof(bs));
      
      	ret=stat(argv[1],&bs);//读取源文件状态
      	dfd=creat(argv[2],bs.st_mode);//以源文件权限位创建目标文件
      
      	close(dfd);//啥都不做
      	return 0;
      }
      
  3. 目录操作
    dir,opendir,closedir,readdir
    步骤:打开目录、读取目录信息、关闭目录

    1. 打开目录(要头文件dirent.h)
      打开目录返回的句柄不是一个整数,而是一个指针:
      DIR* opendir(目录名称)
      – 成功:返回地址
      – 失败:返回NULL

    2. 关闭目录
      closedir(DIR* 目录指针)
      – 成功:0
      – 失败:-1

    3. 读取目录信息
      怎么读取信息、信息放到哪去?
      struct dirent* readdir(DIR* 指针);
      – 成功:返回结构体指针(每次读完的信息返回到这个结构体)
      – 失败:NULL
      – 方式:循环自动读取
      man 3 readdir(3是传统的)
      在这里插入图片描述在这里插入图片描述
      主要就是为了得到那两个分量在这里插入图片描述

      #define LEN 512		//定义一次要读取的字节数
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <dirent.h>//与目录操作有关的头文件
      #include <sys/stat.h>
      #include <sys/types.h>
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	DIR* dp=NULL;//定义一个目录指针
      	struct dirent* dir=NULL;//定义一个结构体指针,指向读取的内容
      
      	dp=opendir(argv[1]);
      	if(dp==NULL)
      		bail("open dir error.");
      	//readdir(dp);//不重复读,读取的是目录的第一个文件
      	while((dir=readdir(dp))!=NULL)
      	{
      		if(dir->d_name[0]=='.')//如果文件名以.引导,不可见;.为当前目录,..为父目录,这两个在目录中一定存在
      			continue;
      		if(dir->d_type==DT_REG)
      			//各种目录操作的位置
      			printf("Regular file:%s\n",dir->d_name);
      		if(dir->d_type==DT_DIR)
      			printf("Dir file:%s\n",dir->d_name);
      	}
      	closedir(dp);
      	return 0;
      }
      

      ./readdir .
      ./readdir $HOME

    4. 创建目录函数
      man 2 mkdir
      在这里插入图片描述
      #include <sys/stat.h>:权限参数可以从<stat.h>中的结构体得到
      int mkdir(const char *pathname, mode_t mode);
      – 返回值:成功:0,失败:-1

    5. 切换目录函数
      chdir(新目录名称)
      – 代码:给定源目录,遍历目录,创建一个新目录,将源目录中子项添加到新目录中去
      — 假定:仅复制一级目录,不进入子目录,即只创建,不复制
      — cpdir(source,dest):将源目录的子项复制到目标目录中去

      1. 可行性:source要存在、可读,dest不存在,可写;
      2. 步骤:
        – 读source状态,作为dest的状态创建目标目录
        – source:readdir;chdir(dest)
        – dest:mkdir/creat;chdir(source)
        – 关闭目录
      #define LEN 512
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>	
      #include <string.h>
      #include <sys/types.h>
      #include <dirent.h>//目录头文件
      #include <sys/stat.h>//文件状态
      
      int bail(const char*str)
      {
      	perror(str);
      	printf("%s\n",strerror(errno));	
      	exit(-1);
      }
      int main(int argc,char** argv)	
      {
      	char sdir[1024]={0};//源目录路径
      	char ddir[1024]={0};//目标目录路径
      	char sname[1024]={0};//源文件名
      	char dname[1024]={0};//目标文件名
      	int ret=-1;
      	struct dirent* dir=NULL;//读取的目录中每一条(目录子项)的信息,用readdir返回
      	DIR* dp=NULL;//指针指向目录的指针,用opendir返回
      	struct stat sb;//存放源目录的权限状态
      	int fd=-1;//creat返回的句柄
      
      	if(access(argv[1],F_OK)<0)//第一件事看源目录是否存在
      		bail("Source directory not exist.");
      	if(access(argv[1],R_OK)<0)
      		bail("Perssion denied.");
      	if(access(argv[2],F_OK)==0)
      		bail("Dest directory exist.");
      
      	getcwd(sdir,sizeof(sdir));//获取源目录(当前路径)绝对路径,get current workspace directory
      	memset(&sb,0,sizeof(sb));
      	stat(argv[1],&sb);//获得源目录的权限
      	if(mkdir(argv[2],sb.st_mode)<0)//创建目标路径
      		bail("Creat dest directory error.");
      	chdir(argv[2]);
      	getcwd(ddir,sizeof(ddir));//为什么不直接是argv[2]?因为那可能是相对路径
      	chdir(sdir);
      	dp=opendir(argv[1]);
      	while((dir=readdir(dp))!=NULL)
      	{
      		if(dir->d_name[0]=='.')
      			continue;
      		else if(dir->d_type==DT_DIR)
      		{
      			memset(&sb,0,sizeof(sb));
      			stat(dir->d_name,&sb);//根据源文件的权限模式创建目标文件
      			chdir(ddir);
      			mkdir(dir->d_name,sb.st_mode);
      			chdir(sdir);
      			continue;
      		}
      		else//目录用mkdir,其他用creat
      		{
      			memset(&sb,0,sizeof(sb));
      			stat(dir->d_name,&sb);
      			chdir(ddir);
      			fd=creat(dir->d_name,sb.st_mode);
      			close(fd);
      			chdir(sdir);
      			continue;
      		}
      	}
      	closedir(dp);
      
      }
      

      效果:在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值