linux 应用编程————三、文件属性

一、linux 中各种文件类型

1、普通文件( - regular file )

  • 文本文件(将 0101 的数字按照一定编码,变得能让人看懂)

文件中的内容是由文本构成的,文本指的是ASCII码字符。文件里的内容本质上都是数字(不管什么文件内容本质上都是数字,因为计算机本身只有0和1),而文本文件中的数字本身应该被理解为这个数字对应的ASCII码。常见的有**.c文件、.h文件、.txt文件**等都是文本文件。文本文件的好处就是可以被人轻松读懂和编写。所以说文本文件天生为人类发明的。

  • 二进制文件(编译生成的可执行文件)

二进制文件中存储的本质也是数字,只不过这些数字并不是文字的编码数字,而是真正的数字。常见的可执行文件(gcc编译生成的a.out,arm-linux-gcc编译链接生成的.bin)都是二进制文件。

总结:
本质上看,文本文件和二进制文件并没有二进制文件。都是在一个文件里存放了数字
区别就是理解方式不同,如果把这些数字就当作数字处理则就是二进制文件,如果把这些数字按照某种编码格式去解码成文本字符,则就是文本文件。

问:我们如何知道一个文件是文本文件还是二进制文件呢?
在Linux系统层面是不区分这两个的(譬如open、read、write等方法操作文本文件和二进制文件时一点区别都没有),所以我们无法从文件本身准确知道文件属于哪一种,我们只能本来就知道这个文件的类型然后用这个文件的用法去用他。有时候会用一些后缀名来人为的标记文件的类型。

使用文本文件时,常规用法就是用文本文件编辑器去打开它、编辑它。常见的文本文件编辑器如vim、gedit、notepad++、sourceinsight等,我们用这些文本文件编辑器去打开文件的时候,编辑器会read读出文件二进制数字内容,然后按照编码格式去解码将其还原成文字展现给我们。

问:如果用文本文件编辑器去打开一个二进制文件会如何?
这时候编辑器就以为这个二进制文件还是文本文件,然后试图去将其解码成文字,但是解码过程中很多数字并不对应有意义的文字所以成了乱码。

问:如果用二进制阅读工具去打开一个文本文件如何?
得出的就是文本文件对应的二进制的编码。

2、目录文件( d   directory )

目录就是文件夹,文件夹在Linux中 也是一种文件,不过是特殊文件。
用vi打开一个文件夹就能看到,文件夹其实就是一种特殊文件,里边存的内容包括这个文件的路径还有文件夹里边的文件列表
在这里插入图片描述
(kong 文件当中包含 1.txt,2.c,kong 三个文件)

但是文件夹这种文件比较特殊,本身并不适合用普通的方式来读写。Linux中是使用特殊的一些API来专门读写文件夹的。

3、设备文件(c , b)

设备文件包括:

字符设备文件(c   character)

块设备文件(b  block)。

设备文件对应的是硬件设备,也就是说这个文件虽然在文件系统中存在,但是并不是真正存在于硬盘上的一个文件,而是文件系统虚拟制造出来的(叫做虚拟文件系统,如/dev /sys /proc等)

虚拟文件系统中的文件大多数不能或者说是不用直接读写的,而是用一些特殊的API产生或者使用的。

4、管道文件(p  pipe)

管道在Linux中是一种通信手段,在通信的时候需要建立管道文件作为辅助。

5、套接字文件(s   socket)

在上网的时候需要建立套接字文件

6、符号链接文件(l  link)

类似于Windows中的快捷方式,包括硬链接、软连接等等。

二、常用文件属性获取

什么是文件属性
在这里插入图片描述
在这里插入图片描述

利用 stat 函数可以看到,文件名,文件大小,在硬盘什么位置,文件权限等等这都是文件属性。

Blocks:占用多少个块
IO Block: 之前讲的在应用层有一个缓冲机制, 此处代表凑够 1024 个字节,进行一次 IO 读写
Device :指这个文件对应的设备,(18h/24d 可能代表设备名,或者块名那一类)
Inode : 静态文件存储在硬盘当中的标识。
Access:可访问性:
A time: 最后一次访问的时间
Modify: 表示修改文件里面的内容
Change:表示更改了一次文件的权限

1、stat、fstat、lstat API简介

文件属性信息查看的API有三个:stat、fstat、lstat,三个作用一样,参数不同,细节略有不同。

int stat(const char *path, struct stat *buf);
  	 分析这个函数:
 	 const char *path : 输入型参数,表示这个参数在这个函数当中不会改变。
	 struct stat *buf : 输出型参数,这个结构体在这个函数当中会被填充。
     总结: 输入文件名(const char *path),输出文件属性(struct stat *buf)


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

fstat和stat的区别是:

stat是从文件名出发得到文件属性信息结构体,而fstat是从一个已经打开的文件fd出发得到一个文件的属性信息。
所以用的时候如果文件没有打开(我们并不想打开文件操作而只是希望得到文件属性)那就用stat,
如果文件已经被打开了然后要属性那就用fstat效率会更高

stat是从磁盘去读取文件的,而fstat是从内存读取动态文件的。

