文章

linux系统编程-文件IO

文件IO

系统调用

什么是系统调用:

​ 由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。

​ C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。

img

C标准库文件IO函数。

fopen、fclose、fseek、fgets、fputs、fread、fwrite......

​ r 只读、 r+读写

w只写并截断为0、 w+读写并截断为0

a追加只写、 a+追加读写

open/close函数

函数原型:

int open(const char *pathname, int flags);

参数:
	pathname: 欲打开的文件路径名

	flags:文件打开方式:	#include <fcntl.h>

		O_RDONLY|O_WRONLY|O_RDWR	O_CREAT|O_APPEND|O_TRUNC|O_EXCL|O_NONBLOCK ....

返回值:
	成功: 打开文件所得到对应的 文件描述符(整数)

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

参数:
	pathname: 欲打开的文件路径名
	flags:文件打开方式:	O_RDONLY|O_WRONLY|O_RDWR	O_CREAT|O_APPEND|O_TRUNC|O_EXCL|O_NONBLOCK ....

	mode: 参数3使用的前提, 参2指定了 O_CREAT。	取值8进制数,用来描述文件的 访问权限。 rwx    0664

		创建文件最终权限 = mode & ~umask

返回值:
	成功: 打开文件所得到对应的 文件描述符(整数)

	失败: -1, 设置errno	

int close(int fd);

创建文件时,指定文件访问权限。权限同时受umask影响。结论为:文件权限 = mode & ~umask

文件权限位图

image-20240821095713673

使用头文件:<fcntl.h>

read函数:

ssize_t read(int fd, void *buf, size_t count);

参数:
fd:文件描述符

​ buf:存数据的缓冲区

​ count:缓冲区大小

返回值:

​ 0:读到文件末尾。

​ 成功; > 0 读到的字节数。

​ 失败: -1, 设置 errno

​ -1: 并且 errno = EAGIN 或 EWOULDBLOCK, 说明不是read失败,而是read在以非阻塞方式读一个设备文件(网络文件),并且文件无数据。

write函数:

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

参数:
fd:文件描述符

​ buf:待写出数据的缓冲区

​ count:数据大小

返回值:

​ 成功; 写入的字节数。

​ 失败: -1, 设置 errno

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

int main(int argc, char *agrv[])
{
    char buf[1024];
    int n = 0;
    int fd1 = open(agrv[1], O_RDONLY);
    if (fd1 == -1)
    {
        perror("open argv1 error");
        exit(1);
    }

    int fd2 = open(agrv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd1 == -1)
    {
        perror("open argv2 error");
        exit(1);
    }
    while ((n = read(fd1, buf, 1024)) != 0)
    {
        if (n < 0)
        {
            perror("read error");
            break;
        }
        write(fd2, buf, n);
    }

    close(fd1);
    close(fd2);
    return 0;
}

阻塞、非阻塞

阻塞、非阻塞: 是设备文件、网络文件的属性。

产生阻塞的场景。 读设备文件。读网络文件。(读常规文件无阻塞概念。)

/dev/tty -- 终端文件。

open("/dev/tty", O_RDWR|O_NONBLOCK) --- 设置 /dev/tty 非阻塞状态。(默认为阻塞状态)

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "time out\n"

int main(void)
{
    char buf[10];
    int fd, n, i;

    fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);
    if(fd < 0){
        perror("open /dev/tty");
        exit(1);
    }
    printf("open /dev/tty ok... %d\n", fd);

    for (i = 0; i < 5; i++){
        n = read(fd, buf, 10);
        if (n > 0) {                    //说明读到了东西
            break;
        }
        if (errno != EAGAIN) {          //EWOULDBLOCK  
            perror("read /dev/tty");
            exit(1);
        } else {
            write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
            sleep(2);
        }
    }

    if (i == 5) {
        write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
    } else {
        write(STDOUT_FILENO, buf, n);
    }

    close(fd);

    return 0;
}


fcntl:

​ int (int fd, int cmd, ...)

int flgs = fcntl(fd,  F_GETFL);

flgs |= O_NONBLOCK

fcntl(fd,  F_SETFL, flgs);

获取文件状态: F_GETFL

设置文件状态: F_SETFL

lseek函数:

off_t lseek(int fd, off_t offset, int whence);

参数:
fd:文件描述符

​ offset: 偏移量

​ whence:起始偏移位置: SEEK_SET/SEEK_CUR/SEEK_END

返回值:

​ 成功:较起始位置偏移量

​ 失败:-1 errno

应用场景:

  1. 文件的“读”、“写”使用同一偏移位置。

    1. 使用lseek获取文件大小

    2. 使用lseek拓展文件大小:要想使文件大小真正拓展,必须引起IO操作。

      使用 truncate 函数,直接拓展文件。 int ret = truncate("dict.cp", 250);

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(void)
{
	int fd, n;
	char msg[] = "It's a test for lseek\n";
	char ch;

	fd = open("lseek.txt", O_RDWR|O_CREAT, 0644);
	if(fd < 0){
		perror("open lseek.txt error");
		exit(1);
	}

	write(fd, msg, strlen(msg));    //使用fd对打开的文件进行写操作,问价读写位置位于文件结尾处。

	lseek(fd, 0, SEEK_SET);         //修改文件读写指针位置,位于文件开头。 

	while((n = read(fd, &ch, 1))){
		if(n < 0){
			perror("read error");
			exit(1);
		}
		write(STDOUT_FILENO, &ch, n);   //将文件内容按字节读出,写出到屏幕
	}

	close(fd);

	return 0;
}

文件描述符:

PCB进程控制块

​ 可使用命令locate sched.h查看位置: /usr/src/linux-headers-3.16.0-30/include/linux/sched.h

struct task_struct { 结构体

img

新打开文件返回文件描述符表中未使用的最小文件描述符。

预读入缓输出

*缓冲区*

read、write函数常常被称为Unbuffered I/O。指的是无用户及缓冲区。但不保证不使用内核缓冲区。

读数据时:内核缓冲区会与读入一块空间的数据 。

写数据到磁盘:内核缓冲区会等待合适时机刷新到磁盘。

img

License:  CC BY 4.0