unix环境高级编程第三章笔记

时间:2023-03-09 09:14:36
unix环境高级编程第三章笔记

文件描述符

1、文件描述符的概念

对于内核而言,所有打开的文件都会用一个文件描述符来引用,打开或和创建一个新文件的时候,内核会给进程返回一个文件描述符,而当使用read write时,可以使用这个文件描述符来代替文件。

UNIX系统下,使用0 1 2 来分别与标准输入 标准输出 标准出错输出相关联。

2、文件操作有关的函数

open函数

  /*fcntl.h定义了很多宏和fcntl函数原型*/
#include <fcntl.h>
int open(const char *pathname ,int oflag,/*mode_t mode*/); /*例子*/
int fd = open("a.txt",O_RDONLY)
if(fd ==-1)
return -1;

函数的参数:

pathname:需要打开或者创建文件的路径

oflag:此参数的多个选项

mode:当oflag = O_CREAT 时,需要额外指定的参数,用来表示新创建文件的访问权限位如0777

函数的返回值:

成功返回文件描述符,失败返回 -1

oflag可选的选项:

O_APPEND 每次写都追加到文件的末端

O_CREAT 如果文件不存在就创建一个

O_TRUNC 如果此文件存在而且是以读和写的形式打开,就把他的文件长度截断到0

O_DSYNC 使每次write等待物理io操作完成,但是如果写操作不影响读取则不等待

O_RSYNC 让每一个read操作等待,直到对文件同一部分的写操作完成

O_RDONLY O_WRONLY O_RDWR 只读打开 只写打开 读写打开

open返回的文件描述符一定是最小的文件描述符

close函数

  #include<unistd>
int close(int fd);

返回值:

成功返回0,失败返回-1

关闭一个文件还会清除这个文件的记录锁

当一个进程终止时,内核会自动关闭进程打开的文件

lseek函数

每个打开的文件都有其文件偏移量,通常是一个非负数,读写操作都会从这个当前偏移量开始,每读或者写一字节,文件偏移量后移一字节

  #include<unist.h>
off_t lseek (int fd, off_t offset,int whence);

参数:

fd: 需要被修改文件偏移量的文件描述符

whence:

SEEK_SET 文件偏移量设为距离文件开始处offset字节

SEEK_CUE 文件偏移量设为当前值处offset字节

SEEK_END 文件偏移量设为距离文件末尾处offset字节

返回值:

成功返回新的文件偏移量,出错就返回 -1

如果文件描述符引用的是一个管道 FIFO或网络套接字,lseek返回-1,并将errno设置为ESPIPE

lseek把当前文件的偏移量记录在内核,不会引起io操作

  #include<fcntl>
#include"apue.h"
char buf[] = "asdfg"
char buf[] = "ASDFG"
int main ()
{
int fd;
if ((fd = open ("a.txt",O_WDWR | O_CREAT,FILE_MODE))<0)
err_sys("creat err");
if(write(fd,buff1, 5) != 5)
err_sys("buff1 write err");
/*会制造一个空洞*/
if(lseek(fd,10 ,SEEK_SET) == -1)
err_sys ("lseek err ");
if(write(fd,buff2, 5) != 5)
err_sys("buff2 write err");
exit(0);
}

read函数

调用read可以从打开的文件中读取数据

  #include <unistd.h>
ssize_t read(int fd , void *buff ,size nbyte);

参数:

fd:文件描述符

buff:文件要读到哪里去

nbyte:文件要读多少字节

返回值:

读取成功则返回读取的字符,失败就返回-1

当没有读完要求的字节就已经到达文件末尾,直接返回已经读取的文件数。此时文件偏移量在末尾,继续调用read只会返回0

write函数

调用write函数向打开的文件写数据

  #include<unistd.h>
size_t write(int fd , const void *buff ,size nbyte);

参数:

fd:文件描述符

buff:文件要把哪里的数据写入

nbyte:文件要写多少字节

返回值:

写入成功就返回已经写入的字节数,失败就返回-1

返回值一般要和 nbyte 相同,不然就显示出错,出错的原因一般是:磁盘已经写满,超过了一个给定进程的文件长度限制

io的效率

  #include "apue.h"

  #define	BUFFSIZE	4096

  int
main(void)
{
int n;
char buf[BUFFSIZE];
while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error"); exit(0);
}

上述函数把标准输入复制到标准输出,但对于不同的BUFFSIZE,程序运行的时间是不一样的

一般来讲,系统cpu时间随着BUFFSIZE的增大而减小(越来越快),而最大值出现在4096,继续增大不会对系统cpu时间产生更大的影响

文件共享

内核用三种数据结构来表示打开的文件:

  • 每个进程都会在进程表中有一个记录项,记录项中包含有一张这个进程打开的文件表,文件表中每一项包含文件描述符,和一个指向文件表项的指针
  • 内核为所有已经打开的文件维持了一个文件表,每个文件表项包括:文件状态标志 当前文件偏移量 指向该文件v节点表项的指针
  • v节点表项包含了v节点信息 i节点信息 当前文件长度

unix环境高级编程第三章笔记

如果有两个独立进程各自打开了同一个文件,打开该文件的每一个进程都会得到一个文件表项:每个进程都有独立的文件偏移量

每个文件都只有一个v节点表项

也有共享同一个文件表项的情况:fork()之后的父子进程

原子操作

原子操作是独立的一个个体,要么全都完成,要么一点都不做。

dup和dup2函数

  #include<unist.h>
int dup(int fd);
int dup(int oldfd,int newfd);

dup函数:内核在进程中创建一个新的文件描述符,此描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。

dup2函数:用newfd参数指定新描述符的数值,如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。dup2函数返回的新文件描述符同样与参数oldfd共享同一文件表项。

sync fsync fdatasync函数

unix内核也存在一块缓冲区,被称为update的系统守护进程会周期性的调用sync,当数据写入文件时,内核把数据复制到其中一个缓冲区,如果该缓冲区满了,派入输出队列,没有满则等待

  #incude<unist.h>
/*把所有修改过的块缓冲区送到队列,不等待写磁盘操作结束*/
void sync;
/*只对单一文件有效果,等待写磁盘操作结束*/
int fsync(int fd);
/*只对数据有效,其他同fsync*/
int fdatasync(int fd);

/dev/fd

打开/dev/fd/n 和复制描述符n等效。