lstat 和 stat/fstat的差别在于:

对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。

2、struct stat结构体简介

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

3、利用 stat 函数来,显示一些自己想要的文件属性。

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

#define NAME "1.txt"

int main(void)
{
    
    
	int ret = -1;
	struct stat buf;
	
	memset(&buf, 0, sizeof(buf));		// memset后buf中全是0
	ret = stat(NAME, &buf);				// stat后buf中有内容了
	if (ret < 0)
	{
    
    
		perror("stat");
		exit(-1);
	}
	// 成功获取了stat结构体,从中可以得到各种属性信息了
	printf("inode = %d.\n", buf.st_ino);
	printf("size = %d bytes.\n", buf.st_size);
	printf("st_blksize = %d.\n", buf.st_blksize);
	
	return 0;
}

在这里插入图片描述

4、利用 stat 来判断文件类型

  • 文件属性中的文件类型标志在 struct stat结构体的mode_t st_mode元素中,这个元素其实是一个按位来定义的一个位标志(有点类似于ARM CPU的CPSR寄存器的模式位定义)。

  • 利用 man 手册当中特定的宏,来确定这个文件到底是什么文件。譬如S_ISREG宏返回值是1表示这个文件是一个普通文件,如果文件不是普通文件则返回值是0.
    在这里插入图片描述

理解英文: s_isreg:is regular? 都是缩写

实验代码:

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


#define NAME "1.txt"

int main(void)
{
    
    
	int ret = -1;
	struct stat buf;
	
	memset(&buf, 0, sizeof(buf));		// memset后buf中全是0
	ret = stat(NAME, &buf);				// stat后buf中有内容了
	if (ret < 0)
	{
    
    
		perror("stat");
		exit(-1);
	}	
	// 判断这个文件属性
	//int result = S_ISREG(buf.st_mode);        //判断是否为普通文件,是则返回1,不是返回0
	int result = S_ISDIR(buf.st_mode);           //判断是否为目录文件,是则返回1,不是返回0
	printf("result = %d\n", result);
	return 0;
}

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

三、linux 当中的文件权限(以及详解)

linux 当中并没有给文件权限测试提供宏操作,而只是提供了位掩码,所以我们只能用位掩码自己来判断。

重点还是: struct stat 里面的 st_mode 元素, 这个元素本质是 unsigned int (32位数字)。

  • st_mode 当中记录的文件权限位和文件类型
    在这里插入图片描述
    使用规则:(需要自己回头查找)

1、linux 当中使用 ls -l 来显示文件权限

在这里插入图片描述

在这里插入图片描述
user(属主权限): 创建这个文件的用户,在 aston 用户下创建的文件。
group(属组权限):属组(不太清楚),好像一般和属主相同。
other(其他用户权限):既不是属主 也不是属组。

在这里插入图片描述

2、文件操作时的权限检查规则

思考:当一个可执行文件 a.out 在执行的时候,内部程序尝试访问文件,这时可执行程序扮演什么角色?

答案:主要看 a.out 可执行程序,在哪个用户下被执行。
若在 test 用户下执行,访问的属主和属组都是 aston 的文件,那么这个 a.out 在这个文件看来就是其他用户(other)。

代码测试:

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

       
#define NAME "1.txt"

int main(int argv,char *argv[])
{
    
    
	int fd1 = -1;

	fd1 = open(NAME, O_RDONLY);  // O_RDONLY :表示这个有可读权限,如果没有可读权限就会打开失败
	if(fd1 > 0)
	{
    
    
	printf("可读\n");
	}
	
	fd1 = open(NAME, O_WRONLY);  // O_WRONLY :表示这个有可读权限,如果没有可写权限也会打开失败
	if(fd1 > 0)
	{
    
    
	printf("可读\n");
	}
	
	return 0;
}

