文章目录
- 文件描述符
- 相关系统调用
- 文件有关的系统调用
- 文件操作函数--creat函数
- 文件操作函数--open函数
- 文件操作函数--read函数
- 文件操作函数--write函数
- 文件操作函数--close函数
- 文件操作函数--lseek函数
- 缓冲区的大小对性能的影响
- 实验:调用系统函数,实现文件的复制功能
文件描述符
对于内核而言,所有打开文件都由文件描述符引用
- 文件描述符是一个非负整数
- 当
打开
一个现存文件或创建
一个新文件时,内核向进程返回一个文件描述符。
当读,写一个文件时,用open或creat返回的文件描述符标识该文件,然后将其作为参数传送给read或write.
- 按照惯例,linux shell使文件描述符
0
与进程的标准输入
相结合,文件描述符1
与标准输出
相结合,文件描述符2
与标准出错输出
相结合。 -
文件描述符的本质是一个非负整数,当打开一个文件时,该整数由系统来分配。文件描述符的范围是
0-OPEN_MAX
早期的UNIX版本OPEN_MAX=19
,即允许每个进程同时打开20
个文件,现在很多系统则将其增加至1024
. - 静态文件
-
未打开时,存放在块设备中的文件系统中的文件
-
- 动态文件
-
当打开文件时,Linux内核会在进程中建立一个该文件的数据结构,记录该文件已被打开,同时申请一段内存空间,再将静态文件的内容从块设备中读取到内存中特定的地址管理存放
-
当关闭动态文件时,close内部内核将内存中的动态文件更新至静态文件。
-
相关系统调用
-
系统调用
是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。
- 例如用户可以通过进程控制相关的
系统调用
来创建进程
、实现进程调度
、进程管理
等。
- 例如用户可以通过进程控制相关的
-
为什么用户程序不能直接访问系统内核提供的服务呢?
为了更好地保护内核空间,将程序的运行空间分为内核空间
和用户空间
(内核态和用户态)。 -
用户进程在通常情况下
不允许
访问内核数据,也无法使用
内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。 -
在有些情况下,用户空间的进程需要获得一定的系统服务(调用内核空间程序),这时操作系统就必须利用系统提供给用户的“特殊接口”——系统调用规定用户进程进入内核空间的具体位置
-
进行系统调用时,程序运行空间需要从用户空间进入内核空间,处理完后再返回到用户空间。
-
系统调用
按照按照功能逻辑
可分为:进程控制
进程间通信
文件系统控制
系统控制
存储管理
网络管理
socket控制
用户管理
系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。
在实际使用中程序员调用的通常是用户编程接口——API
系统命令相对API更高了一层,它实际上一个可执行程序,它的内部引用了用户编程接口(API)来实现相应的功能。
文件有关的系统调用
- creat
- open
- read
- write
- lseek
- close
- perror
#include <fcntl.h>
#include <unistd.h>int fd = creat(char *filename, mode_t mode)
int fd = open(char *name, int how)
ssize_t numread = read(int fd, void *buf, size_t qty)
ssize_t result = write(int fd, void *buf, size_t amt)
off_t oldpos = lseek(int fd, off_t dist, int base)
int resule = close(int fd)
文件操作函数–creat函数
- creat告诉内核创建一个名为filename的文件,如果这个文件不存在,就创建它,如果已存在,就把它的内容清空,把文件的长度设为0
- 如果内核成功地创建了文件,文件的许可权限将被设置为由mode指定的值。
如:fd = creat(“test”,0644);
名为test的文件权限被设为-rw-r--r--
文件操作函数–open函数
O_RDONLY
,O_WRONLY
,O_RDWR
分别对应:只读、只写、可读可写
文件操作函数–read函数
- 从文件描述符fd所指定的文件中读取qty个字节到buf所指向的缓冲区中,返回值为
实际读取
的字节数
- 1.如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
- 2.有多种情况可使实际读到的字节数少于要求读字节数:
- 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0 (文件尾端)。
- 当从终端设备读时,通常一次最多读一行
- 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
- 某些面向记录的设备,例如磁带,一次最多返回一个记录。
文件操作函数–write函数
- 把amt个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,
返回值
为实际写入的字节数
文件操作函数–close函数
- 当
不需要
再对文件进行读写操作时,就要把文件关闭。 - close会
关闭
进程和文件fd之间的连接。
文件操作函数–lseek函数
off_t oldpos = lseek(int fd, off_t dist, int base)
offset
可取负值,表示向前移动。
如:lseek(fd, -5, SEEK_CUR)
下述调用可将文件指针相对当前位置向前移动5个字节
缓冲区的大小对性能的影响
实验:调用系统函数,实现文件的复制功能
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>//用法:cp /etc/passwd /root/passwdmain(int argc, char *argv[])
{int read_fd, write_fd;unsigned char buf[128];int num_read;int write_result;//判断用户输入的字串是否为两个if(argc != 3){printf("Usage Error\n");exit(1);} write_fd = creat(argv[2], 0644);if(write_fd == -1){printf('create file error!\n');exit(1);}printf("write_fd=%d\n",write_fd);read_fd = open(argv[1],O_RDONLY); if(read_fd == -1){printf('open file error!\n');exit(1);}printf("read_fd=%d\n",read_fd);int x=0;while(1){num_read = read(read_fd, buf, 128);if(num_read==0){printf("all successful finished!\n");break;}if(num_read==-1){printf("read error!\n");break;}printf("num_read=%d\n",num_read);write_result = write(write_fd, buf, sizeof(buf));buf[0]='\0';printf("write_result=%d\n",write_result);}close(read_fd);close(write_fd);
}
-
具体思路如下:
- 1.实现复制第一步,我们需要打开我们要复制的文件,并获取其中的内容:
- (1)因此我们调用int fd=open(charname,int how)来打开文件
(2)再根据fd,使用read函数读取文件内容:read(int fd,voidbuf,size_t num);
(3)读取的内容我们读取在了buf字符数组中。
- (1)因此我们调用int fd=open(charname,int how)来打开文件
- 2.实现复制第二步,获取数据后我们要写入要复制到的文件中,而复制到的目标文件我们需要先创建,然后再写入数据:
- (1)创建文件:creat(char*filename,mode_t mode);
- (2)写入文件:write(int fd,void*buf,size_t amt);
- 3.由于我们不知道要复制的文件中有多少个字节,因此我们可以加一个循环,由read函数系统调用后若返回0,代表读取完毕,此时我们就可以退出循环了。
- 1.实现复制第一步,我们需要打开我们要复制的文件,并获取其中的内容:
-
实验运行结果:
我尝试将/etc/fstab文件copy到当前目录下来,并查看copy过来的文件,成功复制了过来,实验截图如下:
为再次确认其复制功能,我将/etc/passwd复制到wyl的家目录下,并查看复制过来的文件看是否正确。实验截图如下:
-
我们发现内容成功复制!接下来我们对结果进行分析:
-
由上图我们发现最后复制的passwd文件发现了一个问题,对比两个文件发现
行数不同
,发现复制的文件比原来的文件多了以下数据:
nager:/var/lib/gdm3:/bin/false
wyl:x:1000:1000:Ubuntu18.04,,,:/home/wyl:/bin/bash
sshd:x:122:65534::/run/ssh
而fstab却不会出现此问题。
我总结推测这是因为当我们运行这个程序的时候,会创建一些系统用户来执行,会被记录在/etc/passwd中,而程序结束后,此记录又会消失,所以出现了行数不同,并多出了上方数据。
以上内容仅供参考,如有不对,欢迎指正!谢谢