2.文件描述符

万物皆文件

在Unix/Linux里,“一切皆文件”——文件是文件、目录是文件、设备是文件,socket连接、管道也类似于打开的文件。

因为一个进程宏观上就两个状态,CPU burst或者I/O burst。I(input)无非就是:不是该进程的数据进入到该进程;O(output)无非就是:进程的输入输出到进程的外部。

文件描述符

一个“打开”的文件用文件描述符(file descriptor)表示。

每个进程都有一个文件描述符表,表示这个进程打开的所有文件。里面的每一条项目都对应一个打开的文件。文件描述符是一个大于等于0的整数,该数值表示这个打开的文件在该进程的打开文件表是第一个项目(从0开始计数)。

由于不同的进程都用大于等于零的整数作为文件描述符,所以在不同的进程中你会看到相同数值的文件描述符。文件描述表中每一项都包含一个指针,它指向系统级的打开文件表中的一个用于描述打开的文件的数据块。

对于一个进程而言,每一个文件描述符会与一个打开文件相对应,同时,不同的文件描述符也可以指向同一个文件。相同的文件可以被不同的进程打开也可以在同一个进程中被多次打开。内核维护了3个数据结构,分别是(1)进程级的文件描述符表、(2)系统级的打开文件描述符表、(3)文件系统的i-node表,如下图所示。

img

如何查找合适的系统调用/库函数

man手册是最终重要的参考文档和学习资料。

可以使用man -kgrep命令相结合查找想要的内容。

例:想要查找open file的系统调用

man -k file|grep open|grep \(2\)

系统调用open

man手册给出了系统调用open的头文件,以及函数原型。我们注意到open有两种调用格式,一种是两个参数,另外一种是三个参数。我们先看两个参数的调用格式。根据man文档,我们可以知道,pathname为欲打开的文件的文件名,flags为打开文件的访问模式,可以是O_RDONLY, O_WRONLY, 或 O_RDWR,分别对应为只读、只写和读写模式。注意,这种全大写的宏定义在C语言里一般都是对应为一个整数,即用一个整数数值来区分不同flag,写成这种全大写的宏定义是为了程序更易读。

open的返回值是文件描述符。表示为进程中打开的一个文件。当然,如果由于文件不存在或权限不够很有可能导致文件打开失败,此时open的返回值为-1。

错误(“异常”)处理

C语言没有“异常”这一概念,当系统调用出错时,会通过返回值来标识系统调用出错,并通过errno来指示到底出现了什么错误。我们可以用perror来打印出errno对应的以字符串描述的错误原因。

系统调用出错返回值的一般的规律是:如果系统调用的返回值是整数,则返回-1代表错误;如果系统调用的返回值是指针类型,则返回NULL代表错误。

#include <stdio.h>
#include <fcntl.h>

void main(){
    int fd=open("no_file", O_RDONLY);
    if(-1==fd){
        perror("cannot open");
    }
}

cannot open之后的内容即为errno对应的以字符串描述的错误原因——没有这个文件或目录。

image-20211130210626918

模仿cat命令

文件打开后,我们试图读取文件内容。可以通过man -k file|grep read|grep 2,亦可以通过open手册页中see also找到该系统调用,即read。我们用man 2 read查看read的手册页。

man手册写得非常清楚,在此不再赘述。我们试图写一个cat命令来演示open和read的使用。

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

void main(int ac, char *av[]){
    int fd;
    char buf[32];
    int n;

    if(ac!=2){
        printf("Usage: CMD filename\n");
        exit(1);
    }

    if((fd=open(av[1], O_RDONLY))==-1){
        perror("cannot open");
        exit(1);
    }
    
    while((n=read(fd, buf, sizeof(buf)))>0){
        printf("%.*s", n, buf); 
    }

    close(fd);
}

运行结果

image-20211130211933391

设备也是文件

在Unix/Linux上,设备也是文件。这些设备文件存放于/dev目录下。对于输出设备其属性是只写的,对于输入设备其属性是只读的,对于既能输入也能输出的设备——比如终端——其属性是可读可写的。

我们以终端设备为例。通过tty命令来查看当前终端对应的文件,比如我这台机器的结果是这样的。

image-20211130212049505

说明/dev/pts/0是我当前的设备文件。我们可以试着以只写方式将其打开,并将内容写入。

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>

void main(){
    int fd;

    if((fd=open("/dev/pts/0", O_WRONLY))==-1){
        perror("cannot open");
        exit(1);
    }

    write(fd, "hello tty!\n", 11);

    close(fd);
}

运行结果

image-20211130212634863

如果你的个人计算机上有鼠标的话,你可以试试以下代码:

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>

void main(){
    int fd;
    char buf[512];
    int n;
    int i;

    if((fd=open("/dev/input/mice", O_RDONLY))==-1){
        perror("cannot open");
        exit(1);
    }

    while((n=read(fd, buf, sizeof(buf)))>0){
        for(i=0;i<n;i++){
            printf("%d", buf[i]);
        }
        printf("\n");
    }

    close(fd);
}

运行结果如下,在这里,因为一般用户不具备访问鼠标文件的权限,因此我们用su命令切换成超级用户root。

        printf("%d", buf[i]);
    }
    printf("\n");
}

close(fd);

}


运行结果如下,在这里,因为一般用户不具备访问鼠标文件的权限,因此我们用`su`命令切换成超级用户root。

<img src="https://gitee.com/aipolaris/picture/raw/master/img/image-20211130213127338.png" alt="image-20211130213127338" style="zoom:80%;" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值