LinuxC—高级IO

news2024/10/6 2:28:47

高级IO

1 非阻塞IO/有限状态机编程

1.1 基本概念

定义

有限状态机(Finite State Machine) 缩写为 FSM,状态机有 3 个组成部分:状态、事件、动作。

  • 状态:所有可能存在的状态。包括当前状态和条件满足后要迁移的状态。
  • 事件:也称为转移条件,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是* 必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

作用

用来解决复杂流程的问题:

  • 简单流程:一个程序的自然流程是结构化
  • 复杂流程:一个程序的自然流程不是结构化的

自然流程即解决问题的直接思路

1.2 有限状态机实现文件复制

分析

实现的功能就是从文件1中复制文件到文件2或者从文件2中复制文件到文件1,有限状态机共有4个状态,分别是read读态,write写态,exception出错态和terminate结束态。

FSM模型图如下:

在这里插入图片描述

实现

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024
enum {
    STATE_R = 1, //读态
    STATE_W = 2, //写态
    STATE_EX = 3, //异常态
    STATE_T = 4 //退出态
};

struct fsm_st {
    int state;//当前状态机的状态
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;
};

//状态机推动函数
static void fsm_driver(struct fsm_st *fsm) {
    int ret;
    switch (fsm->state) {
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
            if (fsm->len == 0) fsm->state = STATE_T; // 读完切换到结束态
            else if (fsm->state < 0) {
                if (errno == EAGAIN) fsm->state = STATE_R; // 假错接着读
                else {
                    fsm->state = STATE_EX; // 真错切换到异常态
                    fsm->errstr = "read()";
                }
            } else {
                fsm->pos = 0;
                fsm->state = STATE_W; // 读成功切换为写态
            }
            break;
        case STATE_W:
            ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
            if (ret < 0) {
                if (errno == EAGAIN) fsm->state = STATE_W;
                else {
                    fsm->errstr = "write()";
                    fsm->state = STATE_EX;
                }
            } else {
                fsm->pos += ret;
                fsm->len -= ret;
                if (ret == 0) fsm->state = STATE_R;
                else fsm->state = STATE_W;
            }
            break;
        case STATE_EX:
            perror(fsm->errstr);
            fsm->state = STATE_T;
            break;
        case STATE_T:
            break;
        default:
            abort();
    }
}

static void relay(int fd1, int fd2) {
    int fd1_save, fd2_save;
    struct fsm_st fsm12, fsm21;

    // 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
    // 状态机初始化
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);
    }

    //恢复文件描述符之前的状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);

}

