文件IO编程 1 & 2

头文件包含路径 

文件IO编程 1 & 2_第1张图片

 文件IO编程 1 & 2_第2张图片

文件IO编程 1 & 2_第3张图片

linux 操作系统分为两大空间:用户空间和内核空间

这样划分,是为了保护内核的核心组件,不被轻易访问和修改

        

        系统调用:安全的访问内核空间

其核心是:函数API(API:用户编程接口)

所谓系统调用是指操作系统提供给用户的组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的的服务

文件IO编程 1 & 2_第4张图片

 学习linux应用编程,就是学习使用API【API需要阅读手册——参考man手册、linux c手册】

硬件中断

2.

文件编程

文件IO:一切皆为文件(内核中虚拟文件系统(VFS));用文件系统来管理

文件IO:

 文件分为四大类:-普通文件、d目录文件、l链接文件和c/b设备文件(三小类:p管道文件、s套接字和f堆栈文件)

都可以用C库函数和API来调用

把一切抽象成文件,用统一的方式管理设备和文件,节省开发资源;用过文件来操作硬件

文件描述符:

文件IO编程 1 & 2_第5张图片

 知道一个文件的id(文件指针),操作该id就是操作该文件;

3.初级IO(creat及异常处理)

creat函数

1.宏定义  :

2.数字【0:八进制;3位8进制来表示文件的权限,r用4标识,w用2标识,x用1标识】

已经创建的文件,不能通过再creat来修改权限;

如何获取错误信息?

方法一:

errno:系统全局变量(所有应用都可以访问),用来保存错误编号(整数)

使用该变量时,要有这个头文件

文件IO编程 1 & 2_第6张图片

 方法二:perror --------------------常用-----------------------

文件IO编程 1 & 2_第7张图片

 方法三:

文件IO编程 1 & 2_第8张图片

open函数

O_WRONLY:写        O_RDONLY:读      O_RWRD:可读可写

O_CREAT:不存在就创建;【O_EXCEL: O_CREAT存在时,打开已有的文件就报错】

O_APPEND:文件读写位置移到末尾;    O_TRUNC:文件长度为零(清空)

O_NONBLOCK:非阻塞的方式打开;

close函数

假设有两个file descript指向同一个文件,可以close(fd1/fd2)都可以,也可以各close一次

read

write

lseek

lseek的返回值是,lseek操作后,文件读写位置距离文件首的距离(字节数),以此可以测量文件大小:

lseek(fd3, 0, SEEK_END); // 返回值是文件大小(字节数)
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
    // int fd1;
    // fd1 = creat(argv[1], S_IRWXU | S_IRWXG | S_IRWXO);
#if 0
    // 第一种方法
    if (errno == EISDIR)
    {
        printf("is a directory\n");
    }
    // 第二种方法:
    perror("error is:");
    // 第三种方法:
    printf("%s\n", strerror(errno));

#endif
    // printf("%d\n",fd1);
    // int fd2=open(argv[1],O_RDWR);
    // printf("%d\n",fd2);

    // printf("%d\n",close(fd1));
    // printf("%d\n",close(fd2));

    int fd3 = open(argv[1], O_RDWR | O_CREAT, S_IRWXG | S_IRWXO | S_IRWXU);
    char buffer[1024];
    gets(buffer);

    int w_num;
    printf("%d\n", w_num = write(fd3, buffer, strlen(buffer)));
    if (w_num == -1)
    {
        printf("write error\n");
        exit(-1);
    }

    //lseek(fd3, 0, SEEK_SET);
    //lseek(fd3, 0, SEEK_END); // 返回值是文件大小(字节数)
    lseek(fd3, -w_num, SEEK_CUR);//将文件读写位置移到开头

    int r_num;
    r_num = read(fd3, buffer, strlen(buffer));
    buffer[r_num] = '\0';
    printf("read num:%d; read content:%s\n", r_num, buffer);

    return 0;
}

获取文件属性:

文件IO编程 1 & 2_第9张图片

文件IO编程 1 & 2_第10张图片

