Linux_文件系统类型

文件在内核中的数据结构

一个打开的文件在内核中使用三种数据结构(结构体)表示

  • 1.文件描述符表
    • 文件描述符标志
    • 文件表项指针
  • 2.文件表项
    • 文件状态标志
      • 读、写、追加、同步和非阻塞等状态标志
    • 当前文件偏移量
    • i节点表项指针
    • 引用计数器
  • 3.i节点
    • 文件类型和对该文件的操作函数指针
    • 当前文件长度
    • 文件所有者
    • 文件所在的设备、文件访问权限
    • 指向文件数据在磁盘块上所在位置的指针等。
      在这里插入图片描述

文件系统

  • 普通文件:
    • 在程序中使用带参宏S_ISREG()表示
    • 指令:
  • 目录文件:
    • 在程序中使用带参宏S_ISDIR()表示
    • 指令:
  • 链接文件:
    • 在程序中使用带参宏S_ISLNK()表示
    • 指令:
    • 指令:
	硬链接包含2个部分:普通文件和链接部分
	软链接包含1个部分:链接部分
	当源文件消失后,硬链接还存在普通文件部分,而软链接完全失效
	硬链接可以看成是带链接功能的复制粘贴,软链接为快捷方式
程序中创建链接
- 创建硬链接
	- link(源文件名, 链接文件名)
- 创建软链接
	- symlink(源文件名, 链接文件名)
  • 管道文件

    • 在程序中使用带参宏S_ISFIFO()表示

    • 作用:在两个进程间进行数据交互

    • 分类:

      • 匿名管道:由于在硬盘中不存在文件,只在程序中存在(指没有路径及文件名只有文件描述符)。匿名管道只允许在拥有亲缘关系(父子关系)的进程中使用。
      • 命名管道:存在于硬盘中,拥有绝对路径及文件名,其可以在两个任意的进程中使用。
    • 管道自带同步机制(按照严格的既定顺序运行程序)。
      场景:
      有2个进程,1为输入内容,2为读取内容
      首先1以写端打开管道,2为读打开管道
      此时如果1先打开open,则open会阻塞。直到2号打开才会解除阻塞
      若2先打开管道,不会堵塞,但read会阻塞,等1write完成解除

    一根管道是单向的,只能是一端读一端写

    • 创建:
      • popen()
      • pipe()
    • 指令:
      mkfifo + name
EXAMPLE
       The following program creates a pipe, and then fork(2)s to create a child process; the child inherits  a  duplicate
       set  of  file descriptors that refer to the same pipe.  After the fork(2), each process closes the file descriptors
       that it doesn't need for the pipe (see pipe(7)).  The parent then writes the string contained in the program's com‐
       mand-line  argument  to  the  pipe, and the child reads this string a byte at a time from the pipe and echoes it on
       standard output.

   Program source
       #include <sys/types.h>
       #include <sys/wait.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <string.h>

       int
       main(int argc, char *argv[])
       {
           int pipefd[2];
           pid_t cpid;
           char buf;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <string>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           if (pipe(pipefd) == -1) {
               perror("pipe");
               exit(EXIT_FAILURE);
           }

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {    /* Child reads from pipe */
               close(pipefd[1]);          /* Close unused write end */

               while (read(pipefd[0], &buf, 1) > 0)
                   write(STDOUT_FILENO, &buf, 1);

               write(STDOUT_FILENO, "\n", 1);
               close(pipefd[0]);
               _exit(EXIT_SUCCESS);

           } else {            /* Parent writes argv[1] to pipe */
               close(pipefd[0]);          /* Close unused read end */
               write(pipefd[1], argv[1], strlen(argv[1]));
               close(pipefd[1]);          /* Reader will see EOF */
               wait(NULL);                /* Wait for child */
               exit(EXIT_SUCCESS);
           }
       }

  • 块特殊文件:

    • 程序使用 S_ISBLK()
    • 特性:随机访问
    • 典型代表:硬盘对应的文件
    • 块特殊文件都存放于 /dev
    • 将块特殊文件挂载到一个指定目录文件下,则可以以目录文件形式查看它
    • 挂载:mount + 块特殊文件名 + 指定目录名
    • 例:mount /dev/sdb1 ./usb
    • 解除挂载:umount + 被挂载的目录文件名
    • 例:umount usb
  • 字符特殊文件

    • 程序使用 S_ISCHR()
    • 特性:顺序访问
    • 典型代表:键盘对应的文件
  • 套接字

    • 程序使用 S_ISSOCK()
    • 专门用来对互联网中的2个不同的程序进行数据交互
    • ip地址 相当于一台设备在互联网中的门牌号码
    • port(端口号)相当于:一个程序在一台设备中的门牌号码
    • 套接字文件只有文件描述符,没有绝对路径
      套接字文件单向双向都可以
      使用函数socket()来创建一个套接字文件
      #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
    
       int socket(int domain, int type, int protocol);
    

套接字工作原理:
服务器端:服务器首先创建一个套接字,为该套接字绑定ip地址和端口号,表示服务器在网络中的位置。
客户端:也创建一个套接字,然后绑定服务器的ip地址和端口号,用来寻找服务器所在地。

Linux中如何去判断一个文件是什么类型?

使用函数 stat 一族

