Linux系统调用下的文件I/O编程

【图书推荐】《Linux C与C++一线开发实践(第2版)》_linux c与c++一线开发实践pdf-CSDN博客

LinuxC\C++编程技术_夏天又到了的博客-CSDN博客

《Linux C与C++一线开发实践(第2版)(Linux技术丛书)》(朱文伟,李建英)【摘要 书评 试读】- 京东图书

I/O就是输入/输出,它是主存和外部设备(比如硬盘、U盘)之间复制数据的过程,其中数据从设备到内存的过程称为输入,数据从内存到设备的过程称为输出。I/O可以分为低级I/O和高级I/O。低级I/O通常也称为不带缓冲的I/O,它是Linux提供的系统调用,如函数open、read、write等。高级I/O通常也称为带缓冲的I/O,比如ANSI C提供的标准I/O库中的fopen、fread、fwrite等函数就是这种高级I/O的例子。带缓冲的I/O在系统调用前会采用一定的策略,因此速度会比较慢,但它比不带缓冲的I/O安全。

4.8.1  文件描述符

对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数),后续对文件的操作就可以通过该文件描述符来执行,内核记录有关这个打开文件的信息。一个进程启动时,默认打开3个文件,标准输入、标准输出、标准错误,对应的文件描述符分别是0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO)。这些常量定义在unistd.h头文件中。

我们以前可能没接触过文件描述符,接触较多的是文件指针,其实两者之间是可以通过函数fileno和fdopen相互转换的。函数fileno将文件指针转换为文件描述符,声明如下:

int fileno(FILE *stream);

其中,参数stream是文件指针。

函数fdopen将文件描述符转换为文件指针,声明如下:

FILE *fdopen(int fd, const char *mode);

其中,参数fd是文件描述符,mode是打开方式。

【例4.1】打印stdin、stdout和stderr的文件描述符值

(1)打开Visual Studio Code,新建一个test.cpp文件,在test.cpp中输入如下代码:

#include 
#include 

int main(void)
{
	printf("fileno(stdin) = %d\n", fileno(stdin));
	printf("fileno(stdout) = %d\n", fileno(stdout));
	printf("fileno(stderr) = %d\n", fileno(stderr));
	return 0;
}

(2)上传test.cpp到Linux,在终端下输入命令g++ -o test test.cpp,然后运行test,运行结果如下:

[root@localhost cpp98]# g++ -o test test.cpp
[root@localhost cpp98]# ./test
fileno(stdin) = 0
fileno(stdout) = 1
fileno(stderr) = 2

4.8.2  打开或创建文件

Linux提供open函数来打开或者创建一个文件。该函数声明如下:

#include  
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

其中,参数pathname表示文件的名称,可以包含(绝对和相对)路径;flags表示文件打开方式;mode用来规定对该文件的所有者、文件的用户组及系统中其他用户的访问权限。如果函数执行成功,就返回文件描述符,如果函数执行失败,就返回-1。

文件打开的方式flags可以使用下列宏(当有多个选项时,采用“|”连接):

  • O_RDONLY:打开一个只供读取的文件。
  • O_WRONLY:打开一个只供写入的文件。
  • O_RDWR:打开一个可供读写的文件。
  • O_APPEND:写入的所有数据将被追加到文件的末尾。
  • O_CREAT:打开文件,如果文件不存在就建立文件。
  • O_EXCL:如果已经设置O_CREAT且文件存在,就强制open失败。
  • O_TRUNC:在打开文件时,将文件的内容清空。
  • O_DSYNC:每次写入时,等待数据写到磁盘上。
  • O_RSYNC:每次读取时,等待相同部分先写到磁盘上。
  • O_SYNC:以同步方式写入文件,强制刷新内核缓冲区到输出文件。

最后3个SYNC(同步)选项都会降低性能。使用这些宏需要包含头文件fcntl.h。值得注意的是,O_RDONLY、O_WRONLY和O_RDWR这3个选项是必选其一的。

参数mode只在创建文件时才使用,用于指定文件的访问权限。Mode可以使用下列宏:

  • S_IRUSR:文件所有者的读权限位。
  • S_IWUSR:文件所有者的写权限位。
  • S_IXUSR:文件所有者的执行权限位。
  • S_IRWXU:S_IRUSR|S_IWUSR|S_IXUSR。
  • S_IRGRP:文件用户组的读权限位。
  • S_IWGRP:文件用户组的写权限位。
  • S_IXGRP:文件用户组的执行权限位。
  • S_IRWXG:S_IRGRP|S_IWGRP|S_IXGRP。
  • S_IROTH:文件其他用户的读权限位。
  • S_IWOTH:文件其他用户的写权限位。
  • S_IXOTH:文件其他用户的执行权限位。
  • S_IRWXO:S_IROTH|S_IWOTH|S_IXOTH。