struct stat file_a1;
    //1.
    stat("a.txt",&file_a1);
    printf("stat.size=%ld\n",file_a1.st_size);//输出a.txt的大小;
    
    //2.
     struct stat file_a2;
    fstat(fd3,&file_a2);
    printf("fstat.size=%ld\n",file_a2.st_size);//输出a.txt的大小;

    //3.
    struct stat file_a3;
    lstat("a.txt",&file_a3);
    printf("lstat.size=%ld\n",file_a3.st_size);//输出a.txt的大小;

以上函数实现:读一行,写一行的read_line 函数,并以此实现文件拷贝(需要考虑到换行符)

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

// 按行读取
int read_line(int fd1,int fd2)
{
    char src[200] = {0};
    char des[200] = {0};
    int index_s = 0;
    int index_d = 0;
    int r_num = 0;
    int count = 0;
    while ((r_num = read(fd1, src, sizeof(src))) > 0)
    {
        for (int i = 0; i < r_num; ++i)
        {
            des[index_d++] = src[index_s++];
            if (des[index_d - 1] == '\n')
            {
                index_d = 0;
                write(fd2,des,strlen(des));
                memset(des, 0, sizeof(des));
            }
        }
        index_s = 0;
    }
    if (strlen(des) != 0)
    {
        write(fd2,des,strlen(des));
    }
}

int main(int argc, char **argv)
{
    if (argc != 3)
    {
        printf("input error!\n");
        exit(-1);
    }
    int fd1 = open(argv[1], O_RDWR);
    if (fd1 == -1)
    {
        perror("open1 :");
    }
    int fd2 = open(argv[2], O_RDWR|O_CREAT,0777);
    if (fd2 == -1)
    {
        perror("open2 :");
    }
    read_line(fd1,fd2);
    return 0;
}

作业:统计文件中的单词数:

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

// 统计字符串数组内个数:
int count_words(char *src)
{
    int index = 0;
    int count = 0;
    int flag = 0;
    while (index <= strlen(src))
    {
        if (('A' <= src[index] && src[index] <= 'Z') || ('a' <= src[index] && src[index] <= 'z'))
        {
            flag = 1;
        }
        else if (src[index] == ' ')
        {
            if (flag == 1)
            {
                flag = 0;
                count++;
            }
        }
        index++;
    }
    if (flag == 1)
    {
        count++;
    }
    return count;
}

// 按行读取
int read_line(int fd1)
{
    char src[200] = {0};
    char des[200] = {0};
    int index_s = 0;
    int index_d = 0;
    int r_num = 0;
    int count = 0;
    while ((r_num = read(fd1, src, sizeof(src))) > 0)
    {
        for (int i = 0; i < r_num; ++i)
        {
            des[index_d++] = src[index_s++];
            if (des[index_d - 1] == '\n')
            {
                index_d = 0;
                count = count + count_words(des);
                memset(des, 0, sizeof(des));
            }
        }
        index_s = 0;
    }
    if (strlen(des) != 0)
    {
        count = count + count_words(des);
    }
    printf("words number:%d\n", count);
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("input error!\n");
        exit(-1);
    }
    int fd = open(argv[1], O_RDWR);
    if (fd == -1)
    {
        perror("open :");
    }
    read_line(fd);
    return 0;
}

0、1、2三个文件描述符

一个程序一旦运行,则打开三个文件【/dev/下的stdin-0         stdout-1         stderr-2】

文件指针fp:stdin ,stdout, stderr

 stdout是行缓冲的,而stderr是无缓冲

这意味着当你向stdout写入数据时,如果没有遇到换行符('\n')或者没有显式调用fflush函数,数据将会暂存在输出缓冲区中。

而当你向stderr写入数据时,它会立即被刷新到输出设备上,无需等待。这样做是为了确保重要的错误消息能够尽快显示在终端上。

库函数 vs 系统调用

有缓冲库函数(标准IO) ——————   无缓冲系统调用(初级IO)

对普通文件的操作,使用库函数fread,更方便;

对于特殊文件,尤其是对于设备文件,库函数没有封装相应的API,则只能选择系统调用

【文件映射   系统编程  通信  】

库函数内部封装的还是系统调用,系统调用大量数据存入缓冲区中,减少访问内核-硬件的次数,减少中断次数