lstat使用频率最高,fstat在只有匿名管道和套接字文件判断时使用。
stat和fstat有一个最大缺点,无法判断链接文件

stat使用:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

将文件信息传入到设定的stat结构体。

示例:

EXAMPLE
       The following program calls lstat() and displays selected fields in the
       returned stat structure.

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <time.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/sysmacros.h>

       int
       main(int argc, char *argv[])
       {
           struct stat sb;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           if (lstat(argv[1], &sb) == -1) {
               perror("lstat");
               exit(EXIT_FAILURE);
           }

           printf("ID of containing device:  [%lx,%lx]\n",
                (long) major(sb.st_dev), (long) minor(sb.st_dev));

           printf("File type:                ");
			//判断st_mode(文件类型)
           switch (sb.st_mode & S_IFMT) { 
           case S_IFBLK:  printf("block device\n");            break;
           case S_IFCHR:  printf("character device\n");        break;
           case S_IFDIR:  printf("directory\n");               break;
           case S_IFIFO:  printf("FIFO/pipe\n");               break;
           case S_IFLNK:  printf("symlink\n");                 break;
           case S_IFREG:  printf("regular file\n");            break;
           case S_IFSOCK: printf("socket\n");                  break;
           default:       printf("unknown?\n");                break;
           }

           printf("I-node number:            %ld\n", (long) sb.st_ino);

           printf("Mode:                     %lo (octal)\n",
                   (unsigned long) sb.st_mode);

           printf("Link count:               %ld\n", (long) sb.st_nlink);
           printf("Ownership:                UID=%ld   GID=%ld\n",
                   (long) sb.st_uid, (long) sb.st_gid);

           printf("Preferred I/O block size: %ld bytes\n",
                   (long) sb.st_blksize);
           printf("File size:                %lld bytes\n",
                   (long long) sb.st_size);
           printf("Blocks allocated:         %lld\n",
                   (long long) sb.st_blocks);

           printf("Last status change:       %s", ctime(&sb.st_ctime));
           printf("Last file access:         %s", ctime(&sb.st_atime));
           printf("Last file modification:   %s", ctime(&sb.st_mtime));

           exit(EXIT_SUCCESS);
       }

输出:

pi@raspberrypi:~/haitong-learning/Linux/homework/文件系统 $ ./stat 判断 stat判断.c
ID of containing device:  [b3,2]
File type:                regular file
I-node number:            651140
Mode:                     100644 (octal)
Link count:               1
Ownership:                UID=1000   GID=1000
Preferred I/O block size: 4096 bytes
File size:                2289 bytes
Blocks allocated:         8
Last status change:       Thu May 13 15:02:08 2021
Last file access:         Thu May 13 15:02:08 2021
Last file modification:   Thu May 13 15:02:08 2021

cp指令实现:

  1. 示例1:
/*
 * cp指令实现.c
 * 实现cp的文件复制功能和将文件复制到文件夹功能
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


int main(int argc, char **argv)
{
	char buf;
	
	if(argc!=3)
	{printf("cp fnsrc fndes");exit(1);}
	if(!strcmp(argv[1],argv[2]))
	{printf("same name!");exit(1);}
	
	struct stat sb;
	if(lstat(argv[2],&sb)!=-1)//若文件存在
	{
		
	int mode=sb.st_mode & S_IFMT;
	 //注意sb.st_mode & S_IFMT 才能分辨S_IFREG,S_IFDIR等标志
	if(mode==S_IFREG){printf("file exist!");exit(1);}
	if(mode==S_IFDIR)
	{printf("directory\n");  strcat(argv[2],argv[1]);}
	
	}
	
	if(creat(argv[2],0666)<0)
	{printf("creat error");exit(1);}

	int src=open(argv[1],O_RDONLY);
	int des=open(argv[2],O_WRONLY);
	while(read(src,&buf,1)>0)write(des,&buf,1);
	close(src); close(des);
	
	return 0;
}

  1. 示例2:
/*
 * cp指令文件夹实现.c
 * 实现cp文件夹复制到文件夹
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	//popen在command程序和当前程序建立管道,并执行command指令
	//当command以"r"形式打开,command所有标准输出都对直接输入到管道中,当前程序只需要读取管道中的内容即可。
	//由于command所有标准输出都会进入管道,所以只适合简单逻辑的输出
	char command[]="/bin/ls ";
	strcat(command,argv[1]);
	FILE* fp=popen(command,"r"); 
	//将ls 111所输出的文件名都放入管道
	char buf[128]={0};
	char pathnamesrc[128];
	char pathnamedes[128];
	int src,des;

	while(fscanf(fp,"%s",buf)>0){
		strcpy(pathnamedes,argv[2]);
		strcat(pathnamedes,buf);
		printf("%s\n",pathnamedes);
		
		if(creat(pathnamedes,0666)<0)
		{printf("creat error");exit(1);}
		
		strcpy(pathnamesrc,argv[1]);
		strcat(pathnamesrc,buf);
		printf("%s\n",pathnamesrc);
		
		src=open(pathnamesrc,O_RDONLY);
		des=open(pathnamedes,O_WRONLY);
		while(read(src,&buf,1)>0)write(des,&buf,1);
		close(src); close(des);
	}
	
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值