系统调用I/O常用函数

系统调用:

由上向下一次调用
库函数(标准C库) man 3 printf(3)
系统调用函数 man 2 write(2)
内核(kernel) Linux write( )

文件描述符:

​ 已打开文件的标志,非负整型数,默认情况最多打开1024个文件(0~1023).

​ 系统默认打开3个文件,占用三个描述符:0 标准输入,1 标准输出, 2标准错误输出.

系统调用I/O常用函数_第1张图片
文件描述符

基本IO操作函数:

如何得到和使用文件描述符:

open(2) 打开文件

头文件:

#include 
#include 
#include 

函数声明:

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

open(2)参数含义:

​ 文件路径;打开方式;当打开方式为O_CREAT时需要第三个参数,第三个参数为创建文件的权限

文件权限一般写 0666 ,创建好的文件实际权限为 mode & ~umask

终端输入umask查看值(不同用户不一样)

​ 最常用的六种打开方式

打开方式
O_RDONLY O_WRONLY O_RDWR 必须三选一
只读 只写 读写
O_CREAT O_TRUNC O_APPEND 可选
创建文件 将文件截断为0 在文件后追加

当需要多个打开方式时,用“ | ”连接.

函数返回值:

//返回新的文件描述符;
//如果发生错误,则返回-1,在这种情况下,会设置errno值

errno是一个全局变量,定义在errno.h中 大多数系统调用出错是会设置errno的值,

根据不同的值得到相应的错误原因 perror()直接将相应错误原因打印输出

关于更多 errno 值请查看man 3 errno

使用 open(2) 函数打开文件后一定要使用 close(2) 函数关闭文件!

read(2) 读取文件

头文件:

include 

函数声明:

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

read(2)参数含义:

​ 文件描述符;缓冲区( 就是读到哪 );缓冲区大小(不能大于定义好的缓冲区大小)

一般定义一个字符数组作为缓冲区,传递字符数组首地址

函数返回值:

//返回读取的字节个数;返回 0 表示文件读完;
//如果发生错误,则返回-1,在这种情况下,会设置errno值

关于 errno 值请查看man 3 errno

write(2) 写入文件

头文件:

include 

函数声明:

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

write(2)参数含义:

​ 文件描述符;缓冲区( 就是写到哪 );缓冲区大小(不能大于定义好的缓冲区大小)

一般定义一个字符数组作为缓冲区,传递字符数组首地址

函数返回值:

//成功时,返回写入的字节数
//如果发生错误,则返回-1,在这种情况下,会设置errno值

关于 errno 值请查看man 3 errno

close(2) 关闭文件

头文件:

include 

函数声明:

int close(int fd);

close(2)参数含义:

​ 文件描述符

函数返回值:

//成功时,返回0
//如果发生错误,则返回-1,在这种情况下,会设置errno值

关于 errno 值请查看man 3 errno

open(2)、read(2)、write(2)、close(2) 用法示例

#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 10

int main(int argc, char *argv[])
{
    int fd;
    char buf[BUFSIZE] = {};
    int cnt;
    
    if (argc < 2)
        return 1;

    // fd 文件描述符>= 0                 mode & ~umask
    // fd = open(argv[1], O_RDWR | O_CREAT, 0666);
    fd = open(argv[1], O_RDONLY);     //通过open()获取文件描述符
    if (fd == -1) 
    {
        perror("open()");    //提示出错
        return 1;
    }

    // 打开成功
    printf("fd:%d\n", fd);    //输出一下fd看为多少

    // 
    while (1) 
    {
        cnt = read(fd, buf, BUFSIZE);    //获取读取的字节个数,并将读取的内容放入缓冲区
        if (cnt == -1) 
        {
            perror("read()");   //如果读取的字节个数为-1,提示错误
            goto ERROR;
        }
        if (cnt == 0)           //如果为读取的字节个数为 0 表示文件读完
            break;
        
        //将缓冲区的内容写入文件描述符为1的文件,即标准输出;(说白的就是打印到终端上)
        write(1, buf, cnt);     
    }

    // close
    close(fd);   //关闭文件

    return 0;
ERROR:
    close(fd);
    return 1;
}

练习:

1.将文件 1 的内容拷贝到文件 2

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 10



int main(int argc, char **argv)
{
    int rfd, wfd;
    char buf[BUFSIZE] = {};
    int cnt;

    if (argc < 3) 
    {
        write(2, "传参不够\n", strlen("传参不够\n"));   //写入到标准错误输出
        return 1;
    }
//errno是一个全局变量,定义在errno.h中 大多数系统调用出错是会设置errno的值,
//根据不同的值得到相应的错误原因 perror()直接将相应错误原因打印输出
//strerror(errno)返回错误字符串
    printf("%s\n", strerror(errno));

    rfd = open(argv[1], O_RDONLY);
    if (rfd == -1) 
    {
        // printf("open():%s\n", strerror(errno));
        perror("open()"); // 输出到标准错误输出
        return 1;
    }
    
    wfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (wfd == -1) 
    {
        // printf("open():%s\n", strerror(errno));
        perror("open()");
        goto ERROR1;    
    }

    while (1) 
    {
        cnt = read(rfd, buf, BUFSIZE);
        if (-1 == cnt) 
        {
            perror("read()");
            goto ERROR2;
        }
        if (0 == cnt) 
        {
            // rfd结束标志
            break;
        }
        write(wfd, buf, cnt);
    }

    close(rfd);
    close(wfd);

    return 0;
ERROR2:
    close(wfd);
ERROR1:
    close(rfd);
    return 1;
}