int main(int argc, char **argv) {

    // 模拟用户打开两个设备的操作
    int fd1, fd2;
    fd1 = open(TTY1, O_RDWR);
    if (fd1 < 0) {
        perror("open()");
        exit(1);
    }
    fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
    if (fd2 < 0) {
        close(fd1);
        perror("open()");
        exit(1);
    }
    // 调用数据中继函数
    relay(fd1, fd2);

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

1.3 中继引擎

头文件

#ifndef LINUX_RELAYER_H
#define LINUX_RELAYER_H

#include <stdint-gcc.h>
#include <time.h>

#define REL_JOBMAX 10000
enum {
    STATE_RUNNING = 1,
    STATE_CANCELED,
    STATE_OVER
};


/**
 * 表示任务状态的结构体,这个结构体是我们希望用户看到的,当我们实际操作的时候可以不是这个结构体
 * 即实现结构体部分的隐藏和封装
 */
struct rel_stat_st {
    int state;
    int fd1;
    int fd2;
    int64_t count12, count21; //通讯的字节数
    time_t start, end; //记录任务的起始时间
};

/**
 * 添加任务
 * @param fd1
 * @param fd2
 * @return >=0 表示成功,返回当前任务id;-EINVAL 表示失败参数非法; -ENOSPC 表示失败,任务数组满
 * -ENOMEM 表示失败,内存分配有误
 */
int rel_addjob(int fd1, int fd2);

/**
 * 取消任务
 * @param id
 * @return =0 表示指定任务已经成功取;-EINVAL 表示失败,参数非法; -EBUSY 失败,任务被重复取消
 */
int rel_canceljob(int id);

/**
 * 给任务收尸
 * @param id 任务id
 * @param old 被收尸任务的状态
 * @return =0 成功,指定任务已终止并返回状态; -EINVAL 失败,参数非法
 */
int rel_waitjob(int id, struct rel_stat_st *old);

/**
 * 返回id任务的状态
 * @param id 任务id
 * @param stat 任务状态
 * @return 0 成功,指定任务状态已返回; -EINVAL 失败,参数非法
 */
int rel_statjob(int id, struct  rel_stat_st *stat);

#endif //LINUX_RELAYER_H

实现

#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "relayer.h"

#define BUFSIZE 1024

enum {
    STATE_R = 1, //读态
    STATE_W = 2, //写态
    STATE_EX = 3, //异常态
    STATE_T = 4 //退出态
};

struct rel_fsm_st {
    int state;//当前状态机的状态
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    size_t len;
    int pos;
    char *errstr;
    int64_t count;
};

struct rel_job_st {
    int fd1, fd2;
    int job_state;
    struct rel_fsm_st fsm12, fsm21;
    time_t start, end;
    int fd1_save, fd2_save;
};

static struct rel_job_st * rel_job[REL_JOBMAX];
static pthread_mutex_t mut_rel_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static pthread_t tid_relayer;

static int get_free_pos_unlocked() {
    int i;
    for (i = 0; i < REL_JOBMAX; i++) {
        if (rel_job[i] == NULL) {
            return i;
        }
    }
    return -1;
}

//状态机推动函数
static void fsm_driver(struct rel_fsm_st *fsm) {
    int ret;
    switch (fsm->state) {
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
            if (fsm->len == 0) fsm->state = STATE_T; // 读完切换到结束态
            else if (fsm->state < 0) {
                if (errno == EAGAIN) fsm->state = STATE_R; // 假错接着读
                else {
                    fsm->state = STATE_EX; // 真错切换到异常态
                    fsm->errstr = "read()";
                }
            } else {
                fsm->pos = 0;
                fsm->state = STATE_W; // 读成功切换为写态
            }
            break;
        case STATE_W:
            ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
            if (ret < 0) {
                if (errno == EAGAIN) fsm->state = STATE_W;
                else {
                    fsm->errstr = "write()";
                    fsm->state = STATE_EX;
                }
            } else {
                fsm->pos += ret;
                fsm->len -= ret;
                fsm->count += ret;
                if (ret == 0) fsm->state = STATE_R;
                else fsm->state = STATE_W;
            }
            break;
        case STATE_EX:
            perror(fsm->errstr);
            fsm->state = STATE_T;
            break;
        case STATE_T:
            break;
        default:
            abort();
    }
}

static void * thr_relayer(void *p) {
    int i;
    while (1) {
        pthread_mutex_lock(&mut_rel_job);
        for (i = 0; i < REL_JOBMAX; i++) {
            if (rel_job[i] != NULL && rel_job[i]->job_state == STATE_RUNNING) {
                fsm_driver(&rel_job[i]->fsm12);
                fsm_driver(&rel_job[i]->fsm21);
                if (rel_job[i]->fsm12.state == STATE_T && rel_job[i]->fsm21.state == STATE_T) {
                    rel_job[i]->job_state =STATE_OVER;
                    rel_job[i]->end = time(NULL);
                }
            }
        }
        pthread_mutex_unlock(&mut_rel_job);
    }
}

static void module_unload() {
    int i;

    pthread_cancel(tid_relayer);
    pthread_join(tid_relayer, NULL);

    pthread_mutex_lock(&mut_rel_job);
    for (i = 0; i < REL_JOBMAX; i++) {
        if (rel_job[i] != NULL) {
            fcntl(rel_job[i]->fd1, F_SETFL, rel_job[i]->fd1_save);
            fcntl(rel_job[i]->fd2, F_SETFL, rel_job[i]->fd2_save);
            free(rel_job[i]);
        }
    }
    pthread_mutex_unlock(&mut_rel_job);
}

/**
 * 该函数用来创建一个线程,持续的推状态机
 */
static void module_load(void) {
    int err;
    err = pthread_create(&tid_relayer, NULL, thr_relayer, NULL);
    if (err < 0) {
        fprintf(stderr, "pthread_create():%s", strerror(err));
        exit(1);
    }
    atexit(module_unload);
}

int rel_addjob(int fd1, int fd2) {
    struct rel_job_st *me;
    int pos;

    pthread_once(&init_once, module_load);

    me = malloc(sizeof (*me));
    if (me == NULL) return -ENOMEM;
    //初始化任务
    me->fd1 = fd1;
    me->fd2 = fd2;
    me->job_state = STATE_RUNNING;
    me->start = time(NULL);

    //保证fd是以非阻塞的方式打开的
    me->fd1_save = fcntl(me->fd1, F_GETFL);
    fcntl(me->fd1, F_SETFL, me->fd1_save | O_NONBLOCK);
    me->fd2_save = fcntl(me->fd2, F_GETFL);
    fcntl(me->fd2, F_SETFL, me->fd2_save | O_NONBLOCK);
    //初始化状态机
    me->fsm12.sfd = fd1;
    me->fsm12.dfd = fd2;
    me->fsm12.state = STATE_R;
    me->fsm21.sfd = fd2;
    me->fsm21.dfd = fd1;
    me->fsm21.state = STATE_R;

    //在任务池中找位置
    pthread_mutex_lock(&mut_rel_job);
    pos = get_free_pos_unlocked();
    if (pos == -1) {
        pthread_mutex_unlock(&mut_rel_job);
        fcntl(me->fd1, F_SETFL, me->fd1_save);
        fcntl(me->fd2, F_SETFL, me->fd2_save);
        free(me);
        return -ENOSPC;
    }
    rel_job[pos] = me;
    pthread_mutex_unlock(&mut_rel_job);
    return pos;
}

/**
 * 取消任务
 * @param id
 * @return =0 表示指定任务已经成功取;-EINVAL 表示失败,参数非法; -EBUSY 失败,任务被重复取消
 */
int rel_canceljob(int id) {
    if (id < 0 || id >= REL_JOBMAX) return -EINVAL;
    pthread_mutex_lock(&mut_rel_job);
    if (rel_job[id] == NULL) {
        pthread_mutex_unlock(&mut_rel_job);
        return -EINVAL;
    }
    rel_job[id]->job_state = STATE_CANCELED;
    rel_job[id]->end = time(NULL);
    pthread_mutex_unlock(&mut_rel_job);
}

/**
 * 给任务收尸
 * @param id 任务id
 * @param old 被收尸任务的状态
 * @return =0 成功,指定任务已终止并返回状态; -EINVAL 失败,参数非法
 */
int rel_waitjob(int id, struct rel_stat_st *old) {
    pthread_mutex_lock(&mut_rel_job);
    old->start = rel_job[id]->start;
    old->end = rel_job[id]->end;
    old->state = rel_job[id]->job_state;
    old->count12 = rel_job[id]->fsm12.count;
    old->count21 = rel_job[id]->fsm21.count;
    old->fd1 = rel_job[id]->fd1;
    old->fd2 = rel_job[id]->fd2;
    pthread_mutex_unlock(&mut_rel_job);
}

/**
 * 返回id任务的状态
 * @param id 任务id
 * @param stat 任务状态
 * @return 0 成功,指定任务状态已返回; -EINVAL 失败,参数非法
 */
int rel_statjob(int id, struct  rel_stat_st *stat) {
    pthread_mutex_lock(&mut_rel_job);
    stat->start = rel_job[id]->start;
    stat->end = rel_job[id]->end;
    stat->state = rel_job[id]->job_state;
    stat->count12 = rel_job[id]->fsm12.count;
    stat->count21 = rel_job[id]->fsm21.count;
    stat->fd1 = rel_job[id]->fd1;
    stat->fd2 = rel_job[id]->fd2;
    pthread_mutex_unlock(&mut_rel_job);
}

2 IO多路复用

2.1 select

基本概念

  • select定义——以事件为单位组织文件描述符
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
  • 参数含义

    nfds为所监视文件描述符中最大的那个+1

    readfds为关心读状态的文件描述符集合

    writefds为关心写状态的文件描述符集合

    exceptfds为关心异常的文件描述符集合

    timeout为超时设置,无则死等到感兴趣的时间发生,为阻塞的

  • 返回值

    成功返回三个文件描述符集合中的总文件描述符数,同时将结果存放到三个集合中,超时返回0,失败返回-1并设置errno同时清空这三个集合

  • timeval结构体

struct timeval {
    long    tv_sec;         /* seconds */
    long    tv_usec;        /* microseconds */
};
  • 文件描述符处理函数
void FD_CLR(int fd, fd_set *set);//从set中删除fd
int  FD_ISSET(int fd, fd_set *set);//判断fd是否在set中
void FD_SET(int fd, fd_set *set);//将fd放入到set中
void FD_ZERO(fd_set *set);//清空文件描述符

select重构中继函数

​ select使用步骤:

  • 构建监视任务,即fd_set集合
  • 调用select进行监视
  • 监视结果处理
static void relay(int fd1, int fd2) {
    int fd1_save, fd2_save;
    struct fsm_st fsm12, fsm21;
    fd_set rset, wset;

    // 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
    // 状态机初始化
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
        //监视任务
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        if (fsm12.state == STATE_R) {
            FD_SET(fsm12.sfd, &rset);
        }
        if (fsm12.state == STATE_W) {
            FD_SET(fsm12.dfd, &wset);
        }
        if (fsm21.state == STATE_R) {
            FD_SET(fsm21.sfd, &rset);
        }
        if (fsm21.state == STATE_W) {
            FD_SET(fsm21.dfd, &wset);
        }

        //监视
        if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
            if (select(max(fd1, fd2) + 1, &rset, &wset, NULL, NULL) < 0) {
                if (errno == EINTR) continue;
                perror("select()");
                exit(1);
            }
        }
        //查看监视结果
        if (FD_ISSET(fd1, &rset) || FD_ISSET(fd2, &wset) || fsm12.state > STATE_AUTO) {
            fsm_driver(&fsm12);
        }
        if (FD_ISSET(fd2, &rset) || FD_ISSET(fd1, &wset) || fsm21.state > STATE_AUTO) {
            fsm_driver(&fsm21);
        }
    }

    //恢复文件描述符之前的状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);

}