库函数可以跨平台使用,C语言内定义了宏,可以确认当前Linux/win系统

对比

文件IO编程 1 & 2_第11张图片

4.标准IO

库函数 :fgets fputs ...... ......

缓冲区

类型:全缓冲  行缓冲  无缓冲

#include 
#include 

int main(int argc, char **argv)
{
    // setbuf(stdout,NULL);    //修改为无缓冲

    // setvbuf(stdout, NULL, _IONBF, 0); // 改为无缓冲
    
    char *ptr = (char *)malloc(200); // 设置全缓冲,缓冲区大小为200字节
    setvbuf(stdout, ptr, _IOFBF, 200);

    printf("hello wolrd");
    while (1);
    return 0;
}

fread fwrite是有缓冲区的!       全缓冲or行缓冲?

 char *src = "hello world";
    FILE *fp = fopen(argv[1], "a+");
    setvbuf(fp, NULL, _IONBF, 0);//注释此行,则无法写入,说明fwrite有缓冲区
    fwrite(src, 1, strlen(src), fp);
    while (1);
    
    return 0;

清空缓冲区函数

  1. int fflush(FILE*Stream);        清空缓冲区
  2. int flushall()        清空所有打开文件所对应的缓冲区

提升读写效率的一种方法就是适当的选择大的缓冲区,以减少IO操作,或者:

  1. 使用存储映射:原理是内存读写效率高于硬盘——系统调用提升效率的方法
  2. 合并多次写入/读取操作
  3. 异步 I/O
  4. 使用合适的缓冲方式:根据不同的场景和需求,选择合适的缓冲方式,例如行缓冲、全缓冲或无缓冲。
  5. 优化代码结构和算法

在标准I/O库中,`fwrite` 函数通常会将数据存储在缓冲区中,而不会立即将其写入文件。这是为了提高性能,在一次 `fwrite` 调用中可以一次性写入多个数据。当缓冲区满了、文件关闭或者显式地调用了 `fflush` 函数时,缓冲区的数据才会被写入文件。

`fflush` 函数被用来刷新(清空)缓冲区,将缓冲中的数据立即写入到文件中。

参数 `stream` 是要刷新的文件流指针。当 `fflush` 函数被调用时,它会将缓冲区中的内容写入文件,并清空缓冲区。这样可以确保数据被立即写入文件,而不需要等待缓冲区满或者文件关闭。

在示例代码中,将文件流 `fp` 设置为行缓冲模式后,`fwrite` 函数将字符串 `"hello world"` 写入缓冲区。然后,使用 `fflush(fp)` 显式地刷新缓冲区,将数据写入文件。这样可以确保数据被写入文件,而不需要等待缓冲区满或者程序结束。

请注意,`fflush` 函数调用时会导致I/O操作,因此在频繁执行 `fflush` 的情况下,可能会影响程序的性能。因此,最好使用缓冲机制并在适当的时机使用 `fflush` 来确保数据写入文件

5.高级IO

mmap:映射

文件IO编程 1 & 2_第12张图片

文件IO编程 1 & 2_第13张图片

文件IO编程 1 & 2_第14张图片

文件IO编程 1 & 2_第15张图片

 

fstat 、lstat和stat获取文件属性:

文件IO编程 1 & 2_第16张图片

ftruncate:

int ftruncate(int fd, off_t length);

导致由fd引用的常规文件被截断为精确长度字节的大小。

如果先前的文件大于此大小,则会丢失额外的数据。如果先前的文件较短,则对其进行扩展,并且扩展部分读取为空字节('\0')。

使用ftruncate(),文件必须打开才能写入;使用truncate(),文件必须是可写的。

使用以上三个函数,以文件映射方式实现文件拷贝:

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

#include 

