重要术语
什么是文件描述符?
文件描述符是整数,用于唯一标识进程的打开文件。
文件描述符表:文件描述符表是整数数组索引的集合,这些整数数组索引是文件描述符,其中元素是指向文件表条目的指针。操作系统中为每个进程提供了一个唯一的文件描述符表。
文件表条目:文件表条目是打开文件的内存中替代结构,在进程请求打开文件时创建,并且这些条目保持文件位置。
标准文件描述符:当任何进程启动时,该进程文件描述符表的fd(文件描述符)将自动打开0、1、2(默认情况下)这3个fd引用文件表条目中的每一个,它们分别名为/ dev / tty
/ dev / tty :终端的内存中替代
终端:组合键盘/视频屏
从stdin读取=>从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:表示新文件的权限。
- 返回值:
- 返回第一个未使用的文件描述符(通常在进程中首次使用creat时为3,因为保留了0、1、2 fd)
- 错误时返回-1
- 在OS中的运作方式
- 在磁盘上创建新的空文件
- 创建文件表条目
- 设置第一个未使用的文件描述符以指向文件表条目
- 返回使用的文件描述符,失败时返回-1
- open :用于打开文件以进行读取,写入或两者兼而有之。
Syntax in C language #include
#include #include 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
#include #include 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
- 路径:您要使用的文件的路径
- close:告诉操作系统您已经完成了文件描述符,然后关闭fd指向的文件。
Syntax in C language #include
int close(int fd); - 范围
- fd:文件描述符
- 返回
- 成功时为0 。
- -1表示错误。
- 它在操作系统中的工作方式
- 销毁文件描述符表的元素fd引用的文件表条目
–只要没有其他过程指向它! - 将文件描述符表的元素fd设置为NULL
// C program to illustrate close system Call #include
#include 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
#include 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,因为在创建主进程时, stdin , stdout和stderr已经采用了fd 0、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
#include 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
#include #include #include 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。
- 写:将bnt中的cnt字节写入与fd相关联的文件或套接字。 cnt不应大于INT_MAX(在limits.h头文件中定义)。如果cnt为零,则write()仅返回0,而不会尝试任何其他操作。
#include
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
#include 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中看到时,您会看到“ hello geeks ”。如果foo.txt文件中已经包含一些内容,则写入系统调用将覆盖该内容,并且所有先前的内容都将被删除,并且文件中仅包含“ hello geeks ”内容。
从程序中打印“ hello world”,而无需使用任何printf或cout函数。
// C program to illustrate
// I/O system Calls
#include
#include
#include
#include
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数组的字符串“ hello world”写入stdin fd [0],然后将该字符串写入stdin到buf2数组。之后,将buf2数组写入stdout并打印输出“ hello world ”。