通常,我们在输入数据或输出数据的设备为键盘或者显示器。当然,我们比较熟悉的输入输出,可能就是对于文件的操作,还有直接从终端输出,显示到显示器上。在C语言中,我们使用fopen,fclose,fread,fwrite对文件进行相应的操作。由于操作系统内核的不同,在linux系统下,我们不仅仅可以使用C库里边的那些函数,还使用open,close,read,write对文件进行相应的操作,这些都是系统调用的函数。它们之间有联系也有区别,现在我们分析一下吧。
C库:
(1)FILE *fopen(const char *path, const char *mode);
(2)int fclose(FILE *fp);
(3)size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
(4)size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
系统调用:
(1)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode); //flags表示打开的方式,主要有O_RDONLY,O_WRONLY,O_CREAT,O_EXCL等
int creat(const char *pathname, mode_t mode); //如果打开不成功的话,就自己创建一个
我们看出,使用open函数的时候,调用的是系统的函数,而上述fopen是使用C库里边的函数。还有一点是open的返回值是整形,这也是与fopen不一样的。我们使用fopen的时候,它是返回一个file*,在这里,open的返回值是整形,它代表文件描述符。即唯一标识打开文件的信息。文件描述符是什么呢。我们之前在task_struct中介绍过PCB的基本信息。简单了解了PCB结构体中的信息后,我们发现在task_struct结构体中有一个files_struct结构体就是用于存放打开文件的一系列信息。
struct files_struct {atomic_t count; bool resize_in_progress;wait_queue_head_t resize_wait;struct fdtable __rcu *fdt; struct fdtable fdtab; spinlock_t file_lock ____cacheline_aligned_in_smp;unsigned int next_fd; unsigned long close_on_exec_init[1 ];
需要关闭的文件描述符初值集合 unsigned long open_fds_init[1 ];
的文件描述符屏蔽字 unsigned long full_fds_bits_init[1 ];struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
FILE的结构
struct _iobuf {char *_ptr; int _cnt;char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname;};
typedef struct _iobuf FILE;
可以看到,files_struct中有一个文件描述符表,用来存放文件描述符。我们对于程序启动时默认会打开三个文件:stdin,stdout,stderr,它们的文件描述符分别表示:0,1,2。头文件unistd.h中有如下宏:
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
总结一下:
每个进程在linux内核中都有一个描述进程信息的结构体,称为task_struct。而描述进程的信息称为PCB,每个进程都有自己的PCB(进程控制块)。在task_struct结构体中,有一个指向files_struct的结构体指针,files_struct结构体中,描述了相应的文件描述符,I/O缓冲区,下一个文件描述符等。
说起库函数和系统调用,我们知道,最底层是硬件->驱动程序->操作系统->系统调用->shell外壳程序->库函数->用户程序;所以可以知道,库函数是在系统调用的基础上形成的,因此也知道open与fopen的关系了吧,fopen的底层也是调用了open的。
同理,看一下另外的read,write,close
ssize_t read (int fd, void *buf , size_t count);
ssize_t write (int fd, const void *buf , size_t count);
int close (int fd);
使用这些函数的时候,参数传入文件描述符来确定是哪个文件。