Linux--文件系统复习

来源:互联网 发布:电脑连接的常用端口 编辑:程序博客网 时间:2024/05/16 09:36

1.系统调用

所有的操作系统都提供多种服务的入口点,程序由此向内核请求服务。这些可直接进入内核的入口点被称为系统调用。

2.操作系统提供的服务

 进程控制

 文件系统控制

 内存管理

 网络管理

 用户管理

 3.  为什么用户程序不能直接访问内核提供的服务?

Linux中,为了更好地保护内核空间,程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数

4.是不是系统调用或库函数一定会执行成功?怎么知道执行成功或失败?执行失败了怎么办?

几乎所有的系统调用或库函数都会返回某种类型的状态值,以表明是否执行成功!

需要对状态值进行检查!!

需要对执行失败的情况进行处理,至少也要显示错误信息!!

5. 通过errno(具体出错原因的确定)

当系统调用执行失败,会设置全局整型变量errno的值,以表明具体的出错原因。

6.文件的概念

 “文件”这个名词不陌生,什么是文件?

 系统资源(内存、硬盘、一般设备、进程间通信的通道等)的一个抽象

 对系统资源进行访问的一个通用接口。

 采用这种“文件”的方式有什么好处?

对资源提供通用的操作接口,可以极大地简化系统编程接口的设计。

7.文件的类型

 常见的文件类型(可以通过文件来访问的系统资源)有:

 普通文件

一般意义上的文件,作为数据存储在磁盘中,可以随机访问文件的内容。Linux系统中的文件是面向字节的,文件的内容以字节为单位进行存储和访问。

 目录

目录是一种特殊的文件,目录可以像普通文件一样打开、关闭以及进行相应的操作。

 管道

 管道是Linux中的一种进程间通信的机制。

 设备文件

 设备文件没有具体的内容,对设备文件的读写操作实际上与某个设备的输入输出操作关联在一起。

 符号链接

符号链接的内容是指向另一个文件的路径。当对符号链接进行操作时,系统会根据情况将这个操作转移到它所指向的文件上去,而不是对它本身进行操作。

 Socket

 socket也是一种进程间通信的方式,与管道不同的是,它们可以在不同的主机上进行通信,也就是网络通信。

8.文件描述符

所有执行I/O操作的系统调用使用文件描述符来表示打开的文件。

 文件描述符是一个非负整数。

 文件描述符可以表示各种类型的打开的文件。

 对文件的操作只要使用文件描述符即可指定所操作的文件。

9.文件操作的一般过程

 打开文件,打开成功后,应用程序将获得文件描述符。

 应用程序使用文件描述符对文件进行读写等操作。

 全部操作完毕后,应用程序需要将文件关闭以释放用于管理打开文件的内存。

10.打开文件

 open()系统调用可以打开或创建一个文件。

#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);

Returns file descriptor on success, or 1 on error    

    各参数及返回值的含义如下:

 pathname:要打开或创建的文件名称。

 flags:标志位,指定打开文件的操作方式

 mode:指定新文件的访问权限(仅当创建新文件时才使用该参数)

 返回值:若成功返回文件描述符,否则返回-1并设置变量errno的值。

11.flag参数取值

 基本取值:

 O_RDONLY:以只读方式打开文件。

 O_WRONLY:以只写方式打开文件。

 O_RDWR:以读写方式打开文件。

注意:上述三个常量必须指定且只能指定一个。

 可选取值(部分)

 O_CREAT:如果被打开的文件不存在,则自动创建这个文件。

 O_EXCL:如果O_CREAT标志已经使用,那么当由pathname参数指定的文件已经存在时,open返回失败。

 O_TRUNC:如果被打开的文件存在并且是以可写的方式打开的,则清空文件原有的内容。

 O_APPEND:新写入的内容将被附加在文件原来的内容之后,即打开后文件的读写位置被置于文件尾。

12.mode参数取值

