计算机学习日志之系统级I/O

打开和关闭文件

进程时通过调用open函数来打开一个已存在的文件或者创建一个新文件的:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);

◆标准输入(fd=0)、标准输出(fd=1)和标准错误(fd=2)三种文件自动打开,其他文件需用creat或open函数显式创建或打开后才能读写。
◆参数mode用于指定文件的访问权限,通常在open函数中该参数总是0,除非以创建方式打开,此时,参数flags中应带有O_CREAT标志。
◆参数flags:
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
例如:
fd = Open(“test.txt”, O_RDONLY, 0);

flags参数也可以是一个或者更多位掩码的或,为写提供一些额外的指示:
O_CREAT:如果文件不存在,就创建它的一个截断的(truncated)(空)文件
O_TRUNC:如果文件已经存在,就截断它(截断:清空文件内容,若文件只读,则不会清空)
O_APPEND:在每次写操作之前,设置文件位置到文件的结尾处
例如:
fd = Open(“test.txt”, O_WRDONLY|O_APPEND, 0);

最后,进程通过调用close函数关闭一个打开的文件

#include <unistd.h>

int close(int fd);

读和写文件

应用程序时通过分别调用read和write函数来执行输入和输出的。

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n);

read 函数从描述符为 fd 的当前文件位置复制最多n个字节到内存位置 buf。返回值-1表示一个错误,而返回值0表示EOF。否则,返回值表示的是实际传送的字节数量。
write 函数从内存位置 buf 复制至多n个字节到描述符 fd的当前文件位置。下面这个例子展示了一个程序使用read和write调用一次一个字节地从标准输入复制到标准输出。

#include "csapp.h"

int main(void)
{
	char c;
	while(Read(STDIN_FILENO,&c,1)!=0)
		Write(STDOUT_FILENO,&c,1);
	exit(0);
}

共享文件

可以用许多不同的方式来共享Linux文件。内核用三个相关的数据来表示打开的文件:
(1)描述符表
(2)文件表
(3)v-node表
下图展示了一个示例,其中描述符1和4通过不同的打开文件表表项来引用两个不同的文件。这是一种典型的情况,没有共享文件,并且每个描述符对应一个不同的文件。
没有共享文件
如下图所示,多个描述符也可以通过不同的文件表表项来引用同一个文件。如果以啊同一个filename调用open函数两次,就会发现这种情况。

共享同一个磁盘文件

I/O重定向

Linux shell中提供了重定向操作符,允许用户将磁盘文件和标准输入输出联系起来,例如,键入

linux> ls > foo.txt

使得shell加载和执行ls程序,将标准输入输出重定向到磁盘文件foo.txt。
那么I/O重定向时如何工作的呢?一种方式时使用dup2函数

#include <unistd.h>
int dup2(int oldfd, int newfd);
调用dup2(4,1)

调用dup2(4,1)两个描述符现在都指向文件B,文件A已经被关闭了,并且它的文件表和v-node表表项也已经被删除了;文件B的引用计数也已经增加了,从此以后,任何写到标准输入输出的数据都被重定向到文件B了。

例子

ffiles1.c

#inlcude "csapp.h"
int main(int argc, char *argv[])
{
	int fd1, fd2, fd3;
	char *fname = argv[1];
	fd1 = Open(fname, O_RDONLY, 0);
    	fd2 = Open(fname, O_RDONLY, 0);
    	fd3 = Open(fname, O_RDONLY, 0);
   	dup2(fd2, fd3);
   	
   	Read(fd1, &c1, 1);
    	Read(fd2, &c2, 1);
    	Read(fd3, &c3, 1);
    	printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);
	
	Close(fd1);
    	Close(fd2);
    	Close(fd3);
    	return 0;
}

运行结果:
1
abcde.txt中的内容是:abcde

因为调用了dup2(fd2, fd3);
所以当执行Read(fd3, &c3, 1);时,实际上是从fd2中读取一个字符,因为fd2已经读取了一个字符,此时光标在字符a后面,所以c3得到的字符是b

ffiles2.c

#include "csapp.h"
int main(int argc, char *argv[])
{
	int fd1;
    	int s = getpid() & 0x1;
    	char c1, c2;
    	char *fname = argv[1];
    	fd1 = Open(fname, O_RDONLY, 0);
    	Read(fd1, &c1, 1);
    	if (fork()) {
 		/* Parent */
 		sleep(s);
 		Read(fd1, &c2, 1);
 		printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    	} 
    	else {
 		/* Child */
 		sleep(1-s);
 		Read(fd1, &c2, 1);
 		printf("Child: c1 = %c, c2 = %c\n", c1, c2);
    }
    return 0;
}

运行结果:
2
我们发现运行两次结果不同,分析代码发现

int s = getpid() & 0x1;

s可能会取0或1

当父进程的pid是偶数时,和0x1做 & 运算,得到的s值为0
sleep函数在父进程处是sleep(0),子进程处是sleep(1)
所以父进程先输出

当父进程的pid是奇数时,和0x1做 & 运算,得到的s值为1
sleep函数在父进程处是sleep(1),子进程处是sleep(0)
所以子进程先输出

为了验证结论是正确的,我们把父进程的pid输出到屏幕上
在这里插入图片描述
输出结果与上述结论相同

ffiles3.c

#include "csapp.h"
int main(int argc, char *argv[])
{
	int fd1, fd2, fd3;
    	char *fname = argv[1];
    	fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
    	Write(fd1, "pqrs", 4); 
	
	fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
    	Write(fd3, "jklmn", 5);
    	fd2 = dup(fd1);  /* Allocates new descriptor */
    	Write(fd2, "wxyz", 4);
    	Write(fd3, "ef", 2);
	
	Close(fd1);
    	Close(fd2);
    	Close(fd3);
   	return 0;
}

运行结果:
abcde.txt中的内容由abcde变成了pqrswxyznef
在这里插入图片描述
首先看fd1的打开方式:

fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);

O_TRUNC:如果文件已经存在,就截断它

所以abcde.txt中的内容被清空

下一句向fd1中写入pqrs,所以abcde.txt中的内容为pqrs

Write(fd1, "pqrs", 4);

看fd3的打开方式:

fd3 = Open(fname, O_APPEND|O_WRONLY, 0);

O_APPEND:在每次写操作前,设置文件位置到文件的结尾处

所以执行下一句:

Write(fd3, "jklmn", 5);

文件中的内容变成了pqrsjklmn
此时fd1的光标位置在s后面,fd3的光标位置在n后面

看下一句

fd2 = dup(fd1);

fd2被重定向到fd1

所以执行这一句:

Write(fd2, "wxyz", 4);

实际上是向fd1的光标位置后写入wxyz,jklm被wxyz覆盖,此时abcde.txt中的内容为pqrswxyzn

最后

Write(fd3, "ef", 2);

再向fd3的末尾写入ef,所以最后文件中的内容为pqrswxyznef


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值