Linux编程中的的IO函数以及相关概念
1 文件描述符表
sched.h 是一个与操作系统调度相关的头文件。
你可以使用locate sched.h
命令来查找这个文件的位置。
这个头文件里有很多有意思的东西。就比如文件描述符表
在结构体 PCB 的成员变量有这么一个变量
查看PCB结构体,里面有这么一个成员变量file_struct *file
struct task_struct { |
1.1 文件描述符
还记得Linux的哲学吗?万物皆文件。Linux上的万物皆是文件,屏幕,键盘,socket,都是文件。Linux通过一个名叫文件描述符表的结构来管理当前进程的所有文件。
文件描述表中维护了一个线性数组,文件描述符的本质就是一个整型的数,内核通过某种机制将这个整数和具体的文件关联起来了。
1.2 初窥文件描述符
我们可以通过一个小的案例来直观的看到文件描述符。
- 我们先准备一个简单的程序。一个死循环程序。
int main(){
while(1){}
return 0;
} - 编译并运行该程序,此时这个程序就会一直挂在后台
gcc main.c
./a.out - 查找当前进程的PID 145424
ubuntu@ubuntu:~/sys_code/io$ ps -aux | grep a.out
ubuntu 145424 100 0.0 2644 1024 pts/1 R+ 21:16 10:42 ./a.out - 查看
/proc/145424
该进程的目录,会在里面看到一个名为fd
的文件夹。ubuntu@ubuntu:~/sys_code/io$ cd /proc/145424
ubuntu@ubuntu:/proc/145424$ ls
cgroup fd maps oom_score_adj smaps timerslack_ns
ubuntu@ubuntu:/proc/145424$ - 查看
fd
里的内容。里面有三个软连接,0 1 2,都指向了/dev/pts/1ubuntu@ubuntu:/proc/145424/fd$ ll
总计 0
dr-x------ 2 ubuntu ubuntu 3 9月 10 21:17 ./
dr-xr-xr-x 9 ubuntu ubuntu 0 9月 10 21:16 ../
lrwx------ 1 ubuntu ubuntu 64 9月 10 21:17 0 -> /dev/pts/1
lrwx------ 1 ubuntu ubuntu 64 9月 10 21:17 1 -> /dev/pts/1
lrwx------ 1 ubuntu ubuntu 64 9月 10 21:17 2 -> /dev/pts/1
/dev/pts/1 是一个特殊的文件路径,它代表的是伪终端设备(Pseudo-Terminal Master)的一个实例。
而这里的0,1,2分别就是C语言里的 标准输入,标准输出,标准错误输出
上面的程序里啥也没有,就一个死循环,所以可以确定的是,这三个东西绝对不是我们弄出来的,那我们有没有办法增加点东西呢?
我们修改一下程序,按照上面的步骤再看一下fd
文件下会有什么。
需要再同目录下准备a.txt文件,里面可以啥也不写
|
运行程序,打印出了3
ubuntu@ubuntu:~/sys_code/io$ ./a.out |
查看fd,里面多了一个名为3的软连接指向我们打开的文件。
ubuntu@ubuntu:/proc/145635/fd$ ll |
1.3 文件描述符表的上限
文件描述符表是有默认上限的,这意味着我们不能无休止的打开文件。使用ulimit -a
命令可以看到open files
的最大值是1024。可以通过ulimit -n 4096 修改。但默认的一般就够了。
ubuntu@ubuntu:~$ ulimit -a |
2 文件
还是哲学万物皆文件,那这里的文件在Linux上是怎么描述的呢?
Linux使用了一个结构体FILE
来描述,这里面的内容很多,我们只关注一下几个
- fmode_t f_mode; 文件访问权限
- unsigned int f_flags; 文件打开标志
- struct address_space *f_mapping; 文件内核缓冲区的首地址
- struct file_operations *f_op; 文件内容操作函数指针
- struct inode_operations 文件属性操作函数指针
在 /usr/src/linux-headers-6.5.0-18-generic/include/linux/fs.h 文件里
3 系统编程提供的IO
3.1 open/close
3.1.1 opan
通过man手册第2卷可以看到open函数的形参,返回值类型,所属头文件。
pathname就是文件名称,可以写绝对路径和相对路径。
flags:
NAME |
示例代码
|
3.1.2 close
关闭文件
|
3.2 read/write
read/write既可以写文件也可以写socket,因为它们都是文件。
read、write 函数常常被称为 Unbuffered I/O。指的是无用户及缓冲区。但不保证不使用内核 缓冲区。
相较于C库提供的fgetc、fputc等函数,是没有用户级缓冲区的、
|
失败返回-1;成功:返回的值是读/写字节数。
3.3 lseek
每个打开的文件都记录着当前读写位置,打开文件时读写位置是 0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以 O_APPEND 方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek 和标准 I/O 库的 fseek 函数类似,可以移动当前读写位置(或者叫偏移量)。
|
失败返回-1;成功:返回的值是较文件起始位 置向后的偏移量。