select特点

优点:

  • 可移植性好

缺点

  • 监视现场和监视结果放在同一块空间,即结果会覆盖监视现场
  • nfds存在溢出的风险
  • 监视的事件过于单一

2.2 poll

基本概念

  • 定义——以文件描述符为单位组织事件
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • 参数定义

    fds:监视的pollfd数组

    nfds:fds数组的大小

    timeout:超时时间,毫秒为单位,0表示非阻塞,-1表示阻塞

  • 返回值

    成功返回一个正数表示有多少事件发生,失败返回-1并设置errno

  • pollfd结构体

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events 关心的事件*/
    short revents;    /* returned events 发生的事件*/
}
  • events/revents是一个位图
POLLIN There is data to read.

POLLPRI
There is some exceptional condition on the file descriptor.  Possibilities include:


The bits that may be set/returned in events and revents are defined in <poll.h>:

POLLIN There is data to read.

POLLPRI
POLLPRI
There is some exceptional condition on the file descriptor.  Possibilities include:

*  There is out-of-band data on a TCP socket (see tcp(7)).

*  A pseudoterminal master in packet mode has seen a state change on the slave (see ioctl_tty(2)).

*  A cgroup.events file has been modified (see cgroups(7)).

POLLOUT
Writing is now possible, though a write larger that the available space in a socket or pipe will still block (unless O_NONBLOCK is set).