在这里插入图片描述

  • 情况一:在 aston 用户下,执行程序访问 1.txt,这时候扮演 user 的角色,就是这个文件的属主在访问他,所以看前三个标志。(可读,可写
  • 情况二:在 aston 用户下,执行程序访问 2.txt,这时候扮演 other 的角色,就是其他用户在访问这个文件,所以看后三个标志。(只是可写
    在这里插入图片描述

3、access函数检查权限设置

举例: windows 系统下,有很多软件只有在管理员模式下才可以正常进行使用。然而在使用的时候也没有提醒我们要不要使用管理员模式。
缺点:在使用之前没有添加检查目前用户有没有访问权限的代码。
解决:使用 access 函数可其进行测试。

  • 函数原型
int access(const char *pathname, int mode);
参数:
const char *pathname :文件名称
int mode :检测各种权限有: F_OK , R_OK , W_OK, X_OK
 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,
返回值:
 On  success zero is returned.
 On error -1 is returned, 
  • 代码测试
#include <unistd.h>
#include <stdio.h>

//#define NAME "1.txt"
int main(int argc, char *argv[])
{
    
    
	int ret = -1;
	ret = access(argv[1],F_OK);
	if(ret == 0)
		printf("file exsit\n");
	else 
	{
    
    
		printf("please to creat\n");
		_exit(-1);
	}
	
	
	ret = access(argv[1],R_OK);
	if(ret == 0)
		printf("file can be read\n");
	else 
	{
    
    
		printf("the file havn't read permission\n");
	}	
	
	ret = access(argv[1],W_OK);
	if(ret == 0)
		printf("file can be write\n");
	else 
	{
    
    
		printf("the file havn't write permission\n");
	}	
	
	ret = access(argv[1],X_OK);
	if(ret == 0)
		printf("file can be executed \n");
	else 
	{
    
    
		printf("the file havn't execute permission\n");
	}	
	return 0;
}

4、chmod 和 chown

在这里插入图片描述

chowm 修改文件的 属主或者 属组
chmod 修改 user group and other 对这个文件的权限(read write execute)
注:chmod 只有 root 用户才可以使用

  • chmod 既是linux命令 也是 linux的API
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);

参数:
const char *path :文件名
mode_t mode :参数
S_IRUSR :set read usr ,给 usr 添加read 权限
S_IWUSR :set write usr ,给 usr 添加write 权限
案例:
chmod("1.txt",S_IRUSR  | S_IWUSR ); usr or operation

补充:chmod命令(linux 命令)的使用:
在这里插入图片描述

  • chmod函数(linux API)的使用
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
    
    
	int ret = -1;
	if (argc != 2)
	{
    
    
		printf("usage: %s filename\n", argv[0]);
		return -1;
	}
	
	ret = chmod(argv[1], S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWOTH);  //设置属主对文件有可读可写可执行权限
	if (ret < 0)                                     //属主所在的组对文件有可读权限,其他用户对文件有可操作权限
	{
    
    
		perror("chmod");
		return -1;
	}
	
	return 0;
}

在这里插入图片描述

注:我们这里只给了 usr 用户的权限。

  • chown命令(linux 命令)的使用

chown [–R] 属主名 文件名
chown [-R] 属主名:属组名 文件名

  • 只修改属主
    在这里插入图片描述在这里插入图片描述
  • 既修改属主,也修改属组
    在这里插入图片描述
    在这里插入图片描述

5、umask (文件权限掩码)

umask 是linux 系统当中维护的一个全局设置,

引入:新创建的文件默认的权限是什么?(实际操作显示这样)
在这里插入图片描述
利用 umask 可以看出是 0022
在这里插入图片描述
将 umask 修改为 0044
在这里插入图片描述
在这里插入图片描述
总结:当为 2 的时候,对应的w被屏蔽。 当为 4 的时候,对应的 r 位被屏蔽。
然后分别改为 0077(全部不允许) 和 0000(全部允许) 的时候。

为了安全考虑,一般文件不具备执行权限,只有编译生成的可执行文件才具有可执行权限。

在这里插入图片描述

四、读取目录文件

1、Opendir 和 readfir函数

  • 函数模型
DIR *opendir(const char *name);
   const char *name: 要打开的目录文件
   DIR *  : 返回的文件指针

struct dirent *readdir(DIR *dirp);
	DIR *dirp: 就是使用 opendir 函数返回的指针
	返回一个结构体指针
     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 */				(文件名字)

           			}

1、opendir打开一个目录后得到一个DIR类型的指针给readdir使用
2、readdir函数调用一次就会返回一个struct dirent类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。
在这里插入图片描述
注:一个文件夹下有 8 个文件,我们每调用一次只返回一个该文件下的子文件信息,所以我们一共需要调用 8次。
readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。
当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。

  • 代码实践
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>


int main(int argc, char **argv)
{
    
    
	DIR *pDir = NULL;
	struct dirent * pEnt = NULL;
	unsigned int cnt = 0;
	
	if (argc != 2)
	{
    
    
		printf("usage: %s dirname\n", argv[0]);
		return -1;
	}
	
	pDir = opendir(argv[1]);
	if (NULL == pDir)
	{
    
    
		perror("opendir");
		return -1;
	}
	
	while (1)
	{
    
    
		pEnt = readdir(pDir);
		if(pEnt != NULL)
		{
    
    
			// 还有子文件,在此处理子文件
			printf("name:[%s]	,", pEnt->d_name);
			cnt++;
			if (pEnt->d_type == DT_REG)
			{
    
    
				printf("是普通文件\n");
			}
			else
			{
    
    
				printf("不是普通文件\n");
			}
		}
		else
		{
    
    
			break;
		}
	};
	printf("总文件数为:%d\n", cnt);
	
	return 0;
}

在这里插入图片描述

2、可重入函数引入

(1)有些函数是可重入的有些是不可重入的,至于具体用法读者自行去百度查阅。
(2)readdir函数和我们前面接触的一些函数是不同的,首先readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法是readdir不可重入的关键。
(3)readdir在多次调用时是有关联的,这个关联也标明readdir函数是不可重入的。
(4)库函数中有一些函数当年刚开始提供时都是不可重入的,后来意识到这种方式不安全,所以重新封装了C库,提供了对应的可重复版本(一般是不可重入版本函数名_r

猜你喜欢

转载自blog.csdn.net/vincent3678/article/details/112081522