每个文件有9个权限位(permission bits),这些权限位构成了mode的取值:

 

 

 

13.读取文件

read()系统调用从打开的文件中读取数据。

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

Returns number of bytes read, 0 on EOF, or 1 on error    

    各参数及返回值的含义如下:

 fd:要读取的文件的描述符。

 buf:读取到的数据要放入的缓冲区。

 count:要读取的字节数。

 返回值:若成功返回读到的字节数,若已到文件结尾则返回0,若出错则返回-1并设置变量errno的值。

注意:

1. 这里的size_t是无符号整型,ssize_t是有符号整型。

2. buf指向的内存空间必须事先分配好。

14.read()可能出现的情况

 使用read()时,可能遇到哪些情况?

 返回值等于count

 返回一个大于0小于count的值

 返回0

 阻塞

 返回-1errno的值为EINTR

 返回-1errno的值为EAGAIN

 

 15. 文件以阻塞方式进行操作

ssize_t ret;

while (len != 0 && (ret = read (fd, buf, len)) != 0) {

    if (-1 == ret) {

        if (EINTR == errno)

            continue;

        perror("read");

        break;

    }

    len -= ret;

    buf += ret;

}

其中:len初始值为要读取的字节数。

 

16. 文件以非阻塞方式进行操作

ssize_t nr;//无符号整形nr

start:

nr = read (fd, buf, len);//fd文件描述符,buf将读取的数据放入缓冲区,len读取的字节数

 

if (-1 == nr) {                     //-1代表读取文件错误

    if (EINTR == errno)

        goto start;

    if (EAGAIN == errno)

        /* 处理其他事务,在恰当的时候再调用read */

    else

        /* 有错误发生,处理错误 */

}

    errno如果是EINTR,可以再次进行读操作;如果是EAGAIN,表示要读取的文件现在没有可供读取的数据。

 

 

17.写文件(write)

 write()系统调用向打开的文件写数据。

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

Returns number of bytes written, or 1 on error    

    各参数及返回值的含义如下:

 fd:要写入的文件的描述符。

 buf:要写入的数据所存放的缓冲区。

 count:要写入的字节数。

 返回值:若成功返回已写的字节数,出错则返回-1并设置变量

 

18.write()可能出现的情况

 使用write()时,可能遇到哪些情况?

 返回值等于count

 返回一个大于0小于count的值

 阻塞

 返回-1errno的值为EINTR

 返回-1errno的值为EAGAIN

 返回-1errno的值为EBADF

 返回-1errno的值为EFAULT

 返回-1errno的值为EPIPE

 

19.write()使用示例代码

   与读操作一样,在调用write函数向文件写数据时,并不能保证一次写完所提供的全部数据,并且也会有各种各样的异常情况,这就需要采取措施以保证数据的可靠写入。

ssize_t ret;

while (len != 0 && (ret = write (fd, buf, len)) != 0) {   //ret返回值,fd文件描述符

    if (-1 == ret) {

        if (EINTR == errno)

            continue;

        perror ("write");

        break;

    }

    len -= ret;

    buf += ret;

}

 

20.关闭文件(close)

close()系统调用关闭一个打开的文件。

#include <unistd.h>

int close(int fd);

Returns 0 on success, or 1 on error    

    各参数及返回值的含义如下:

 fd:要关闭的文件的描述符。

 返回值:若成功返回0,出错则返回-1

注:当一个进程终止时,内核会自动关闭它所有打开的文件。

 

21.lseek()改变文件偏移量(File Offset)

lseek系统调用可以改变文件偏移量(File Offset)文件偏移量是一个整数,表示距文件起始处的字节数。

#include <sys/types.h>

#include <unistd.h>

off_t lseek(int fildes, off_t offset, int whence);

Returns new file offset if successful, or 1 on error

    其中,参数whence必需是以下三个常量之一:

SEEK_SET:将文件偏移量设置在距文件开始处offset个字节。