POLLRDHUP (since Linux 2.6.17)
Stream  socket  peer  closed connection, or shut down writing half of connection.  The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to
obtain this definition.

POLLERR
Error condition (only returned in revents; ignored in events).  This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed.

POLLHUP
Hang up (only returned in revents; ignored in events).  Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its
end of the channel.  Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.

POLLNVAL
Invalid request: fd not open (only returned in revents; ignored in events).

When compiling with _XOPEN_SOURCE defined, one also has the following, which convey no further information beyond the bits listed above:

POLLRDNORM
Equivalent to POLLIN.

POLLRDBAND
Priority band data can be read (generally unused on Linux).

POLLWRNORM
Equivalent to POLLOUT.

POLLWRBAND
Priority data may be written.

poll重构中继函数

static void relay(int fd1, int fd2) {
    int fd1_save, fd2_save;
    struct fsm_st fsm12, fsm21;
    struct pollfd pfd[2];

    // 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
    // 状态机初始化
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    pfd[0].fd = fd1;
    pfd[1].fd = fd2;

    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {

        //布置监视任务
        pfd[0].events = 0;
        pfd[1].events = 0;
        if (fsm12.state == STATE_R) {
            pfd[0].events |= POLLIN;
        }
        if (fsm12.state == STATE_W) {
            pfd[1].events |= POLLOUT;
        }
        if (fsm21.state == STATE_R) {
            pfd[1].events |= POLLIN;
        }
        if (fsm21.state == STATE_W) {
            pfd[0].events |= POLLOUT;
        }

        //监视
        if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
            while (poll(pfd, 2, -1) < 0) {
                if (errno == EINTR) continue;
                perror("select()");
                exit(1);
            }
        }
        //查看监视结果
        if ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLOUT) || fsm12.state > STATE_AUTO) {
            fsm_driver(&fsm12);
        }
        if ((pfd[1].revents & POLLIN) || (pfd[0].revents & POLLOUT) || fsm21.state > STATE_AUTO) {
            fsm_driver(&fsm21);
        }

    }

    //恢复文件描述符之前的状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);
}

