加载中…
正文 字体大小:

小结啊小结

(2014-07-19 09:15:27)
标签:

it

学习小结-关于文件的一些信息

有四个函数stat()fstat()、lstat()fstat()可以得到与命名文件有关的信息结构。这四个函数的原型:

#include "\<"sys/stat.h"\>"
int stat(const char *restrict pathname , struct stat * restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fstatat(int fd, const char *restrict pathname, strcut stat *restrict buf, int flag);

  

  他们的函数返回值都是成功返回0,出错返回-1。其中,lstat不跟随符号链接文件,fstatat通过flag标志来确定是否跟随符号链接文件。

关于命名文件的信息都存在了struct stat结构体中。


struct stat {

mode_t st_mode; //文件的类型和访问权限

ino_t st_ino; //i节点

nlink_t st_nlink; //连到该文件的连接数目

uid_t st_uid; //用户ID

gid_t st_gid; //ID

dev_t st_dev; //文件的设备编号

dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号

off_t st_size; //文件字节数(文件大小)

blksize_t st_blksize; //对文件I/O较合适的长度

blkcnt_t st_blocks; //所分配的实际块数

time_t st_atime; //最后一次访问时间

time_t st_mtime; //最后一次修改时间

time_t st_ctime; //最后一次文件属性改变时间

 

};


1.关于文件类型


    Linux文件系统的文件类型有普通文件、目录文件、块特殊文件(通常是指可以随机访问文件系统的设备,如硬盘驱动器和CD-OM等),字符特殊文件、FIFO文件、套接字和符号链接。我们通过7个宏来判断文件的类型:

    小结啊小结


    通过宏比如S_ISLNK(buf.st_mode)得到了非零数,即可表示为该文件为符号链接文件。

    它还可以对进程间通信对象的类型进行判断。在终端下,我们可以通过"ls -l"命令查看到文件类型。显示结果第一列的第一个字符就是表示该查看的文件所属的类型,如果是符号链接文件就会显示位”l”,如果是目录就会显示”d”,如果是普通文件那么就显示“-”


2.关于文件访问权限


 Linux系统中,权限是一个非常重要的概念,因为通过权限可以加强系统的安全性。一般是将权限分为三组,分别是用户,用户组和其他用户。


权限

含义

S_IRUSR

用户读

S_IWUSR

用户写

S_IXUSR

用户执行

S_IRGRP

用户所在的组读

S_IWGRP

用户所在的组写

S_IXGRP

用户所在的组执行

S_IROTH

其他读

S_IWOTH

其他写

S_IXOTH

其他执行

   

      我们将struct stat buf中的st_mode字段与所要判断的权限按位与,如果得到的结果为非负,那么就具有该权限。同样在终端下,我们可以通过”ls -l”命令查看文件的权限信息。在结果中第一个字段除了第一个字符外,剩余字符用来表示用户、用户组以及其他的读(r)、写(w)、可执行(x)权限。有时,我们可以看到会出现字符”s” 。出现这个字符表示该文件拥有了设置用户ID位或者设置用户组ID位。


设置这个权限位又有什么作用呢?如果设置了用户ID位,那么当一个进程执行此文件时,进程的有效用户ID设置位为文件所有者(st_uid)的用户ID;如果设置了用户组ID位,那么当一个进程执行此文件时,进程的有效用户组ID设置位为文件的组所有者(st_gid)ID


    这么做有什么好处呢?好处之一就是方便的同时也保证了安全性。很多书说到这里都会举修改用户密码的例子passwd命令。我们可以查看一下/usr/bin/passwd文件,可以看出它具有设置用户ID位,这就说明为什么作为普通用户的我们,同样可以修改密码。因为修改passwd这个文件具有了设置用户ID位,所以当我们执行该文件时,有效用户ID将会设为文件的所有者ID,即root,系统会给予root权限最大的自由度,并且文件所有者root对文件拥有读写的权力。


      而什么是有效用户ID呢?和另外一个名词实际用户ID又有什么区别呢?

    实际用户ID:我们实际上是谁?用于在系统中标识一个用户是谁。当用户使用用户名和密码成功系统后就唯一确定了他的实际用户ID。例如,我用户名:tqy成功登陆系统,那么实际用户ID就是tqy。

     有效用户ID:用于系统决定用户对系统资源的访问权限,通常情况下等于实际用户ID。

    

     我是通过函数access()和facceessat区分清楚的。这两个函数是按实际用户ID和实际组ID进行访问权限测试。

 

#include "./include/apue.h"

#include "./lib/error.c"

#include "\<"fcntl.h"\>"


int main(int argc, char *argv[])

{

if (argc != 2){

err_quit("usage: a.out ");

}

if (access(argv[1], R_OK) < 0){

err_ret("access error for %s", argv[1]);

}else {

printf("read access ok\n");

}

 

if (open(argv[1], O_RDONLY) < 0){

err_ret("open error for &s", argv[1]);

}else{

printf("open for reading ok\n");

}

return 0;

}

         


  可以改变运行程序的所有者(chown)为root,同时添加用户ID位(chmod),对/etc/shadow文件进行测试,查看测试结果。

   

    那什么是访问权限测试呢?这个测试是要涉及文件的所有者ID(st_uid),文件的组所有者ID(st_gid)和进程的有效用户ID和进程的有效用户组ID。所有者ID是文件的性质,有效者ID是进程的性质。他的测试过程是这样的(摘自于apue)

(1)若进程的有效用户ID是0(超级用户),则允许访问。这给予超级用户对整个文件系统进行处理的最充分的自由。

(2)若进程的有效用户ID等于文件的所有者ID(也就是进程拥有此文件),按么如果所有者适当的访问权限位被设置,则允许访问;否则拒绝访问。适当的访问权限位指的是,若进程为读而打开该文件,则用户读位应为1;若进程为写而打开该文件,则用户写位应为1;若进程将执行该文件,则用户执行位应为1.

(3)若进程的有效组ID或进程的附属组ID之一等于文件的组ID,那么如果组适当的访问权限位被设置,则允许访问;否则拒绝访问。

(4)若其他用户适当的访问权限被设置,则允许访问;否则拒绝访问。

(5)按顺序执行这4步。注意,如果进程拥有此文件(第2步),则按用户访问权限批准或拒绝该进程对文件的访问——不查看组访问权限。类似地,若进程并不拥有该文件。但进程属于某个适当的组,则按组访问权限批准或拒绝该进程对文件的访问——不查看其他用户的访问权限。

     对于访问权限就小结到这里。


3.关于inode


    文件数据除了文件的实际内容还包括了文件属性和文件权限。其中呢,系统将文件属性和文件权限存储在inode中,把文件的实际内容存储到block中。每一个block都有编号,每一个inode都有编号。每一个文件占用一个inode号,每一个inode号会记录文件的数据所在的block号码。所以,由此可知inode里存储的信息有:

文件的所有者ID

文件的组ID

文件的字节数

文件的读、写、执行权限

文件的时间戳(最后一次访问时间(buf.st_atime)、最后一次内容修改时间(buf.st_mtime)、最后一次属性更改时间)(but,st_ctime)

链接数(系统支持多个文件名指向同一个inode,这个就和硬链接有关了)

文件数据block的位置


    可以看出除了文件名和文件的具体内容,其他信息都存储在inode当中了。每个文件必须占有一个inode号,并且系统是通过inode号来识别文件的,所以当系统的inode用完了,那么就不可以创新的文件了。


  这里谈到了硬链接,什么是硬链接呢?硬链接只是在某个目录下新建一条文件名连接到某inode号码的关联记录(多个文件名对应到同一个inode号码)。这里buf.st_nlink指的就是硬链接数,我们可以通过“ln”命令创建一个硬链接,然后通过“ls -i”来查看文件的inode号。此时可以看出,硬链接文件和原文件的inode号相同。

     

 

    有了硬链接,就会有软链接。软链接又叫符号链接,符号链接是在创建一个独立的文件,而这个文件会让数据的读取指向它连接的那个文件的文件名。会占用inode和block。符号链接本身的内容就是文件名,用”ls -l”来查看一个符号链接,文件的大小显示为文件名的字节数。

  

4.关于进程里的文件的小知识


 在进程里如果要对文件进行操作,那么进程就会生成一个进程表项:


         小结啊小结


 

      对于linux系统来说并没有v节点,所以文件表项里会直接存储i节点信息即inode信息。对于dup()和dup2()或者功能为复制文件描述符的fcntl函数,调用成功后,他们返回的新文件描述符与第一个参数共享同一个文件表项,所以这两个文件描述符可以对同一个文件进行操作。


      如果一个文件以追加的方式打开的话,该文件表项里的文件偏移量是由inode节点里文件长度值得到的,所以追加方式打开的文件,“写”是直接操作在文件结尾的。


        inode和文件的删除也是有关系的。

        小结啊小结


目录项:文件路径中的每一部分就是所谓的目录项,如“/home/tqy/linuxc“中”/”,”/home”,”/tqy””/linuxc”都是目录项。

 

 

  目录项里存放着inode编号和文件名,所以没有存放在inode里的文件名信息存放在了目录项里。图中有两个文件指向同一个inode,所以可以知道这两个文件里面有硬链接,inode里会存放硬链接数。这个图也同样说明了,inode里面会存放block号,所以知道了文件的inode号,就知道了文件的绝大部分信息(除了文件名)。

   

      函数unlink()、unlinkat()可以删除目录项,并将相应的文件的链接数减1.但如果对该文件还有其他链接,则仍可通过其他链接访问该文件的数据。当连接计数达到0时,文件的内容才可以被删除。比如说,该图中有两个文件指向同一个inode,我们给他们分别起名为文件名1和文件名2。当我们使用unlink函数:unlink(“文件名1”)时,将会删除文件名1,并会在inode里链接数减1,但是原本链接数为2,所以减去1个链接数后,还有一个链接。此时的文件内容不会被删除,可以通过剩余的一个链接(文件2对应的)去访问该文件的数据。当文件2也被删除后,该文件的数据将不能被访问,因为文件数据已被删除了。


  注意:当要对文件删除时,必须对文件的目录具有写和执行权限。如果在删除时,有一个进程打开了文件,那么内容也不能删除。关闭一个文件时,内核首先会检查打开该文件的进程个数;如果这个计数达到0,内核再去检查其链接计数;如果计数也是0,那么就删除该文件的内容。”(摘自apue)

 

    和删除、重命名目录下文件有关的另一个知识点是:黏着位

  如果对一个目录设置黏着位,只有对该目录具有写权限的用户并且满足以下三个条件,才能删除或重命名该目录下的文件:

拥有此文件;

拥有此目录;

是超级用户。

     

     

5.关于设备特殊文件

 

   每个文件系统所在的存储设备都由其主、次设备号表示。主设备好标识设备驱动程序,次设备号标识特定的子设备。通常会用宏major和minor来访问主、次设备号。系统中与每个文件名关联的st_dev值是文件系统的设备号。只有字符特殊文件和块特殊文件才有st_rdev.


6.关于文件大小


buf.st_size表示普通文件大小,buf.st_blksize表示I/O操作最优的读取大小,这里最优指的是读一个文件所需的时间量最少。buf.st_blocks表示文件实际占用的磁盘块数。如果文件大小/每块磁盘的字节数>实际占用的磁盘块数,那么说明了该文件存在“文件空洞”。文件空洞可以通过lseek()和截断文件的函数truncate()和ftruncate()来产生。


7.关于“三个时间”:


buf.st_atime:最后一次的访问时间,比如说,读该文件。

buf.st_mtime:最后一次的修改内容时间,比如说,写该文件。

buf.st_ctime:最后一次改变文件属性状态的时间,比如说,chmod和chown


可以通过futimens和utimens来修改一个文件的访问和修改时间。这两个函数的times数组参数的第一个元素包含访问时间,第二元素包含修改时间。


练习:

#include "./include/apue.h"

#include "./lib/error.c"

#include “\<“fcntl.h"\>"

#include "\<"grp.h"\>"

#include "\<"pwd.h"\>"

#include "\<"time.h"\>"

#include "\<"sys/types.h"\>"

#include "\<"sys/sysmacros.h"\>"



int main(int argc, char *argv[])

{

struct stat buf;

char ptr[11] = " ";


if (argc != 2){

err_sys("erro arguemens");

}


if (lstat(argv[1], &buf) ==-1){

err_ret("lstat error");

}

 

//获得文件类型

if (S_ISREG(buf.st_mode)){

ptr[0] = '-';

}else if (S_ISCHR(buf.st_mode)){

ptr[0] = 'c';

//#define S_ISDIR (mode) (((mode) & S_IFMT) == S_IFDIR)

}else if (S_ISDIR(buf.st_mode)){

ptr[0] = 'd';

}else if (S_ISBLK(buf.st_mode)){

ptr[0] = 'b';

}else if (S_ISFIFO(buf.st_mode)){

ptr[0] = 'f';

}else if (S_ISSOCK(buf.st_mode)){

ptr[0] = 's';

}else if (S_ISLNK(buf.st_mode)){

ptr[0] = 'l';

}else {

printf("** unkown mode **");

}


//获得文件权限

if (buf.st_mode & S_IRUSR){

ptr[1] = 'r';

}else{

ptr[1] = '-';

}

if (buf.st_mode & S_IWUSR){

ptr[2] = 'w';

}else{

ptr[2] = '-';

}

if (buf.st_mode & S_IXUSR){

ptr[3] = 'x';

}else{

ptr[3] = '-';

}

if (buf.st_mode & S_IRGRP){

ptr[4] = 'r';

}else{

ptr[4] = '-';

}

if (buf.st_mode & S_IWGRP){

ptr[5] = 'w';

}else{

ptr[5] = '-';

}

if (buf.st_mode & S_IXGRP){

ptr[6] = 'x';

}else{

ptr[6] = '-';

}

    if (buf.st_mode & S_IROTH){

ptr[7] = 'r';

}else{

ptr[7] = '-';

}

if (buf.st_mode & S_IWOTH){

ptr[8] = 'w';

}else{

ptr[8] = '-';

}

if (buf.st_mode & S_IXOTH){

ptr[9] = 'x';

}else{

ptr[9] = '-';

}

ptr[10] = '\0';


//获得文件的inode号

printf("inode号:%d\n" ,buf.st_ino);

printf("权限: %s\n", ptr);


//获得文件链接数

printf("链接数: %d\n", buf.st_nlink);

 

//获得文家用户名和组名

         struct passwd *psd;

struct group *grp;


psd = getpwuid(buf.st_uid);

grp = getgrgid(buf.st_gid);

printf("用户名: %s\n", psd->pw_name);

printf("组名: %s\n", grp->gr_name);

   

//获得文件长度

printf("文件长度: m\n", buf.st_size);


char ctimebuf[32];

char mtimebuf[32];

char atimebuf[32];


//获得文件的““三个时间”

strcpy(ctimebuf, ctime(&buf.st_ctime));

ctimebuf[strlen(ctimebuf) - 1] = '\0';

printf("修改属性状态时间:%s\n", ctimebuf);

 

strcpy(mtimebuf, ctime(&buf.st_mtime));

mtimebuf[strlen(mtimebuf) - 1] = '\0';

printf("修改内容时间: %s\n", mtimebuf);

 

strcpy(atimebuf, ctime(&buf.st_atime));

atimebuf[strlen(atimebuf) - 1] = '\0';

printf("访问时间: %s\n", atimebuf);



//获得文件的设备号

printf("主/次设备号:%d/%d\n", major(buf.st_dev), minor(buf.st_dev));

//如果是字符特殊文件或者是块特殊文件,通过st_rdev查看

if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)){

printf("(%s) rdev = %d/%d\n", (S_ISCHR(buf.st_mode)) ? "character" : "block", major(buf.st_rdev), minor(buf.st_rdev));

}

 

return 0;

}

 

 

运行结果:

      小结啊小结


为了显示出<>,所以所有头文件均以“\<” "\>"显示

0

阅读 评论 收藏 转载 喜欢 打印举报
已投稿到:
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有