• 05-Linux系统编程-第02天(文件系统、目录操作、dup2)


    1 课程回顾

    02-文件存储

    文件名不在inode里 而是保存在一个叫dentry的结构体里了

    格式化就是指定一组规则

    指定对文件的存储及读取的一般方法

    linux下主要使用 ext2 ext3 ext4

    ext2 是类unix系统的基础文件系统类型

    高版本要向低版本兼容

    03-ext2文件系统

    拿网吧示例 :

    有个人上机了 那么老板的管理系统上旧标注这个人上机了

    (把这个管理系统的电脑也放到这12个机器中把…

    data blocks 存的数据

    inode table 存的inode结构体

    inode bitmap 描述inodetable里每个inode使用情况

    (老板的机器就像是bitmap 来查看inode使用情况)

    block bitmap 描述block的使用情况(描述的是整个块组的所有的block,包括它自己)

    super block 描述整个分区的文件系统信息

    GDT 块组描述符表

    04-ext2文件系统2

    一个groupblock是由无数多个相同大小的数据块组成的

    格式化的时候就生成很多inode都指定好了

    Touch一个文件就是从inode中拿一个分给你

    (先有inode 后有文件

    Inode位图就是来说明你这个inode是否被划分出去了 如果被划分出去了 某个inode bitmap上就划个1… …

    疑惑:

    文件存储之前也不知道磁盘会存多少个文件 那么应该划分多少个inode?

    操作系统的操作机理是按照8Kb对应一个inode

    从整个块组中除去前面的内容后剩下的(直接用数据块大小近似)除以8 得到多少个就在inode里分配多少个

    但是有的文件1kb都不到 有文件上g 会出现inode多余浪费或inode不够的情况..

    所以大家使用的电脑的磁盘都不能百分之百完全利用 都有一定的浪费

    如果能预见到磁盘存放的数据 比如寸电影,就可以把inode调大一点

    如果存小文件就可以把inode放小

    每8KB划分一个inode的机制是系统决定的

    一个inode结构体128byte

     

    vi hello.c 存放到磁盘上比如说占用了一个block 1kb

    inode结构体存着这个文件的编号 然后去磁盘找对应编号的文件

    数据块寻址:

    inode里面会有一个索引项

    来负责找数据块

    如果一个文件有多个数据块 这些数据块很可能不是连续存放的。 这些数据块通过inode中的索引项block来找到

    这样的索引项一共有15个,Block[0]-Block[14] 每个索引项占4字节 前12个索引项都表示块的编号 如果block[0]保存27 表示第27个块是该文件的数据库快

    如果创建的文件系统指定的块大小是1kb 这种方法可以表示从0~12kb的文件 如果剩下三个索引项也这么用 那么旧只能表示最大15kb的文件了 这是远远不够的

    剩下3个索引项block[12] block[13] block[14] 是间接索引 假设块大小为b 那么一个间接寻址块中可以存放b/4个索引项 指向b/4个数据块。

    如: block = 1kb, 那么 block[0] –block[12] 都用上 最大可以表示 12 + 1024/4 = 268K大小的文件 虽然大很多 但仍然不够用。

     于是有了二级间接寻址块Block[13] 三级间接寻址块Block[14] 这样1K的块 最大可以表示16.06GB大小的文件。

    (12 13 14间接索引 分别是一级索引 二级索引 三级索引)

    拿12举例 假设block是1kb 那么 12指向的块分成1k/4 256小块 每个小块自身大小是4byte 这个小块指向一个1kb的block数据块

    如果是二级索引 上面的256个小块中每个小块再指向一个1k/4的块..

    结论: superblock划分1k, 1个inode最大索引16.06GB文件

    目前使用的linux操作系统一般划分block是4k 所以一个inode最大索引64gb大小文件

    疑问: 一个分区里要划分多少个块组?

    答: 使用块位图描述整个分区所有块的使用情况

    如果superblock指定一个逻辑块是1k 那么,因为块位图要占一个块 所以块位图也是1k

    1k 大小是1024*8个位 此时一个块组最多可以有1024*8个块

    如果整个分区有s个块 那么就会有s/1024*8 个块组

    。。。

    05-stat函数

    man 2 stat

     

    注意:

    blocksize 就是superblock中指定的一个逻辑block的大小

    用一下shell command stat 查看一下文件状态:

    IO块:操作系统的逻辑I/O块设定的大小 (默认咱们用的是4kb

    块: 使用扇区的个数 512*24

    设备:组设备号和设备号

    Inode: Inode编号

    读取一下makefile文件的大小

    和ll结果一样都是208

    st_mode 权限:

    st_mode本质也是位图

    后三位用来描述other 的 r w x权限…

    user… other… group…

    最前面的四位描述文件类型

    4个位有2^4种情况 可以描述16种文件类型

    但是linux实际就7种

    在未来的20,30年如果有新加入的文件类型 就留有余地了

    0001 普通文件

    0010 目录

    … …

     

    这里就不做位与 位或操作了 操作系统提供了宏函数:

    参数就是st_mode

    06-穿透符号链接

     

    ln –s stat stat.s

    软连接显示的不对

    对于stat来说,它是会穿透符号链接的系统调用

    stat会穿透链接并找到源文件

    使用lstate不会穿透链接

    把stat函数修改成lstate,然后再跑一遍:

    扩展:

    man 2 stat

    然后看example:

     page中的例子:

    他的判断方法使用switch case 使用st_mode与宏位与 最后用宏来判断:

    掩码S_IFMT

     

    f 就是4个1

    还有一些别的掩码

    07-特殊权限位

    最后一个t表示黏住位

    chmod 01777   在前面加一个1 设置黏住位

    ll 就显示黏住位了 最后一个t表示黏住位

    比如中国银行有一个改密码的业务需求,

    普通用户不能取修改数据库内的数据 修改密码

    要给用户授权变成超级用户取改?

    或者用户提交改密码的请求到中国银行,由root用户去改

    setSIT = 1 ,用户的有效用户ID就发生变化了

    接下来A用户,B用户,C用户 直接调用程序修改密码就行了

    setuid 设置用户id位

    setgid 设置用户组id位

    ll a.out

    这就是一个可执行文件 所有者是itcast

    ./a.out就开始执行这个程序了

    进程在运行期间 所有者就是itcast 这个就是实际用户ID(UID

    默认情况下EID和UID相同,

    但是,当文件setID被设置后两个ID则有可能不一样,

    当执行一个文件加sudo的时候 当前执行期间有效用户变成root用户 但是实际用户并没有改变

    特殊权限位的三位

    设置组id位

    设置用户id位

    黏住位

    08-文件操作常用函数

    access:

    测试是否有读权限 写权限 和执行权限

    0 返回 说明成功

    chmod:

    access.c:

    truncate不仅能清空,也可以将文件设置成指定长度

    (都不需要open或者close 直接就能运行

    目的是为了文件共享

    也就是硬链接

    Inode号一样 但是文件名一样

    上面这个东西就叫dentry

    或者叫 directory entry 目录项

    link函数 为一个已经存在的文件创建目录项(也就是创建硬链接)

    以上其实就是实现了一个ln命令

    unlink:

    man 2 unlink:

    ulink作用 删除一个文件的dentry

    09-隐式回收和其他文件操作函数

    查看上面代码:

    创建文件 然后像文件里写入内容,中间输出”Enter a key to continue” 然后getchar()等待用户输入, 最后unlink

    make

    然后在getchar没输入的时候重新开一个终端cat temp.txt

    能查看到,

    如果此时我按了一个键 再cat 就发现temp已经被删除掉了

    注意: 如果再printf前面加上一个p[0] = ‘H‘

    会引起段错误, 此时查看temp发现临时文件仍然存在

    (因为没有走到unlink就结束了)

    如果希望实现: getchar()之前文件存在,getchar之后文件删除的效果,要怎么做?

    解决方法是写错误处理,如果出错的时候把文件删除

    注意:段错误是运行时错误 不能捕捉, 是不可逆的。

    需要用*(后面讲到 信号) 才能捕捉

    文件open完了紧接着就调用unlink 。。 看看能删了不

    看一下代码

     

    make 然后运行一下,

    make 运行,ls查看文件不存在了

    既然删除成功,为什么还能write, write成功了…write没有报错.. 是怎么回事呢

    把p[0]=’H’ 打开 再试一下:

     

     

    按理说只要改p[0]就会报段错误

    运行一下,的确报段错误了, temp.txt也不存在了,

     

    … … emmmm这不废话么 但是还是

    有一个不理解的地方:  文件都已经给删除掉了,为什么write函数没有报错

    当unlink文件的时候 只是删除了一个dentry(directory entry) 没有线连像inode了

    删除的时候 会把dentry给删除掉 没有dentry指向inode了

    但是磁盘上的数据没有擦除掉

    inodemap上的某个位置从1变成0了 但是磁盘盘块上的数据还是存在的

    将来再有数据来了 这个盘块会分配给新的数据 新的数据会直接将原有的数据给覆盖掉

    磁盘使用就是新数据覆盖旧数据

    所以格式化并不安全 格式化只是把左边两条线断开了..

    磁盘上的数据并没有抹掉

    把这些箭头重新生成的过程 叫 数据恢复

    ulink只是让文件具备了被删除的基础,至于什么时候被删除掉由系统说了算 程序不管

    操作系统如果有需求可以把这段空间拿给其他进程用 我不用了

    至于什么时候释放掉,系统说了算 ,程序不管了。

    刚才的程序为什么unlink后还能write,因为unlink后只是把dentry删除掉了 ,从用户的层面ls –l 已经看不到这个文件了 但是磁盘上的文件没有被真正删除

    所以还能write 因为系统调用write 内核结构文件结构体去访问的 不需要再借助路径了,到内核里就借助结构体指针了

    打开unlink的进程没有关闭 内核没有执行清空 进程如果结束 就不能真正的write了

    (所以write没有报错)

    还有一个问题, open了一个文件以后 中间出现段错误 代码就不再向下执行了,

    后面没有执行到close 所以之前open的文件一直是打开的状态

    假如说打开的文件占用文件描述符是3 (0,1,2分别是标准输入。标准输出。标准出错) 下次我再执行a.out,再打开就占4了 如果执行1024遍就耗光了?

    隐式回收:

    当进程结束运行时 所有该进程打开的文件都会被关闭,申请的内存空间会被释放,系统的这一特性称为隐式回收系统资源。

    思考一下。。执行./a.out的时候 0 1 2 就自动打开了 但是从来不用写close(0) close(1) close(2)

    这个就是依赖的隐式回收

    malloc申请内存 如果进程不结束的话,申请的内存会越积越多 但是如果进程关闭后其实就被操作系统隐式回收了

    link是建立dentry

    symlink是软链接(符号链接

    思考如何实现ln命令:

    ln 硬链接

    ln –s 软连接

    如果argv[1]是-s就去调用symlink

    readlink 是不穿透的

    ls –l是不穿透的

     

    (左侧l 指示这是一个硬链接)

    假设test.soft是指向test目录的一个软连接

    cd test.soft才是穿透

    readlink test.soft 是不穿透的

    (注意这里是shell里的readlink命令)  (man 1 readlink)

    下面说的是readlink函数: (man 2 readlink)

    rename函数:

    10-目录操作函数1

    ./ 表示当前进程工作目录

    当前进程就是 shell进程

    ../ 表示shell进程当前工作目录的上一级目录

    pwd 打印的是shell进程的工作目录(man 1 pwd)

    ./a.out里getcwd 打印的是a.out进程执行的工作目录

    (在第三卷了 是一个标准库函数)

    test.c:

    结果:

    打印输出的目录变了

    为什么执行完后出来的而目录没变?

    答案:出来以后的目录是shell进程的目录 不是a.out进程的目录

    怎么让shell调用chdir呢.. 其实在shell里执行 .. 就是向上一级走了

    改变一下test.c:

     

    结果:

    注意如果目录没有执行权限了就不能进去了

    在linux里目录也是文件

    读目录文件里的内容

    读出来的是目录项

    一个目录文件内部里面就存储着一条一条目录里的内容

    vi一个目录

    这些东西就是目录项

    11-目录操作函数2

    写目录操作几乎不做

    就是添加目录项

    manpage里面也没有writedir

    opendir closedir打开的是目录了 返回的是DIR结构体

    opendir.c:

    在vim下查看man手册

    :!man perror

    readir得到是什么

    读一次目录 得到一个目录项:

    struct dirent结构体就是描述目录项的

    d_name 256 指示这文件长度最大不能超过255 还有一个呢

    读一次调用一次读一条目录项

    再调用一次 再读一条目录项

    如果想把所有都读出要循环去调用

    所以当为null的时候要继续判断errno 如果有值说明程序出错 如果没有值说明到达文件结尾了

    12-递归目录

    opendir如何实现不打印隐藏文件?

    加一行判断 如何文件名的第一个字符不是. 就打印

    思考题: 如何实现 ls –l

    答:用stat函数拿到inode结构体的内容

    stat拿到的是uid和gid 对应的是数, 如果想输出字符串还要自己对应一下

     

  • 相关阅读:
    VOA 转
    DataTable中动态的赋值 转
    哈希表 转
    利用Hook技术实现键盘监控 转
    sql 里的 order by 和 group by 的区别 转
    DES加密算法 转
    Windows的消息机制 转
    “赢在中国”点评人给80后年轻人的30个忠告 转
    ASCII码对照表 转
    以太网 转
  • 原文地址:https://www.cnblogs.com/eret9616/p/10833365.html
Copyright © 2020-2023  润新知