特点

  • 可移植

2.3 epoll

定义

  • epoll_create()创建一个epoll实例,成功返回一个fd(epoll实例可以看做一个文件,所以得注意使用完后调用close关闭),失败返回-1并设置errno
int epoll_create(int size);//size随便取值即可
  • epoll_ctl() 控制一个epoll实例,对epfd实例中的fd文件描述符执行op操作某行为event
//epfd即epoll实例对应的fd
//op进行什么样的操作
//fd操作的对象
//event即针对的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • epoll_wait() 往外取正在发生的事件,从epfd实例中去maxevents个事件到events数组中去,timeout为超时时间
int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
  • 相关结构体
typedef union epoll_data {
    void    *ptr;
    int      fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;    /* Epoll events 感兴趣的事件,是一个位图和poll中类似*/
    epoll_data_t data;      /* User data variable */
};

epoll重构中继函数

static void relay(int fd1, int fd2) {
    int fd1_save, fd2_save;
    struct fsm_st fsm12, fsm21;
    int epfd;
    struct epoll_event ev;

    // 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
    // 状态机初始化
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    epfd = epoll_create(2);
    if (epfd < 0) {
        perror("epoll_create()");
        exit(1);
    }
    ev.events = 0;
    ev.data.fd = fd1;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &ev);
    ev.events = 0;
    ev.data.fd = fd2;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &ev);

    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
        //布置监视任务
        ev.events = 0;
        ev.data.fd = fd1;
        if (fsm12.state == STATE_R) {
            ev.events |= EPOLLIN;
        }
        if (fsm21.state == STATE_W) {
            ev.events |= EPOLLOUT;
        }
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd1, &ev);

        ev.events = 0;
        ev.data.fd = fd2;
        if (fsm12.state == STATE_W) {
            ev.events |= EPOLLOUT;
        }
        if (fsm21.state == STATE_R) {
            ev.events |= EPOLLIN;
        }
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd2, &ev);

        //监视
        if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
            while (epoll_wait(epfd, &ev, 1, -1) < 0) {
                if (errno == EINTR) continue;
                perror("epoll_wait()");
                exit(1);
            }
        }
        //查看监视结果
        if ((ev.data.fd = fd1 && ev.events & EPOLLIN) || (ev.data.fd == fd2 && ev.events & EPOLLOUT) || fsm12.state > STATE_AUTO) {
            fsm_driver(&fsm12);
        }
        if ((ev.data.fd = fd1 && ev.events & EPOLLOUT) || (ev.data.fd == fd2 && ev.events & EPOLLIN) || fsm21.state > STATE_AUTO) {
            fsm_driver(&fsm21);
        }

    }

    //恢复文件描述符之前的状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);
    close(epfd);
}

3 其他读写函数

相关函数

向多个内存空间(iovcnt个iovec)中写数据

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

iovec结构体

struct iovec {
    void  *iov_base;    /* Starting address 起始地址*/
    size_t iov_len;     /* Number of bytes to transfer 空间大小*/
};

4 存储映射IO

mmap函数

  • 定义 将内核中的一块内存区域和用户进程空间的内存做一个映射后返回一个指针用于操作这块数据
void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);//定义映射
int munmap(void *addr, size_t length); //解除映射
  • 参数解析
    • addr 内存映射地址的起始地址
    • length即映射内存大小
    • prot即这块内存的属性,即定义自己的操作权限
    • flags特殊要求
    • fd映射的文件
    • offset映射文件的偏移量
  • 返回值:成功返回映射地址的起始地址,失败返回MAP_FAILED,并设置errno

函数示例:计算文件中字符’a’的数目

