1.11k likes | 1.25k Views
第 5 章. 文件I/O操作. 本章重点. Linux 系统的文件属性 不带缓存的文件 I/O 操作 基于流的文件 I/O 操作 特殊文件的操作. 2. 5.1 Linux 系统文件和文件系统. 文件是指有名字的一组相关信息的集合。文件系统是操作系统用来管理和保存文件的。 Linux 的文件系统目录结构是属于 树形结构 。因此,文件系统的开始是由根目录( / )开始往下长,就像一棵倒长的树一样。 Linux 把不同文件系统挂载 (mount) 在根文件系统下不同的子目录(挂载点)上,用户可以从根( / )开始方便找到存放在不同文件系统的文件。
E N D
第 5 章 文件I/O操作
本章重点 • Linux系统的文件属性 • 不带缓存的文件I/O操作 • 基于流的文件I/O操作 • 特殊文件的操作 2
5.1 Linux系统文件和文件系统 • 文件是指有名字的一组相关信息的集合。文件系统是操作系统用来管理和保存文件的。 • Linux 的文件系统目录结构是属于树形结构。因此,文件系统的开始是由根目录(/)开始往下长,就像一棵倒长的树一样。 • Linux把不同文件系统挂载(mount)在根文件系统下不同的子目录(挂载点)上,用户可以从根(/)开始方便找到存放在不同文件系统的文件。 • 在安装Linux系统时,系统会建立一些默认的目录,而每个目录都有其特殊功能。
5.1 Linux系统文件和文件系统 linux文件系统结构
5.1.1 Linux文件类型 • Linux文件类型分为普通文件、目录文件、符号链接(symbolic link)文件、设备文件、管道文件、socket文件等。 • 例5.1 设计一个程序,要求列出当前目录下的文件信息,以及系统“/dev/sda1”和“/dev/lp0”的文件信息。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-1.c
5.1.1 Linux文件类型 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-1.c –o 5-1 • 步骤3 运行程序。 [root@localhost root]#./5-1 列出当前目录下的文件信息: -rwxr-xr-x 2 root root 12255 7月 11 04:22 file01 -rw-r--r-- 1 root root 786 7月 11 04:21 file02 drwxr-xr-x 7 root root 4096 5月 11 04:21 file03 lrwxrwxrwx 1 root root 11 7月 11 04:20 file04-> etc/passwd -rwxr-xr-x 2 root root 12255 7月 11 04:22 file05 列出“/dev/sda1”的文件信息: brw-rw---- 1 root disk 8, 1 2003-01-30 /dev/sda1 列出“/dev/ lp0”的文件信息: crw-rw---- 1 root lp 6, 0 2003-01-30 /dev/lp0
5.1.1 Linux文件类型 • 用ls命令长列表显示文件类型含义如表5.1所示
5.1.1 Linux文件类型 • system函数说明 :
5.1.2 文件权限 • Linux系统是一个多用户系统。为了保护系统中文件的安全,Linux统对不同用户访问同一文件的权限做了不同的规定。 • 对于Linux系统中的文件来说,它的权限可以分为四种:可读取(Read)、可写入(Write)、可执行(eXecute)和无权限,分别用r、w、x和-表示。 • 例5.2 设计一个程序,要求把系统中“/etc”目录下的passwd文件权限,设置成文件所有者可读可写,所有其他用户为只读权限。 10
5.1.2 文件权限 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-2.c 程序代码如下: /*5-2.c 设置“/etc/passwd”文件权限*/ #include<sys/types.h> /*文件预处理,包含chmod函数库*/ #include<sys/stat.h> /*文件预处理,包含chmod函数库*/ int main () /*C程序的主函数,开始入口*/ { chmod("/etc/passwd",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); /*S_IRUSR表示拥有者具有读权限,S_IRGRP表示组内人具有读权限,S_IROTH表示其他人具有读权限*/ return 0; }
5.1.2 文件权限 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-2.c –o 5-2 • 步骤3 运行程序。 [root@localhost root]#./5-2 如果程序没有出错,此时系统中没有任何显示。 • 步骤4 使用ls –l命令来查看“/etc/passwd”文件的权限。 [root@localhost root]#ls -l /etc/passwd -rw-r--r-- 1 root root 1635 3月 15 00:20 /etc/passwd
5.1.2 文件权限 • 例5.3 设计一个程序,要求设置系统文件与目录的权限掩码。 • 分析 先将系统的权限掩码改为0666(指建立文件时预设的权限为0000),然后调用touch命令新建文件liu1;接着将系统的权限掩码设为0444(指建立文件时预设的权限为0222),然后调用touch命令新建文件liu2;最后调用ls命令观察这些文件的权限,是否按题意的要求已实现。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-3.c
5.1.2 文件权限 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-3.c –o 5-3 • 步骤3 运行程序。 [root@localhost root]#./5-3 系统原来的权限掩码是:123 系统新的权限掩码是:666 创建了文件liu1 系统原来的权限掩码是:666 系统新的权限掩码是:444 创建了文件liu2 ---------- 1 root root 0 7月 17 20:59 liu1 --w--w--w- 1 root root 0 7月 17 20:59 liu2
5.1.2 文件权限 • 上述结果说明如下: • 1)先将系统的权限掩码为0666,所以新建的文件liu1访问权限为0000,即“--------”。 • 2)再将系统的权限掩码为0444,所以新建的文件liu2访问权限为0222,即“--w--w--w-”。 • 语句system("touch liu1")的作用是调用system函数来运行shell命令“touch liu1”,touch命令的作用是更改时间标记,若文件不存在,则新建文件。 • 运行一次此例的程序后,修改源程序中的掩码后,再次编译运行,文件“liu1”和“liu2”的权限并不改变。因为如果文件已经存在,touch只修改时间标记。如果要再次验证新的掩码,需要再次运行程序前删除原来的文件。
5.1.2 文件权限 • chmod函数说明 :
5.1.2 文件权限 • mode参数说明 :
5.1.2 文件权限 • umask函数说明 : 思考题:设计一个程序,要求Linux系统新建的文件权限是0400,提示umask中的参数设置为0266。
5.1.3 Linux文件的其他属性 • 在Linux系统中,文件还有创建时间、大小等其他的属性。这些信息定义在stat结构体中。 • stat结构的定义如下: struct stat { dev_t st_dev; /*文件所在设备的ID*/ ino_t st_ino; /*索引节点号*/ mode_t st_mode; /*文件保护模式*/ nlink_t st_nlink; /*文件的连接数(硬连接)*/ uid_t st_uid; /*用户ID*/ gid_t st_gid; /*组ID*/ dev_t st_rdev; /*设备号,针对设备文件*/ off_t st_size; /*文件字节数*/ unsigned long st_blksize; /*系统块的大小*/ unsigned long st_blocks; /*文件所占块数 */ time_t st_atime; /*最后一次访问时间*/ time_t st_mtime; /*最后一次修改时间*/ time_t st_ctime; /*最后一次改变时间(指属性)*/ };
5.1.3 Linux文件的其他属性 • 要获得文件的其他属性,可以使用stat函数、fstat或lstat函数。 • fstat函数返回与打开的文件描述符相关的文件的状态信息,该信息将会写到stat结构中,stat的地址以参数形式传递给fstat。 • stat和lstat返回的是通过文件名查到的状态信息。它们的结果基本一致,但当文件是一个符号链接时,lstat返回的是该符号链接本身的信息,而stat返回的是该链接指向的文件的信息。
5.1.3 Linux文件的其他属性 • 例5.4 设计一个程序,应用系统函数stat获取系统中“/etc”目录下的passwd文件的大小。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-4.c 程序代码如下: /*5-4.c 获取“/etc/passwd”文件的大小*/ #include<unistd.h> /*文件预处理,包含stat函数库*/ #include<sys/stat.h> /*文件预处理,包含stat函数库*/ int main () /*C程序的主函数,开始入口*/ { struct stat buf; stat("/etc/passwd",&buf); printf("“/etc/passwd”文件的大小是:%d\n",buf.st_size); return 0; }
5.1.3 Linux文件的其他属性 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-4.c –o 5-4 • 步骤3 运行程序。 [root@localhost root]#./5-4 “/etc/passwd”文件的大小是:1635 由结果可知,运行此程序后,在没有打开文件“/etc/passwd”的情况下,通过stat函数取得了文件大小。
5.1.3 Linux文件的其他属性 • stat函数说明 :
5.1.3 Linux文件的其他属性 • 思考题: • 设计一个程序,要求判断“/etc/passwd”的文件类型。 使用st_mode属性,可以使用几个宏来判断:S_ISLNK(st_mode) 是否是一个连接,S_ISREG是否是一个常规文件S_ISDIR是否是一个目录,S_ISCHR是否是一个字符设备,S_ISBLK是否是一个块设备,S_ISFIFO是否是一个FIFO文件,S_ISSOCK是否是一个SOCKET文件。 • 设计一个程序,要求打开文件“/etc/passwd”,判断它的最后一次访问时间。 • 应用命令:man fstat,查阅文件状态相关的应用: int stat(const char *file_name, struct stat *buf); int fstat(int filedes, struct stat *buf); int lstat(const char *file_name, struct stat *buf); • 请查阅相关资料,如何利用结构体struct stat成员获取文件类型。
5.2 不带缓存的文件I/O操作 • Linux系统把目录、设备等的操作,都等同于文件的操作。Linux系统通过一个文件描述符来进行区分引用。文件描述符是一个非负的整数,是一个索引值,指向内核中每个进程打开文件表。 • Linux系统中,基于文件描述符的文件操作主要有:不带缓存的文件I/O操作和带缓存的文件流I/O操作。 • 不带缓存的文件I/O操作是系统调用或API的I/O操作,由操作系统提供的,符合POSIX标准,设计的程序能在各种支持POSIX标准的操作系统中方便地移植。 • 不带缓存的文件I/O程序不能移植到非POSIX标准的系统(如Windows系统)上去,但是在嵌入式程序设计、TCP/IP的Socket套接字程序设计、多路I/O操作程序设计等方面应用广泛。
5.2.1 文件的创建 • 在Linux C程序设计中,创建文件可以调用creat函数。 • 例5.5 设计一个程序,要求在“/root”目录下创建一个名称为“5-5file”的文件,并且把此文件的权限设置为所有者具有只读权限,最后显示此文件的信息。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-5.c
5.2.1 文件的创建 • 程序代码如下: /*5-5.c程序:在“/root”目录下创建一个名称为“5-5file”的文件*/ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main() { int fd; fd=creat("/root/5-5file",S_IRUSR); /*所有者具有只读权限 */ system("ls /root/5-5file -l");/*调用system函数执行命令ls显示此文件的信息 */ return 0; }
5.2.1 文件的创建 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-5.c –o 5-5 • 步骤3 运行程序。 [root@localhost root]#./5-5 -r-------- 1 root root 0 7月 17 22:00 /root/5-5file
5.2.1 文件的创建 • creat函数说明 : 思考题:设计一个程序,要求在“/mnt”目录下创建一个名称为“usb”的文件,编辑、调试成功后,运行两次是否有问题?为什么?
5.2.2 文件的打开和关闭 • 文件的打开可以用open函数,即使原来的文件不存在,也可以用open函数创建文件。在打开或者创建文件时,可以指定文件的属性及用户的权限等参数。 • 关闭一个打开的文件,用close函数。当一个进程终止时,它所有已打开的文件都由内核自动关闭。 • 例5.6 设计一个程序,要求在“/root”下以可读写方式打开一个名为“5-6file”的文件。如果该文件不存在,则创建此文件;如果存在,将文件清空后关闭。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-6.c
5.2.2 文件的打开和关闭 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-6.c –o 5-6 • 步骤3 运行程序。 [root@localhost root]# ./5-6 打开(创建)文件“5-6file”,文件描述符为:3 -rw------- 1 root root 0 7月 17 22:07 /root/5-6file
5.2.2 文件的打开和关闭 • open函数说明:
5.2.2 文件的打开和关闭 • flags参数说明 :
5.2.2 文件的打开和关闭 • close函数说明 : 思考题:设计一个程序,要求在“/mnt”目录下打开名称为“usb”的文件。如果该文件不存在,则创建此文件;如果存在,将文件清空后关闭。
5.2.3 文件的读写操作 • 文件读写操作中,经常用到的函数是read、write和lseek。 • read函数用于将指定的文件描述符中读出数据。 • write函数用于向打开的文件写数据,写操作从文件当前位置开始。 • lseek函数用于在指定的文件描述符中将文件指针定位到相应的位置。
5.2.3 文件的读写操作 • 例5.7 程序从终端读数据再写回终端。 #include <unistd.h> #include <stdlib.h> int main(void) { char buf[80]; int n; n = read(STDIN_FILENO, buf,80); if (n < 0) { perror("read STDIN_FILENO"); exit(1); } write(STDOUT_FILENO, buf, n); printf("\n"); return 0; }
5.2.3 文件的读写操作 • 例5.8 设计一个C程序,完成文件的复制工作。要求通过使用read函数和write函数复制“/etc/passwd”文件到目标文件中,目标文件名在程序运行时从键盘输入。 • 分析 由用户输入目标文件的名称,接着打开源文件“/etc/passwd”及目标文件,利用read函数读取源文件的内容,再利用write函数将读取到的内容写入至目标文件。 • 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-8.c
5.2.3 文件的读写操作 • 步骤2 用gcc编译程序。 [root@localhost root]#gcc 5-8.c –o 5-8 • 步骤3 运行程序。 [root@localhost root]#./5-8 请输入目标文件名:5-8test 复制"/etc/passwd"文件为"5-8test"文件成功!
5.2.3 文件的读写操作 • read函数说明 :
5.2.3 文件的读写操作 • write函数说明 :
5.2.3 文件的读写操作 思考题: 1.设计一个程序,使用read函数从源文件读取数据,再用write函数写入到目标文件,源文件名和目标文件名都由键盘输入。 2.设计一个程序,要求在“/mnt”目录下,打开名称为“usb”的文件,如果该文件不存在,则创建此文件,如果已存在,把字符串“usb作为优盘设备文件”写入此文件后关闭。
5.2.3 文件的读写操作 • 如果在open一个设备时指定了O_NONBLOCK标志,read/write就不会阻塞。以read为例,如果设备暂时没有数据可读就返回-1,同时置errno为EWOULDBLOCK,表示本来应该阻塞但事实上并没有阻塞而是直接返回错误,通过轮询方式试着再读一次,而不是阻塞在这里死等,这样可以同时监视多个设备。 • 在使用非阻塞I/O时,通常不会在一个while循环中一直不停地查询(这称为Tight Loop),而是每延迟等待一会儿来查询一下,以免做太多无用功,在延迟等待的时候可以调度其它进程执行。
5.2.3 文件的读写操作 • 例5.9 以下是一个非阻塞I/O的例子,程序打开当前终端文件/dev/tty,在打开时指定O_NONBLOCK标志。程序运行时每隔一定时间(6秒)等待用户从终端输入,等待30秒,每次等待时屏幕都有提示“”,30秒后程序继续执行主程序,输出以下图形后结束。
5.2.3 文件的读写操作 • 程序运行时,轮询等待用户的输入,等待期间如有输入,即转入主程序执行,如没有输入,30秒后执行主程序,执行结果如下: [root@localhost root]# ./5-9 try again try again try again try again try again timeout * * * * * * * * * * * * * * * [root@localhost root]# ./5-9 try again ls -l try again ls -l * * * * * * * * * * * * * * *