输入/输出( I /O)是在主存和外部设备(例如磁盘驱动器、终端和网络)之间复制数据的过程。输入操作是从I/O设备复制数据到主存,而输出操作是从主存赋值数据到I/O设备。
Unix I/O
所有I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的度和写来执行。这种设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,使得所有的输入和输出都能以一种统一且一致的方式来执行。
- Linux shell 创建的么个进程开始时都有三个打开的文件:标准输入(描述符为0)(键盘)、标准输出(描述符为1)(显示器)、标准错误(描述符为2)。头文件**<unistd.h>**定义了常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,它们可用来代替显示的描述符值。
- 读文件:由指定文件读取到内存。
- 写文件:由内存写到指定文件。
文件
在Linux下一切皆文件!
- 普通文件:包含任意数据。
- 目录:包含一组链接(即文件夹)的文件。
- 套接字:用来与另一个进程进行跨网络通信的文件。
- Linux内核将所有文件都组织成一个目录层次结构,由名为 / 的根目录确定。系统中的每个文件都是根目录的直接间接的后代。
文件的基本操作
文件的基本操作:open、close、read、write、lseek等
- 打开(open):进程通过调用open函数来打开一个已存在的文件,若要打开的文件不存,则创建一个新文件。
- open函数将filename转换为一个文件描述符,并且返回描述符数字。
- flags参数:
- O_RDONLY(只读)
- O_WRONLY(只写)
- O_RDWR(可读可写)
- O_CREAT(如果文件不存在就创建它的一个截断的空文件)
- O_TRUNC(如果文件已经存在就截断它)
- O_APPEND(在每次写操作前,设置文件位置到文件的结尾处)。
- open函数调用:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(char *filename , int flags , mode_t mode);
- 关闭(close):进程通过调用close函数关闭一个打开的文件。
- close函数调用:
#include <unistd.h> int close(int fd);
- 读和写文件(read & write):应用程序是通过分别调用read和write函数来执行输入和输出的。
- read函数从描述符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值为-1表示一个错误,返回值为0表示EOF。若不为-1也不为0 ,则代表实际传送的字节数量。
- write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。
- read、close函数调用:
#include <unistd.h> ssize_t read(*int fd , void *buf , size_t n); ssize_t write(int fd , const void *buf , size_t n);
代码实例
代码(一)
/* $begin cpstdin */
#include "csapp.h"
int main(void)
{
char c;
while(Read(STDIN_FILENO, &c, 1) != 0)
Write(STDOUT_FILENO, &c, 1);
exit(0);
}
运行结果:
结果分析:
- 该程序为一个循环读入,数据从标准输入STDIN_FILENO(键盘)当中输入并读取,每输入一个字符的,就自动读取一个字符到缓冲区中,若输入为 \n 则输出所有缓冲区的内容到标准输出STDOUT_FILENO(屏幕)。
代码(二)
/* $begin statcheck */
#include "csapp.h"
int main (int argc, char **argv)
{
struct stat stat;
char *type, *readok;
/* $end statcheck */
if (argc != 2) {
fprintf(stderr, "usage: %s <filename>\n", argv[0]);
exit(0);
}
/* $begin statcheck */
Stat(argv[1], &stat);
if (S_ISREG(stat.st_mode)) /* Determine file type */
type = "regular";
else if (S_ISDIR(stat.st_mode))
type = "directory";
else
type = "other";
if ((stat.st_mode & S_IRUSR)) /* Check read access */
readok = "yes";
else
readok = "no";
printf("type: %s, read: %s\n", type, readok);
exit(0);
}
结果分析:
-
应用程序通过调用stat、fstat函数,检索到关于文件的信息。若成功则返回0,若出错返回-1。
-
函数调用:
#include <unistd.h> #include <sys/stat.h> int stat(const char *filename , struct stat *buf); int fstat(int fd , struct stat *buf);
-
Linux在sys/stat.h中定义了宏谓词来确定st-mode成员的文件类型:
S_ISREG(m):是否为普通文件,为真则为普通文件。
S_ISDIR(m):是否为目录文件,为真则为目录文件。
S_ISSOCK(m):是否为网络套接字,为真则为网络套接字。
代码(三)
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char c1, c2, c3;
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;
}
运行结果:
结果分析:
- I/O重定向:
#include <unistd.h> int dup2(int oldfd, int newfd);
- 若成功则为非负的描述符,若出错则为-1。
- dup2函数复制描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd以及打开了,dup2会在复制oldfd之前关闭newfd。