int main(int argc, char **argv) {

    struct stat fst;
    int fd, i, cnt;
    char *p;

    if (argc < 2) {
        fprintf(stderr, "Usage...\n");
        exit(1);
    }
    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open()");
        exit(1);
    }

    if (fstat(fd, &fst) < 0) {
        perror("fstat()");
        exit(1);
    }

    p = mmap(NULL, fst.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) {
        perror("mmap()");
        exit(1);
    }

    close(fd);

    for (i = 0; i < fst.st_size; i++) {
        if (p[i] == 'a') cnt++;
    }

    printf("%d\n", cnt);
    munmap(p, fst.st_size);
    exit(1);
}

5 文件锁

lockf()

  • 定义
int lockf(int fd, int cmd, off_t len);
  • cmd的用法

    F_LOCK:加锁

    F_TLOCK:尝试加锁立即返回

    F_ULOCK:解锁

    F_TEST:测试是否有锁

  • len的含义

    0表示文件有多长锁多长

flock()

  • 定义 给一个文件加锁或解锁
int flock(int fd, int operation);
  • operation:
    • LOCK_SH共享锁
    • LOCK_EX独占锁
    • LOCK_UN解除锁定

===

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179844.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动驾驶环境感知——视觉传感器技术

文章目录1. 摄像头的成像原理1.1 单目视觉传感器的硬件结构1.2 单目视觉的成像原理 –小孔成像模型1.3 单目视觉的成像原理 – 像素坐标系1.4 单目视觉三维坐标系转换 – 外参1.5 单目视觉的坐标系转换 – 从世界坐标点到像素坐标1.6 单目视觉的特性2. 视觉传感器的标定2.1 视觉…

CSS之精灵图

1. 精灵图 1.1 为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 为什么使用精灵图&#xff…

9、断点调试

文章目录9、断点调试9.1 为什么需要Debug9.2 Debug的步骤1 添加断点2 启动调试3 单步调试工具介绍9.3 多种Debug情况介绍1 行断点2 方法断点3 字段断点4 条件断点5 异常断点6 线程断点7 强制结束9.4 自定义调试数据视图9.5 常见问题【尚硅谷】idea实战教程-讲师&#xff1a;宋红…

Linux安装mysql--CentOS系统

Linux安装mysql 安装包&#xff1a; https://pan.baidu.com/s/10xvFpfl4nTktaEdhKbY3og 首先启动虚拟机&#xff0c;我是用FinalShell连接的 然后将下载的安装包上传至Linux系统中&#xff0c;直接rz回车就会跳出选择文件的窗口&#xff0c;选择需要上传的安装包即可等待上传…

计算机网络01_---软考高级系统架构师010

计算机网络知识点汇总: IPV4中有单播,组播,广播.IPV6没有广播 网络标准喝协议中要知道有IEEE802.3 以太网协议 局域网是以太网的一种 然后,IEEE802.11是无线局域网协议. TCP/IP协议族,这里,要知道从网络层开始,这里到传输层,然后传输层有个 TCP协议,这里TCP链接的时候有…

【沐风老师】3DMAX地板生成器插件FloorGenerator使用教程

FloorGenerator地板生成器插件&#xff0c;创建任何形状的地板几何图形&#xff0c;你可以完全控制从斜边到木板倾斜的所有参数。 伴随该地板的是”多重纹理贴图&#xff08;MultiTexture&#xff09;“插件&#xff0c;它使你能够将任意数量的位图随机指定给生成的地板。还提…

听障人士亲述:我们在VRChat用手语交流,成员规模5000人

如果你在B站上搜索VRChat&#xff0c;排在前面的热门视频几乎都是与老外聊天的内容。除了练习语言、交文化流外&#xff0c;你还能在VRChat上遇到不少哇哇乱叫的小孩。作为一款VR社交应用&#xff0c;除了有趣的小游戏外&#xff0c;说话聊天也是VRChat关键的玩法之一。而有这么…

离线ctr特征中心更新

3.8 离线ctr特征中心更新 学习目标 目标 了解特征服务中心的作用应用 无 3.8.1 特征服务中心 特征服务中心可以作为离线计算用户与文章的高级特征&#xff0c;充当着重要的角色。可以为程序提供快速的特征处理与特征结果&#xff0c;而且不仅仅提供给离线使用。还可以作为实时…

【My Electronic Notes系列——直流稳压电源】

目录 序言&#xff1a; &#x1f3c6;&#x1f3c6;人生在世&#xff0c;成功并非易事&#xff0c;他需要破茧而出的决心&#xff0c;他需要永不放弃的信念&#xff0c;他需要水滴石穿的坚持&#xff0c;他需要自强不息的勇气&#xff0c;他需要无畏无惧的凛然。要想成功&…

