重要术语
什么是文件描述符?
文件描述符是整数, 用于唯一标识进程的打开文件。
文件描述符表:文件描述符表是整数数组索引的集合, 这些整数数组索引是文件描述符, 其中元素是指向文件表条目的指针。操作系统中为每个进程提供了一个唯一的文件描述符表。
文件表条目:文件表条目是打开文件的内存中代理结构, 该文件在处理请求打开文件时创建, 并且这些条目保持文件位置。
标准文件描述符:任何进程启动时, 该进程文件描述符表的fd(文件描述符)会自动打开0、1、2(默认情况下)这3个fd引用文件表条目中的每个文件/ dev / tty
/ dev / tty
:终端的内存中替代
终奌站
:组合键盘/视频屏幕
从标准输入读取=>从fd读取0
:每当我们从键盘上输入任何字符时, 它都会通过fd 0从stdin读取并保存到名为/ dev / tty的文件中。
写入stdout =>写入fd 1
:每当我们看到视频屏幕上的任何输出时, 它都来自名为/ dev / tty的文件, 并通过fd 1写入屏幕中的stdout。
写入stderr =>写入fd 2
:我们看到视频屏幕出现任何错误, 这也是通过fd 2从该文件写入屏幕中的stderr的结果。
I / O系统调用
基本上共有5种类型的I / O系统调用:
创造:
用于创建一个新的空文件。
Syntax in C language:
int creat(char *filename, mode_t mode)
参数
filename:你要创建的文件的名称
mode:表示新文件的权限。
返回值:
返回第一个未使用的文件描述符(通常在进程中首次创建时使用3, 因为保留了0、1、2 fd)
错误时返回-1
在OS中如何运作
在磁盘上创建新的空文件
创建文件表条目
设置第一个未使用的文件描述符以指向文件表条目
使用的返回文件描述符, 失败时为-1
打开
:用于打开文件以进行读取, 写入或两者同时进行。
Syntax in C language
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
int open (const char* Path, int flags [, int mode ]);
参数
- 路径:你要使用的文件的路径
- 当你不在文件的同一目录中时, 请使用以" /"开头的绝对路径。
- 当你在文件的同一目录中工作时, 请使用相对路径, 该路径仅是带有扩展名的文件名。
- 标志:你喜欢如何使用
- O_RDONLY: 只读, O_WRONLY:仅写, O_RDWR: 读和写, O_CREAT:创建文件(如果不存在), O_EXCL:如果已经存在则阻止创建
在OS中的运作方式
在磁盘上查找现有文件
创建文件表条目
设置第一个未使用的文件描述符以指向文件表条目
使用的返回文件描述符, 失败时为-1
// C program to illustrate
// open system call
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
extern int errno ;
int main()
{
// if file does not have in directory
// then file foo.txt is created.
int fd = open( "foo.txt" , O_RDONLY | O_CREAT);
printf ( "fd = %d/n" , fd);
if (fd ==-1)
{
// print which type of error have in a code
printf ( "Error Number % d\n" , errno );
// print program detail "Success or failure"
perror ( "Program" );
}
return 0;
}
输出如下:
fd = 3
关:
告诉操作系统你已经完成了文件描述符, 然后关闭fd指向的文件。
Syntax in C language
#include <fcntl.h>
int close(int fd);
参数
fd:文件描述符
返回
成功时为0。
-1表示错误。
它在操作系统中的工作方式
销毁文件描述符表的元素fd引用的文件表条目
–只要没有其他过程指向它!
将文件描述符表的元素fd设置为NULL
// C program to illustrate close system Call
#include<stdio.h>
#include <fcntl.h>
int main()
{
int fd1 = open( "foo.txt" , O_RDONLY);
if (fd1 < 0)
{
perror ( "c1" );
exit (1);
}
printf ( "opened the fd = % d\n" , fd1);
// Using close system Call
if (close(fd1) < 0)
{
perror ( "c1" );
exit (1);
}
printf ( "closed the fd.\n" );
}
输出如下:
opened the fd = 3
closed the fd.
// C program to illustrate close system Call
#include<stdio.h>
#include<fcntl.h>
int main()
{
// assume that foo.txt is already created
int fd1 = open( "foo.txt" , O_RDONLY, 0);
close(fd1);
// assume that baz.tzt is already created
int fd2 = open( "baz.txt" , O_RDONLY, 0);
printf ( "fd2 = % d\n" , fd2);
exit (0);
}
输出如下:
fd2 = 3
在这里, 在此代码中, 首先open()返回3因为当创建主进程时, 然后fd0、1、2已经被标准输入, 标准输出和斯特德。所以第一个未使用的文件描述符是3在文件描述符表中。之后在close()系统调用中释放了它3文件描述符, 然后设置3文件描述符为null。因此, 当我们调用第二个open()时, 第一个未使用的fd也是3。因此, 该程序的输出为3.
读:
从文件描述符fd指示的文件中, read()函数将cnt字节的输入读取到buf指示的存储区中。成功的read()将更新文件的访问时间。
Syntax in C language
size_t read (int fd, void* buf, size_t cnt);
参数
fd:文件描述符
buf:从中读取数据的缓冲区
cnt:缓冲区的长度
返回:实际读取了多少字节
返回成功读取的字节数
到达文件末尾时返回0
错误返回-1
信号中断返回-1
重要事项
由于溢出, buf需要指向长度不小于指定大小的有效内存位置。
fd应该是从open()返回的有效文件描述符, 以执行读取操作, 因为如果fd为NULL, 则读取应生成错误。
cnt是请求的读取字节数, 而返回值是实际读取的字节数。同样, 有时读取系统调用读取的字节数应少于cnt。
// C program to illustrate
// read system Call
#include<stdio.h>
#include <fcntl.h>
int main()
{
int fd, sz;
char *c = ( char *) calloc (100, sizeof ( char ));
fd = open( "foo.txt" , O_RDONLY);
if (fd < 0) { perror ( "r1" ); exit (1); }
sz = read(fd, c, 10);
printf ( "called read(% d, c, 10). returned that"
" %d bytes were read.\n" , fd, sz);
c[sz] = '\0' ;
printf ( "Those bytes are as follows: % s\n" , c);
}
输出如下:
called read(3, c, 10). returned that 10 bytes were read.
Those bytes are as follows: 0 0 0 foo.
假设foobar.txt由6个ASCII字符" foobar"组成。那么以下程序的输出是什么?
// C program to illustrate
// read system Call
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
char c;
int fd1 = open( "sample.txt" , O_RDONLY, 0);
int fd2 = open( "sample.txt" , O_RDONLY, 0);
read(fd1, &c, 1);
read(fd2, &c, 1);
printf ( "c = %c\n" , c);
exit (0);
}
输出如下:
c = f
描述符fd1和fd2每个描述符都有自己的打开文件表条目, 因此每个描述符都有自己的文件位置foobar.txt。因此, 从fd2读取的第一个字节foobar.txt, 输出为c = f, 不是c = o.
写:
将buf中的cnt字节写入与fd相关的文件或套接字。 cnt不应大于INT_MAX(在limits.h头文件中定义)。如果cnt为零, 则write()仅返回0, 而不会尝试任何其他操作。
#include <fcntl.h>
size_t write (int fd, void* buf, size_t cnt);
参数
fd:文件描述符
buf:将数据写入的缓冲区
cnt:缓冲区的长度
返回:实际写入了多少字节
返回成功写入的字节数
到达文件末尾时返回0
错误返回-1
信号中断返回-1
重要事项
需要打开文件进行写操作
buf的长度必须至少等于cnt所指定的长度, 因为如果buf的大小小于cnt, 则buf会导致溢出。
cnt是请求写入的字节数, 而返回值是实际写入的字节数。当fd要写入的字节数少于cnt时, 会发生这种情况。
如果write()被信号中断, 则结果是以下之一:
-如果write()尚未写入任何数据, 则返回-1并将errno设置为EINTR。
-如果write()已成功写入某些数据, 则它将返回其在中断之前写入的字节数。
// C program to illustrate
// write system Call
#include<stdio.h>
#include <fcntl.h>
main()
{
int sz;
int fd = open( "foo.txt" , O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
{
perror ( "r1" );
exit (1);
}
sz = write(fd, "hello geeks\n" , strlen ( "hello geeks\n" ));
printf ( "called write(% d, \"hello geeks\\n\", %d)."
" It returned %d\n" , fd, strlen ( "hello geeks\n" ), sz);
close(fd);
}
输出如下:
called write(3, "hello geeks\n", 12). it returned 11
在这里, 运行代码后, 如果在文件foo.txt中看到"你好极客"。如果foo.txt文件中已经包含一些内容, 则写入系统调用将覆盖该内容, 并且所有先前的内容已删除而且只有"你好极客内容将存在于文件中。
从程序中打印" hello world", 而不使用任何printf或cout函数。
// C program to illustrate
// I/O system Calls
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main ( void )
{
int fd[2];
char buf1[12] = "hello world" ;
char buf2[12];
// assume foobar.txt is already created
fd[0] = open( "foobar.txt" , O_RDWR);
fd[1] = open( "foobar.txt" , O_RDWR);
write(fd[0], buf1, strlen (buf1));
write(1, buf2, read(fd[1], buf2, 12));
close(fd[0]);
close(fd[1]);
return 0;
}
输出如下:
hello world
在此代码中, buf1数组的字符串"你好, 世界"首先将其写入stdin fd [0], 然后将该字符串写入stdin到buf2数组。之后, 将buf2阵列写入stdout并打印输出"你好, 世界"。
如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。