SEEK_CUR:将文件偏移量设置在其当前值加offsetoffset可正可负。

SEEK_END:将文件偏移量设置为文件长度加offsetoffset可正可负。

 

22.文件描述符与打开文件关系

 从以上对文件的各种操作中,是不是可以推测出文件描述符和打开的文件是一一对应的关系?

为了搞清楚上述问题,首先需要了解内核如何表示打开的文件          

    内核使用三种数据结构表示一个打开的文件!

 文件描述符表(每个进程都有)

    (1) 文件描述符标志(file descriptor flags),如:close_on_exec

    (2) 指向一个文件表项(file table entry)的指针。

     打开文件表(open file table)

    (1) 文件状态标志(file status flags),如读、写、非阻塞等。

    (2) 当前文件偏移量(file offset)

    (3) 文件访问模式(read-only, write-only, or read-write)

    (4) 指向i-node表项的指针。

   文件系统i-node

    (1) 文件类型(regular file, socket, or FIFO)和权限。

    (2) 指向文件表的指针。

    (3) 文件的属性(file size)

 

 

 

23.文件操作进一步说明

 有了以上知识后,再看看文件操作

 每次完成write后,在文件表项中的当前文件偏移量(file offset)增加所写的字节数。如果当前文件偏移量超过了当前文件长度(file size),则在i-node(文件系统表)表项中的文件长度被设置为当前文件偏移量(也就是说文件加长了)

 如果用O_APPEND标志打开一个文件,则每次对这种具有添写标志的文件执行写操作时,文件表项中的当前文件偏移量首先被设置为i-node表项中的文件长度,这就使得每次写的数据都添加到文件的当前尾端处。

 若一个文件用lseek定位到文件当前的尾端,则文件表项中的当前文件偏移量被设置为i-node表项中的当前文件长度。

 lseek只修改文件表项中的当前文件偏移量,没有进行任何I/O操作。    

 

24.原子操作

例子 :如何向文件结尾添加新的内容?可不可以采用这种方式?

lseek(fd, 0L, SEEK_END);

write(fd, buf, 100);

 

 方案一

    打开文件时设置O_APPEND选项,这就使内核每次对这种文件进行写之前,都将当前文件偏移量设置为该文件的尾端。于是,每次写之前就不需要调用lseek函数了。    

 方案二

 pread()pwrite()

#include <unistd.h>

ssize_t pread(int fd, void *buf, size_t count, off_t offset);

ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);    

    调用pread相当于顺序调用lseekread,但是又与这种顺序调用不同,不同之处在于调用pread时,无法中断其定位操作和读操作。pwrite与之类似(lseekwrite)

 

一般而言,原子操作指的是由多步组成的操作。如果该操作原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。

 

25.I/O缓冲

C标准库提供了操作文件的标准I/O函数库,与系统调用相比,主要差别是实现了一个跨平台的用户态缓冲的解决方案。

 

26.  为了提高系统进行I/O操作的效率

    系统调用要请求内核的服务,会引发CPU模式的切换,期间会有大量的堆栈数据保存操作,开销比较大。如果频繁地进行系统调用,会降低应用程序的运行效率。有了缓冲机制以后,多个读写操作可以合并为一次系统调用,减少了系统调用的次数,将大大提高程序的运行效率。

    所谓的标准I/O函数实际上是对底层系统调用的封装,最终读写设备或文件的操作仍需调用系统I/O函数来完成。

 

 标准I/O函数也是使用文件描述符操作文件?

    标准I/O函数并不直接操作文件描述符,而是使用文件指针。文件指针和文件描述符是一一对应的关系,这种对应关系由标准I/O库自己内部维护。文件指针指向的数据类型为FILE型,但应用程序无须关心它的具体内容。

    在标准I/O中,一个打开的文件称为流(stream),流可以用于读(输入流)、写(输出流)或读写(输入输出流)。每个进程在启动后就会打开三个流,分别对应:stdin(标准输入流)stdout(标准输出流)以及stderr(标准错误输出流)

 

 27.    fopen用于打开一个标准I/O