快速安装OpenShift在Ubuntu系统上并使用

目录 OpenShift简介&#xff1a; 服务器信息 安装Docker 安装OpenShift 访问Dashboard oc常用命令 附 OpenShift简介&#xff1a; OpenShift 是一个开源容器应用平台&#xff0c;由 Red Hat 开发。它建立在 Kubernetes 之上&#xff0c;并提供用于部署、扩展和管理容器…

【Linux】基础:线程的同步与互斥

【Linux】基础&#xff1a;线程的同步与互斥 摘要&#xff1a;本文主要介绍线程的同步与互斥方面的内容&#xff0c;分为理论与实现两部分完成。首先从整体上介绍线程同步与互斥相关概念&#xff0c;在理解概念后对两者分开介绍。在互斥方面&#xff0c;主要介绍内容为互斥量的…

LinuxC—线程

线程 1 线程的基本概念 什么是线程 进程其实是一个容器&#xff0c;当我们在编程的时候实际上是在以线程为单位进行编程&#xff0c;包括处理器的调度也是以线程为单位的&#xff0c;一个进程可以有多个线程&#xff0c;一个进程的多个线程共享相同的进程空间&#xff0c;所以…

设计模式 - 创建型模式_抽象工厂模式

文章目录创建型模式概述Case场景模拟工程模拟早期单机Redis的使用Bad ImplBetter Impl &#xff08;抽象⼯⼚模式重构代码&#xff09;定义适配接⼝实现集群适配器接口代理方式的抽象工厂类单元测试小结创建型模式 创建型模式提供创建对象的机制&#xff0c; 能够提升已有代码…

0、Spring工程构建Spring快速入门Spring配置文件详解注入Sprint相关API

1、Spring工程构建 创建工程项目目录文件夹 IDEA选择项目new一个module 配置案例 aop创建 创建并下载完毕后&#xff0c;点击file选择projert 选择按照的jdk版本 output选择当前目录&#xff0c; 点击右下方apply 选择facets&#xff0c;点击""号选择web 选择当前…

Pinia状态管理

1、Pinia和Vuex的对比 1.1、什么是Pinia呢&#xff1f; Pinia&#xff08;发音为/piːnjʌ/&#xff0c;如英语中的“peenya”&#xff09;是最接近pia&#xff08;西班牙语中的菠萝&#xff09;的词&#xff1b; Pinia开始于大概2019年&#xff0c;最初是作为一个实验为Vue…

Linux使用操作

文章目录各类小技巧&#xff08;快捷键&#xff09;软件安装systemctl软连接日期、时区IP地址、主机名IP地址和主机名虚拟机配置固定IP网络传输下载和网络请求端口进程管理主机状态环境变量上传、下载压缩、解压各类小技巧&#xff08;快捷键&#xff09; 强制停止 Linux某些程…

python语法 dot函数

dot是numpy里的函数&#xff0c;主要用于求向量相乘&#xff0c;矩阵乘法&#xff0c;矩阵与向量乘法一、一维向量相乘要求元素个数相同&#xff0c;相当于求内积&#xff0c;对应元素相乘再相加&#xff0c;“1*3 2*4 11”二、矩阵和矩阵相乘遵循矩阵乘法法则“左行 * 右列”…

高通平台开发系列讲解(WIFI篇)什么是WLAN无线局域网

文章目录 一、什么是WLAN1.1、WLAN发展史1.2、WLAN工作频段二、高通相关文件2.1、配置文件2.2、开机启动2.3、wpa_supplicant沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将基于高通平台介绍什么是无线局域网。 一、什么是WLAN 在WLAN领域被大规模推广和商用的是…

【编程入门】开源记事本(鸿蒙Java版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 本系列对比云笔记&#xff0c;将更为简化&#xff0c;去掉了网络调用&#xff0…

WebSocket 入门:简易聊天室

大家好&#xff0c;我是前端西瓜哥&#xff0c;今天我们用 WebSocket 来实现一个简单的聊天室。 WebSocket 是一个应用层协议&#xff0c;有点类似 HTTP。但和 HTTP 不一样的是&#xff0c;它支持真正的全双工&#xff0c;即不仅客户端可以主动发消息给服务端&#xff0c;服务…