lseek(2) 改变文件偏移量

头文件:

#include 
#include 

函数声明:

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

lseek(2)参数含义:

//文件描述符;   偏移量,传整形,可以是负的;   从哪偏移,可选项如下:

int whence可选项:

SEEK_SET
The offset is set to offset bytes. 将文件偏移量指向文件开头,即从头开始偏移

SEEK_CUR
The offset is set to its current location plus offset bytes. 从当前文件指针开始偏移

SEEK_END
The offset is set to the size of the file plus offset bytes. 从文件末尾开始偏移

函数返回值:

//成功完成后,lseek()将返回从文件开头以字节为单位的结果偏移位置。 
//出错时,返回值(off_t)-1并设置errno以指示错误。

关于 errno 值请查看man errno

空洞文件

lseek()函数允许将文件偏移量设置为超出文件末尾(但这不会改变文件的大小)。 如果稍后写入数据,则后续读取间隙中的数据(“空洞”)将设置为空字节('\ 0'),直到数据实际写入。

即将文件指针设置到文件末尾(SEEK_END),偏移量设置正整数,偏移后写入数据,中间的缝隙会写入(‘\ 0’),并且有实际大小,请看下方示例

用法示例

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int fd;
    char buf[10] = {};
    int cnt;

    if (argc < 2)
        return 1;

    fd = open(argv[1], O_RDWR);
    if (fd == -1) 
    {
        perror("open()");
        return 1;
    }

    read(fd, buf, 2);   
    puts(buf);

    cnt = lseek(fd, 5, SEEK_CUR); // current+5
    printf("cnt:%d\n", cnt);
    read(fd, buf, 2);   
    puts(buf);

    lseek(fd, 5, SEEK_SET); // pos = 5
    read(fd, buf, 2);   
    puts(buf);

    lseek(fd, -5, SEEK_END); // pos = end-5
    read(fd, buf, 2);   
    puts(buf);

    // 空洞文件
    if (lseek(fd, 100, SEEK_END) == (off_t)-1) 
    {
        perror("lseek()");
        return 1;
    }
    write(fd, "q", 1);

    close(fd);

    return 0;
}

练习

1.判读一个文件有多少行,以及字节个数

#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 100

int main(int argc, char *argv[])
{
    int fd;
    int lines = 0;            //行号
    char buf[BUFSIZE] = {};
    char *p = NULL;
    int cnt;

    if (argc < 2)
        return 1;

    fd = open(argv[1], O_RDONLY);
    if (fd == -1) 
    {
        perror("open()");
        return 1;
    }

    while (1) 
    {
        memset(buf, '\0', BUFSIZE);
        cnt = read(fd, buf, BUFSIZE-1);
        if (cnt == -1) 
        {
            perror("read()");
            goto ERROR;
        }
        if (cnt == 0)
            break;
        // 统计buf字符数组中有多少个'\n'
        p = buf;
        while (1) 
        {
            p = strchr(p, '\n');
            if (p == NULL)
                break;
            lines ++;
            p ++;
        }
    }
    
    printf("共%d行\n", lines);

    //将文件指针指向文件末尾,偏移量为0,返回值即为文件字节个数
    printf("此文件共%ld个字节\n", lseek(fd, 0, SEEK_END));  

    close(fd);

    return 0;
ERROR:
    close(fd);
    return 1;
}

dup(2) / dup2(2) 复制文件描述符 实现文件重定向

头文件:

#include 

函数声明:

int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup(2)
复制旧的文件描述符的文件表项给当前可用的最小文件描述符

dup2(2)
复制旧的文件描述符的文件表项给新的文件描述符

对于被复制的文件表项,拥有两个文件描述符,可以一起使用,拥有同一个文件指针

如果新的文件描述符通过 lseek(2) 修改文件偏移量,对于旧的描述符,偏移量也会改变。

dup(2)参数含义:

//旧的文件描述符

dup2(2)参数含义:

//旧的文件描述符;新的文件描述符

函数返回值:

//成功时,将返回新的描述符。 
//出错时,返回-1,并设置errno。

用法示例

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int fd;

    if (argc < 2)
        return 1;

    fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd == -1) 
    {
        perror("open()");
        return 1;
    }

#if 0
    close(1);
    dup(fd); // 当前可用最小的文件描述符作为fd的复制
#endif
    // 原子操作
    // dup2(fd, 1);
    printf("%d\n", fcntl(fd, F_DUPFD, 1));

    printf("hello world\n");
    fflush(NULL); // 刷新标准io的缓存区
    // write(1, "hello world\n", 12);

    close(fd);
    
    return 0;
}

fcntl(2)

复制文件描述符,改变文件的状态,类似dup的功能


系统调用I/O常用函数_第2张图片
fcntl函数介绍
系统调用I/O常用函数_第3张图片
cmd参数

你可能感兴趣的:(系统调用I/O常用函数)