#include <stdio.h>

FILE *fopen(const char *path, const char *mode);

fopen打开由path指定的文件,并把它与一个文件流关联起来。mode参数指定文件的打开方式。

fopen执行成功返回一个非空的FILE*指针,失败时返回NULL

28. Mode取值

"r""rb":以只读方式打开。

 "w""wb":以只写方式打开,并把文件长度截短为零。

 "a""ab":以写方式打开,新内容追加在文件尾。

 "r+""rb+""r+b":以更新方式打开(读和写)

 "w+""wb+""w+b":以更新方式打开,并把文件长度截短为零。

 "a+""ab+""a+b":以更新方式打开,新内容追加在文件尾。

 

注:字母b表示文件是一个二进制文件而不是文本文件。

 

29. fread从文件流中读取数据

 fread()用于从一个文件流里读取数据。

#include <stdio.h>

size_t fread(void* ptr, size_t  size, size_t  nmemb, FILE * stream);

    

    数据从文件流stream读到ptr指向的数据缓冲区里。size参数指定每个数据记录的长度,nmemb给出要传输的记录的个数  函数的返回值是成功读到数据缓冲区里的记录的个数(不是字节数)

30.fwrite

    fwrite()从指定的数据缓冲区里取出数据记录,并把它们写到输出流中。它的返回值是成功写入的记录个数。函数原型如下:

#include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb,

FILE *stream);

 

31.fread/fwrite常见用法

这两个函数主要执行二进制I/O操作。

读或写一个二进制数组

float data[10];

if (fwrite(&data[2], sizeof(float), 4, fp) != 4)

    err_sys("fwrite error");

 读或写一个结构

struct {

      short   count;

      char    name[NAMESIZE];

    } item;

if (fwrite(&item, sizeof(item), 1, fp) != 1)

    err_sys("fwrite error");

注意:使用二进制I/O的基本问题是,它只能用于读在同一个系统上已写的数据。

 

32.fclose(关闭文件流)

fclose()函数用于关闭指定的文件流。

#include <stdio.h>

int fclose(FILE *fp);

 

 

 

33.fflush

 fflush()用于把文件流里所有未写出的数据立即写出。

#include <stdio.h>

int fflush(FILE *stream);  

使用这个函数可以确保在程序继续执行之前重要的数据都已经被写到磁盘上。

 调用fclose函数隐含执行了一次fflush操作,所以不必在调用fclose之前调用fflush

 

34.fseek

fseek()用于在文件流里为下一次读写操作指定位置。

#include <stdio.h>

int fseek(FILE *stream, long offset, int whence);  

offsetwhence参数的含义和取值与lseek函数完全一样,但lseek返回的是一个off_t数值,而fseek返回的是一个整数:0表示成功,-1表示失败并设置errno指出错误。

 

35.fgetc(),getc(),getchar()

fgetc()从文件流里取出下一个字节并把它作为一个字符返回。当它到达文件结尾或出现错误时,返回EOFgetc()fgetc()一样,但它有可能被实现为一个宏。getchar()相当于getc(stdin)

#include <stdio.h>

int fgetc(FILE *stream);

int getc(FILE *stream);

int getchar(void);

 

36.fputc(),putc(),putchar()

fputc()把一个字符写到一个输出文件流中,它返回写入的值,如果失败,则返回EOF。类似fgetc()getc()putc()的作用也相当于fputc(),但它可能被实现为一个宏。putchar()相当于putc(c, stdout),它把单个字符写到标准输出。

#include <stdio.h>

int fputc(int c, FILE *stream);

int putc(int c, FILE *stream);

int putchar(int c);

 

注意:putchargetchar都是把字符当作int类型而不是char类型来使用的,这就允许文件结尾EOF取值为-1

 

原创粉丝点击