int main(int argc, char **argv)
{
    // 检查命令行参数
    if (argc != 3)
    {
        printf("input error!");
        exit(-1);
    }

    // 打开要读取的文件
    int fd1 = open(argv[1], O_RDWR);
    if (fd1 == -1)
    {
        perror("fd1 error:");
        exit(-1);
    }

    // 打开要写入的文件
    int fd2 = open(argv[2], O_RDWR | O_CREAT, 0777);
    if (fd2 == -1)
    {
        perror("fd2 error :");
        exit(-1);
    }

    // 确认要读取文件的大小,通过fstat
    struct stat file1;
    fstat(fd1, &file1);

    // 映射要读取的文件,以及判断映射是否成功
    char *src = mmap(NULL, file1.st_size, PROT_EXEC | PROT_WRITE | PROT_READ, MAP_SHARED, fd1, 0);
    if (src == MAP_FAILED)
    {
        perror("map1 error :");
        exit(-1);
    }

    // 将即将打开(新建)的文件,扩容,防止后续写入失败
    ftruncate(fd2, file1.st_size);

    // 映射要写入的文件,以及判断映射是否成功
    char *des = mmap(NULL, file1.st_size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
    if (des == MAP_FAILED)
    {
        perror("map2 error :");
        exit(-1);
    }

    // 直接对两个映射的地址执行操作
    strncpy(des, src, file1.st_size);
    return 0;
}

文件描述符和文件指针转换

1. fd-->fp

文件IO编程 1 & 2_第17张图片

文件IO编程 1 & 2_第18张图片

2. fp-->fd

文件IO编程 1 & 2_第19张图片

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

int main(int argc, char **argv)
{
    //定义初始化一个fd
    int fd1=open("a.txt",O_RDWR);
    if(fd1==-1)
    {
        perror("fd1:");
        exit(-1);
    }

    //转换为fp
    FILE*fp1=fdopen(fd1,"a+");
    if(fp1==NULL)
    {
        perror("fdope error:");
        exit(-1);
    }
    fwrite("fdopen is ok,hha",16,1,fp1);

    //转换为fd
    int fd2=fileno(fp1);
    if(fd2<=2)
    {
        perror("fileno error");
        exit(-1);
    }
    //移动读写位置到文件首
    int lseek_num=lseek(fd2,0,SEEK_SET);
    if(lseek_num==-1)
    {
        perror("lseek error");
        exit(-1);
    }
    //读取尝试
    char buffer[1024];
    memset(buffer,0,sizeof(buffer));
    if(read(fd2,buffer,25)==-1)
    {
        perror("read error:");
        exit(-1);
    }
    buffer[25]='\0';
    printf("%s\n",buffer);

    return 0;
    }

文件描述符的重定向——【另一种实现文件拷贝的方式(read——write)】

dup和dup2

文件IO编程 1 & 2_第20张图片

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

int main(int argc, char **argv)
{
    int fd1 = open(argv[1], O_RDWR);
    if (fd1 == -1)
    {
        perror("fd1 error");
        exit(-1);
    }

    int fd2 = open(argv[2], O_RDWR | O_CREAT, S_IRWXG | S_IRWXU | S_IRWXO);
    if (fd2 == -1)
    {
        perror("fd2 error");
        exit(-1);
    }
    // 关闭 0:stdin文件,然后将0重定向为fp1所指文件
    close(0);
    dup2(fd1, 0);
    char buffer[1024] = {0};

    // 获取重定向后的fp1指向的文件大小,再读出来到buffer
    struct stat file1;
    fstat(0, &file1);
    read(0, buffer, file1.st_size);
    // scanf("%s", buffer);      //会不能读取空格和回车

    // 关闭1:stdout文件,然后将1重定向为fp2所指文件
    close(1);
    dup2(fd2, 1);

    // 将buffer内的内容输出到fp2所指向的文件
    printf("%s\n", buffer);

    close(fd1);
    close(fd2);

    return 0;
}

应用于:网络读取重定向

fcntl  

1.fcntl

 int flag = fcntl(fd1, F_GETFD); // 获取fd1的flags
    flag = flag | O_APPEND;         // flag加上O_APPEND
    fcntl(fd1, F_SETFL, flag);      // 重新设置flags

2.fcntl  vs dup2文件IO编程 1 & 2_第21张图片

fcntl如果不显式的先close(1);那么就无法重定向!!!

阻塞

普通文件无阻塞——读到数据就成功返回,没读到数据返回0,总之不会堵塞,所以一般认为非阻塞比较好,效率更高

大多数套接字 、设备文件会出现阻塞读取——没有数据就会阻塞,节省了CPU资源,但是程序运行效率低

如何设置成非阻塞呢?

文件IO编程 1 & 2_第22张图片

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

int main(int argc, char **argv)
{
    // 标准输入stdin也是阻塞读取————【没有读到数据就会一直停着】
    char buffer[1024];

    int flags = fcntl(0, F_GETFL);
    flags = flags | O_NONBLOCK;     // 加上非阻塞的打开权限
    fcntl(1, F_SETFL, flags);

    read(0, buffer, sizeof(buffer) - 1);
    buffer[1023] = '\0';
    printf("buffer is=%s\n", buffer);

    // 读取鼠标坐标(阻塞)
    int mouse = open(argv[1], O_RDWR); //
    if (mouse == -1)
    {
        perror("mouse error");
        exit(-1);
    }
    int mou_loc;
    read(mouse, &mou_loc, sizeof(mou_loc));
    printf("%d\n", mou_loc);

    return 0;
}

seletc函数

文件IO编程 1 & 2_第23张图片

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

int main(int argc, char **argv)
{
    int fd = open("/dev/input/mouse0", O_RDWR | O_CREAT);
    if (fd == -1)
    {
        perror("fd open error");
        exit(-1);
    }
    fd_set set1;
    FD_SET(0, &set1);
    FD_SET(fd, &set1);

    fd_set allset;

    struct timeval time1;
    time1.tv_sec = 3;
    time1.tv_usec = 0;

    // while (1)
    // {
        allset = set1;      //如果是循环判断,则需要此行

        int ret = select(fd + 1, &allset, NULL, NULL, &time1);
        //返回值: -1:出错      0:没有检测到/超时          n:变化的文件描述符个数

        if (FD_ISSET(0, &allset) > 0)   
        {
            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            read(0, buffer, sizeof(buffer) - 1);
            printf("%s\n",buffer);
        }
        if (FD_ISSET(fd, &allset) > 0)      //检测fd是不是在allset内,
        {
            int mou_loc;
            read(fd, &mou_loc, sizeof(mou_loc));
            printf("mouse location=%d\n", mou_loc);
        }
    // }
    printf("select is over\n");
    close(fd);
    return 0;
}

select-多文件的轮询:(数组)

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

int main(int argc, char **argv)
{
    int fd = open("/dev/input/mouse0", O_RDWR | O_CREAT);
    if (fd == -1)
    {
        perror("fd open error");
        exit(-1);
    }

    int all_fd[1024]; // 定义一个文件描述符数组,全部初始化为-1————以用于监听时的轮询
    for (int i = 0; i < 1024; ++i)
    {
        all_fd[i] = -1;
    }

    fd_set set1; // 文件描述符组 添加 stdin:0描述符
    FD_SET(0, &set1);
    for (int i = 0; i < 1024; ++i)
    {
        if (all_fd[i] == -1)
        {
            all_fd[i] = 0;
            break;
        }
    }
    FD_SET(fd, &set1); // 文件描述符组 添加 鼠标:fd描述符
    for (int i = 0; i < 1024; ++i)
    {
        if (all_fd[i] == -1)
        {
            all_fd[i] = fd;
            break;
        }
    }
    fd_set allset;
    while (1) // 循环  以轮询文件描述组内,哪个文件描述符可读写变化了
    {
        allset = set1;

        int ret = select(fd + 1, &allset, NULL, NULL, NULL); // null  一直等待
        if (ret < 0)                                         //<0   说明没有变化
        {
            perror("select error");
            exit(-1);
        }
        if (ret > 0)                                         //>0 说明有文件变化了
        {
/**********************         轮询的核心代码         *******************/
            for (int i = 0; i < 1024; ++i)                   
            {
                if (all_fd[i] != -1)
                {
                    if (FD_ISSET(all_fd[i], &allset) > 0)
                    {
                        if (0 == all_fd[i])
                        {
                            char buffer[1024];
                            memset(buffer, 0, 1024);
                            read(0, buffer, sizeof(buffer) - 1);
                            buffer[1023] = '\0';
                            printf("%s\n", buffer);
                        }
                        if (fd == all_fd[i])
                        {
                            int location;
                            read(fd, &location, sizeof(int));
                            printf("%d\n", location);
                        }
                        if (0 == --ret)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
    printf("select is over\n");
    close(fd);
    return 0;
}

缺点:       

1. 范围受限,fd_set()是一个数组,静态分配空间,固定大小是1024

2. 轮询 :全盘轮询,效率最低

优点:

1. 跨平台,win32下也可以实现

轮询和监听

轮询(Polling)和监听(Listening)是两种不同的输入/输出(I/O)处理机制。

1. 轮询(Polling):
   - 概念:在轮询机制中,程序会不断地主动查询(轮询)是否有新的I/O事件发生,然后采取相应的操作。
   - 工作原理:当程序需要等待某个I/O事件时,它会不断地查询该事件是否已经就绪,如果还没就绪就继续查询直至事件就绪。
   - 特点:简单易实现,但是可能会造成CPU资源的浪费,因为程序需要不断地查询事件状态,如果没有事件发生就会浪费CPU时间。

2. 监听(Listening)
   - 概念:在监听机制下,程序会将对I/O事件的管理交给操作系统,通过注册回调函数(或事件处理函数)来处理事件。
   - 工作原理:程序将自己注册到操作系统的事件管理机制中,然后操作系统在相应的事件发生时,会调用注册的回调函数来处理事件。
   - 特点:由操作系统管理事件,程序只需等待事件发生和处理就好,避免了不断轮询造成的资源浪费。

多路I/O复用

是一种I/O处理技术,它将多个I/O流集中到一个线程中处理,并且能实时监测多个I/O事件的发生情况。在传统的单路I/O处理中,每个I/O流在一个线程中独占一个处理线程,而多路I/O复用可以通过轮询或监听等机制,同时处理多个I/O事件。

多路I/O复用的特点是,采用了非阻塞I/O的方式,可以在一个线程中同时监听多个I/O事件,当有事件发生时,触发相应的处理程序,实现并发处理多个I/O操作。这种技术在服务器应用中非常有用,可以提高系统的吞吐量和响应速度。

常见的多路I/O复用模型有基于轮询(如select、poll)和基于事件监听(如epoll)两种。这些技术能够有效地减少资源的占用,提高系统的性能。

poll(链表)

函数原型:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

其中:

1. struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

2. nfds:类型为nfds_t,是fds数组中元素的个数

3.

timeout > 0:表示超时时间,poll函数会等待指定的毫秒数,直到有事件发生或超时。
timeout == 0:表示立即返回,不等待事件发生。
timeout < 0:表示无限等待,poll函数会一直等待直到有事件发生。

返回值:

> 0:表示有一个或多个文件描述符就绪,返回值为就绪的文件描述符个数。
0:表示超时,没有文件描述符就绪。
-1:表示出错,可以通过errno变量获取具体的错误码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char **argv)
{
    int fd1 = open("/dev/input/mouse0", O_RDWR | O_CREAT, 0777);
    if (fd1 == -1)
    {
        perror("fd1 error");
        exit(-1);
    }

    struct pollfd fd[2];        //1.struct结构体内有:int fd;  short int events;  short int revents
    fd[0].fd = 0;
    fd[0].events = POLLIN;
    fd[1].fd = fd1;
    fd[1].events = POLLIN;

    while (1)
    {
        int ret = poll(fd, 2, -1);
        if (ret > 0)
        {
            for (int i = 0; i < 2; ++i)
            {
                if (fd[i].events == fd[i].revents)
                {
                    if (fd[i].fd == 0)
                    {
                        char buffer[64];
                        memset(buffer, 0, sizeof(buffer));
                        read(0, buffer, sizeof(buffer));
                        printf("buffer=%s\n", buffer);
                    }
                    else if (fd[i].fd == fd1)
                    {
                        int location;
                        read(fd1, &location, sizeof(int));
                        printf("location=%d\n", location);
                    }
                    if (--ret == 0)
                    {
                        break;
                    }
                    fd[i].revents=0;
                }
            }
        }
    }
    return 0;
}

epoll(红黑树)

后续网络编程会涉及到

你可能感兴趣的:(C语言,开发语言,c语言,linux,ubuntu)