使用这些权限宏,需要包含头文件sys/stat.h。文件的访问权限是根据umask&~mode得出来的,例如umask=0022,mode = 0655,则访问权限为644。umask是目前用户在建立档案或目录时的权限默认值,我们可以通过命令umask查看该值,比如:

[root@localhost ~]# umask
0022
[root@localhost ~]# umask -S
u=rwx,g=rx,o=rx

加S表示以字符形式显示。

下面看一个小例子,创建一个指定权限的文件。

打开文件,既可以用相对路径,又可以用绝对路径。比如,打开当前目录zww下的文件myfile.dat,可以这样写:

int fd = open("./zww/myfile.dat", O_RDWR); 

其中,“.”表示当前工作目录,当前进程被启动的目录就是当前工作目录。

如果要以只读方式打开当前目录上级目录下的某个文件,可以这样写:

int fd = open("../myfile.dat", O_RDONLY); // 其中..表示当前工作目录的上一级目录

或者打开一个绝对路径下的文件,比如:

int fd = open("/etc/myfile.dat", O_CREAT| O_RDWR); // 不存在就新建,否则以读写方式打开

4.8.3  创建文件

为了维持与早期的UNIX系统的向后兼容性,Linux也提供了一个专门创建文件的系统调用,即creat函数(注意结尾没有e)。它的声明如下:

int creat(const char *pathname, mode_t mode);

其中,参数pathname表示文件的名称,可以包含(绝对和相对)路径;mode用来规定对该文件的所有者、文件的用户组及系统中其他用户的访问权限,其取值与open函数的mode相同。如果函数执行成功,就返回文件描述符,否则返回-1。

在UNIX的早期版本中,open系统调用函数仅仅存在两个参数的形式。如果文件不存在,就不能打开这些文件。文件的创建则由单独的系统调用函数creat完成。在Linux及所有UNIX的近代版本中,creat系统调用函数是多余的,因为open也可以用来创建文件。下面两种形式等价。

int fd = creat(file, mode);
int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);

【例4.2】创建一个只读的文件

(1)打开Visual Studio Code,新建一个test.cpp文件,在test.cpp中输入如下代码:

#include 
#include 
#include 
#include 
#include 

int main(void)
{
	int fd = -1;
	char filename[] = "/root/test.txt"; 	// 注意路径中的/不要写成\
	fd = creat(filename,0666); 				// 创建只读属性的文件
	if (fd == -1)
		printf("fail to pen file %s\n", filename);
	else
		printf("create file %s successfully\n", filename);
	 
	return 0;
}

(2)上传test.cpp到Linux,在终端下输入命令g++ -o test test.cpp,然后运行test,运行结果如下:

[root@localhost cpp98]# g++ -o test test.cpp
[root@localhost cpp98]# ./test
create file /root/test.txt successfully

我们在路径/root下创建了一个文件test.txt。即使test.txt存在,依然会成功创建。

4.8.4  关闭文件

文件不再使用的时候,需要把它关闭。可以用函数close来关闭文件,该函数声明如下:

#include 
int close(int fd);

其中,参数fd为要关闭的文件的描述符。如果函数执行成功,就返回文件描述符,否则返回-1。

文件关闭以后,文件描述符不再指向任何文件,从而描述符可以再次使用。如果每次打开文件后都不关闭,就会将系统的文件描述符耗尽,导致不能再打开文件。

【例4.3】打开并关闭一个文件

(1)打开Visual Studio Code,新建一个test.cpp文件,在test.cpp中输入如下代码:

#include 
#include 
#include 
#include 
#include 

int main(void)
{
	int fd = -1;
	char filename[] = "test.txt"; 	// 若没有使用绝对路径,则打开的是当前目录下的test.txt
	fd = open(filename, O_CREAT | O_RDWR, S_IRWXU); // 打开文件
	if (fd == -1)
		printf("fail to pen file %s,fd:%d\n", filename, fd);
	else
		printf("Open file %s successfully,fd:%d\n", filename, fd);
	close(fd);
	return 0;
}

(2)上传test.cpp到Linux,在终端下输入命令g++ -o test test.cpp,然后运行test,运行结果如下:

[root@localhost cpp98]# g++ -o test test.cpp
[root@localhost cpp98]# ./test
Open file test.txt successfully,fd:3

文件的读取和写入示例,请打开如下链接阅读: 

Linux C/C++编程-文件的读取与写入示例-CSDN博客 

Linux系统调用下的文件I/O编程_第1张图片

Linux系统调用下的文件I/O编程_第2张图片

Linux系统调用下的文件I/O编程_第3张图片

你可能感兴趣的:(LinuxC\C++编程技术,linux,服务器,运维)