美文网首页
linux内核之异步IO

linux内核之异步IO

作者: wipping的技术小栈 | 来源:发表于2019-12-30 23:22 被阅读0次

一、前言

在嵌入式linux中,除了前面讲到的轮询式IO还有异步IO。异步IO可以在驱动或者文件在处理某一件事情后再想用户空间发出信号,使得应用程序可以不用阻塞或者轮序去做其他事情,知道信号发生后再来处理。这样可以使得我们的应用程序更加灵活,它与轮询IO互为补充。本文着重讲一下异步IO信号的原理,结合简单的应用程序及驱动程序来讲解

二、信号

2.1、可靠信号与不可靠信号

异步IO 有一种常用的实现方式,就是信号。在linux操作系统中,信号一共有 30 个。在PC端的linux中,有些发行版的信号有 64 个,并且分为可靠信号与不可靠信号。其中小于 SIGRTMIN=32 的为不可靠信号,而大于SIGRTMIN并且小于 SIGRTMAX=63 的为可靠信号。

我们可以使用下面的命令来查看操作系统支持的信号,如果所示

kill -l

操作系统信号

那么什么是 **可靠信号 **与 不可靠信号
在执行 信号处理程序 时,linux默认不再接收当前正在处理的信号。所以此时如果内核再次发出信号时,那么会被应用程序忽略掉。这样的信号我们称之为不可靠信号,因为造成了信号丢失。可靠信号则不会丢失,因为可靠信号会被加入信号队列,在系统处理完信号之后再次发出,每一次都会被接收到,不造成信号丢失的现象

关于可靠信号不可靠信号 的详情,请各位读者从其他文章资料获取

2.2、信号应用

2.2.1、信号常用接口

我们在应用层一般情况下有 2 种使用信号的方法,分别是:

  • sighandler_t signal(int signum, sighandler_t handler)
  • int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

前者的操作比较简单,只是为某一个信号注册 处理函数。而后者可用于改变进程接收到信号后的行为,但其使用复杂度也比前者高一些,其中 struct sigaction 结构体如下,我们后面还会再说到该结构体。

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

在设置信号后,我们还需要使用 fcntl 系统调用会相关的驱动或者文件进行一些操作,常用的有

  • F_SETOWN:一般是使用语句 fcntl(common_fd, F_SETOWN, getpid()),对于某些多进程共用的文件描述符,比如标准输入输出,我们要让操作系统知道这些信号要发往哪个进程。因为每一个进程都有标准输入输出,所以我们需要让操作系统知道当前的标准输入输出属于哪个进程,从而可以对进程发送信号

  • F_SETFL:一般是使用语句 fcntl(your_fd, F_SETFL, old_flags | FASYNC),该语句让对应的驱动或者文件启动异步通知机制

  • F_SETSIG:该标志可以设置用户的 自定义信号 来替换掉原本的信号 SIGIO。一般的使用场景是多线程下,如果多个线程使用 SIGIO 作为触发信号且每个线程对该信号的处理函数都不相同,那么这样会造成 SIGIO 处理函数的覆盖,最终只有一个处理函数会被执行。从中可以看出,信号是 进程 属性,也就是在进程范围内,一个信号只有一个处理函数。那么我们可以调用该接口为每个线程指定一个自定义信号来替代 SIGIO,并为自定义信号安装处理函数。那么当驱动或者文件触发 SIGIO 信号时,从线程角度来看则是触发了每个线程自己的自定义信号,那么此时就会执行每个线程自己的处理函数。使用该标志时需要加入编译选项** -D_GNU_SOURCE**,不然会出现 F_SETSIG undeclared 错误

  • F_SETOWN_EX:当我们想要让驱动或文件的触发信号只发送到某一 指定线程 。那么我们就可以使用该标志来设置信号的接收线程。它与 F_SETOWN 的区别在 F_SETOWN_EX 更加细致,可以指定只发送给某个线程;而 F_SETOWN 优先发送给线程,如果接收线程被阻塞,则选择同一进程中的其他线程接收。

以上就是笔者总结出来的 应用层信号 使用方法,信号还有很多其他的高级用法,这里笔者暂时未做深入研究,有兴趣的读者可以自行查阅其他资料,后续笔者有机会再把坑给填上

下面的笔者应用层样例代码,有需要的读者可以借鉴。每个人的内核驱动都不同,读者们可以自行实现内核驱动后来使用该样例代码验证

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <signal.h>
#include <sys/types.h>

#define SIGTEST (SIGRTMIN+1)
void test_sigacftionHandle(int signum , siginfo_t* siginfo, void* NULL_ptr)
{
    /* 在非实时信号下 si_code一直等于128,只有在实时信号下才是内核发送出来的值 */
    printf("si_code = %d, si_band = %ld\n", siginfo->si_code, siginfo->si_band);
}
void test_signalHandle(int signum)
{
    printf("signum = %ld\n", signum);
}
int main()
{
    /* 非实时信号的正常sigaction流程 */
    int fd = 0;
    int old_flags = 0;
    struct sigaction sig_act = {0};
    fd = open("/dev/gpio_device", O_NONBLOCK);  
    fcntl(fd, F_SETOWN, getpid());
    old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | FASYNC);
    sig_act.sa_flags = SA_SIGINFO;
    sig_act.sa_sigaction = test_sigacftionHandle;
    sigaction(SIGIO, &sig_act, NULL);//这样写会提示Real-time signal 1
    while(1)
        sleep(1);
    return 0;

    /* 非实时信号的正常signal流程 */
    int fd = 0;
    int old_flags = 0;
    fd = open("/dev/gpio_device", O_NONBLOCK);  
    fcntl(fd, F_SETOWN, getpid());
    old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | FASYNC);
    signal(SIGIO, test_sigHandle);
    while(1)
        sleep(1);
    return 0;

    /* 使用signal安装sa_sigaction类型的函数会编译错误 */
    int fd = 0;
    int old_flags = 0;
    fd = open("/dev/gpio_device", O_NONBLOCK);  
    fcntl(fd, F_SETOWN, getpid());
    old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | FASYNC);
    signal(SIGIO, test_sigacftionHandle);
    while(1)
        sleep(1);
    return 0;

    /* 设置实时信号后,为SIGIO安装处理函数。当信号发生是会出现 Real-time signal 1 ,并退出程序*/
    int fd = 0;
    int old_flags = 0;
    struct sigaction sig_act = {0};
    fd = open("/dev/gpio_device", O_NONBLOCK);  
    fcntl(fd, F_SETISG, SIGTEST);
    fcntl(fd, F_SETOWN, getpid());
    old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | FASYNC);
    sig_act.sa_flags = SA_SIGINFO;
    sig_act.sa_sigaction = test_sigacftionHandle;
    sigaction(SIGIO, &sig_act, NULL);//这样写会提示
    while(1)
        sleep(1);
    return 0;

    /* 实时信号的正常signal流程 */
    int fd = 0;
    int old_flags = 0;
    struct sigaction sig_act = {0};
    fd = open("/dev/gpio_device", O_NONBLOCK);  
    fcntl(fd, F_SETISG, SIGTEST);
    fcntl(fd, F_SETOWN, getpid());
    old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | FASYNC);
    sig_act.sa_flags = SA_SIGINFO;
    sig_act.sa_sigaction = test_sigacftionHandle;
    sigaction(SIGTEST, &sig_act, NULL);//正常
    while(1)
        sleep(1);
    return 0;

}

2.3、驱动层信号

应用程序 是接收信号的,那么发送信号的则是 内核驱动。在驱动层面,linux提供了 2 个接口来实现信号的发送,分别是:

  • int fasync_helper(int fd, struct file* filp, int on, struct fasync_struct **fapp)
  • void kill_fasync(struct fasync_struct **fp, int sig, int band)

2.3.1 fasync_helper

fasync_helper
    ->fasync_remove_entry or fasync_add_entry

下面是 fasync_helper 相关源码解析部分,其中已经把部分代码给省略去,以简化讲解思路。代码 注释 就是讲解内容

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
    if (!on)
        return fasync_remove_entry(filp, fapp);
    return fasync_add_entry(fd, filp, fapp);
}
int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp)
{
    struct fasync_struct *fa, **fp;
    int result = 0;
    ....
    for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
        if (fa->fa_file != filp)
            continue;//一直循环,直到找到fapp所指向相应的struct fasync_struct结构
        ....
        *fp = fa->fa_next;//将fapp前后的元素连接起来,其中fa指向当前的元素,fp是个双重指针,指向了一个元素的next成员的地址
        call_rcu(&fa->fa_rcu, fasync_free_rcu);//释放当前的struct fasync_struct结构
        result = 1;
        break;
    }
    spin_unlock(&fasync_lock);
    spin_unlock(&filp->f_lock);
    return result;
}
static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp)
{
    struct fasync_struct *new;

    new = fasync_alloc();//为新结构开辟内存
    if (!new)
        return -ENOMEM;

    if (fasync_insert_entry(fd, filp, fapp, new)) {//将新结构加入链表
        fasync_free(new);//如果新结构加入队列失败则释放掉
        return 0;
    }

    return 1;
}
struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasync_struct **fapp, struct fasync_struct *new)
{
    struct fasync_struct *fa, **fp;

    ....
    for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {//查找链表是否存在当前文件的struct fasync_struct结构体
        if (fa->fa_file != filp)//如果当前不是则继续遍历下一个
            continue;

        spin_lock_irq(&fa->fa_lock);//找到当前的struct fasync_struct结构体,更换文件描述符
        fa->fa_fd = fd;
        spin_unlock_irq(&fa->fa_lock);
        goto out;
    }
    //跳出循环则说明链表没有当前文件的struct fasync_struct结构体,将新开辟的struct fasync_struct结构体加入链表中,并设置标志位FASYNC
    spin_lock_init(&new->fa_lock);
    new->magic = FASYNC_MAGIC;
    new->fa_file = filp;
    new->fa_fd = fd;
    new->fa_next = *fapp;
    rcu_assign_pointer(*fapp, new);
    filp->f_flags |= FASYNC;

out:
    spin_unlock(&fasync_lock);
    spin_unlock(&filp->f_lock);
    return fa;
}
  1. fasync_helper
    我们看首先看到 fasync_helper 这个函数,该函数功能就是让当前进程进入或离开struct fasync_struct 结构体队列为了方面下面将struct fasync_struct 结构体简称为fa结构体)。而第三个参数 on 就是决定进程是 进入链表 还是 离开链表。而我们传给该函数只需要一个指针,该指针就是 链表头
  2. fasync_add_entry
    该函数先使用 fasync_alloc 开辟了一个 fa结构体,然后讲该结构体指针传入 fasync_insert_entry,注意该函数的参数struct fasync_struct **fapp,它是个二级指针,指向了我们传入给 fasync_helperfa结构体指针,先在链表上进行一次遍历,如果找到链表上有当前进程传入的 fa结构体。如果没有遍历到后就跳出循环,将参数 fapp 赋值为新开辟的结构体指针,这样我们完成了结构体入链的过程了,而我们传入的二级指针也指向了一个 fa结构体
  3. fasync_remove_entry
    该函数是让当前进程的 fa结构体 离开链表,同理也是对链表进行遍历,如果发现当前进程有 fa结构体 链表中,就将结构体出链并释放。这里我们要注意到 变量fp 也是一个二级指针,该指针指向了我们传入的 fp指针 或者 fa结构体的fa_next成员变量fa指针 指向当前遍历到的节点,节点使用fa结构体指针来表示,我们从代码中可以看到 fa*fp 都指向了同一个内存地址,但是我们也要注意到 fp 这个指针指向了上一个节点的fa_next成员,所以这里其实就是将上一个节点的fa_next成员 指向 下一个节点的地址,这样就实现了节点出链。这里的上一个节点和下一个节点都是相对当前节点而言。这里的逻辑可能比较绕,需要各位读者仔细观察思考

2.3.1 kill_fasync

kill_fasync 稍显复杂,我们先看一下调用关系和阅读相关源码,然后再往下看一下讲解。同理,这里笔者也省略了部分代码以简化讲解思路

kill_fasync
    ->send_sigio
        ->send_sigio_to_task
            ->do_send_sig_info
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
    /* First a quick test without locking: usually
     * the list is empty.
     */
    if (*fp) {
        rcu_read_lock();
        kill_fasync_rcu(rcu_dereference(*fp), sig, band);
        rcu_read_unlock();
    }
}
static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
{
    while (fa) {//查看struct fasync_struct结构体是否有效
        struct fown_struct *fown;
        unsigned long flags;

        ....
        spin_lock_irqsave(&fa->fa_lock, flags);
        if (fa->fa_file) {
            fown = &fa->fa_file->f_owner;
            if (!(sig == SIGURG && fown->signum == 0))//sig并没有继续往下传递,只是在这里作为判断用
                send_sigio(fown, fa->fa_fd, band);//向应用空间发送信号
        }
        spin_unlock_irqrestore(&fa->fa_lock, flags);
        fa = rcu_dereference(fa->fa_next);//遍历下一个struct fasync_struct结构体,这样就把所谓在这个链表上的进程都遍历了一遍,对每一个使用了该设备异步通知方法的进程都发送了信号
    }
}
void send_sigio(struct fown_struct *fown, int fd, int band)
{
    struct task_struct *p;
    enum pid_type type;
    struct pid *pid;
    int group = 1;
    
    ....
    pid = fown->pid;
    if (!pid)//如果pid为空则不进行发送,所以要发送信号必须在应用层使用F_SETOWN
        goto out_unlock_fown;

    do_each_pid_task(pid, type, p) {//这里按笔者 的理解是对该进程的所有线程都发送信号
        send_sigio_to_task(p, fown, fd, band, group);
    } while_each_pid_task(pid, type, p);
    ....
out_unlock_fown:
    read_unlock(&fown->lock);
}
static void send_sigio_to_task(struct task_struct *p,
                   struct fown_struct *fown,
                   int fd, int reason, int group)
{

    int signum = ACCESS_ONCE(fown->signum);

    if (!sigio_perm(p, fown, signum))
        return;

    switch (signum) {
        siginfo_t si;
        default:
            /* Queue a rt signal with the appropriate fd as its
               value.  We use SI_SIGIO as the source, not 
               SI_KERNEL, since kernel signals always get 
               delivered even if we can't queue.  Failure to
               queue in this case _should_ be reported; we fall
               back to SIGIO in that case. --sct */
          /*这里是意思是说如果一个实时信号(信号值大于32)无法进队信号队里,
            那么我们需要报告这件事情,那么报告就需要发送信号,这个信号就是SIGIO*/

            si.si_signo = signum;
            si.si_errno = 0;
                si.si_code  = reason;
            /*
             * Posix definies POLL_IN and friends to be signal
             * specific si_codes for SIG_POLL.  Linux extended
             * these si_codes to other signals in a way that is
             * ambiguous if other signals also have signal
             * specific si_codes.  In that case use SI_SIGIO instead
             * to remove the ambiguity.
             */
            //如果发送的信号不是SIGPOLL且有指定的si_code时,此时si_code会被指定为SI_SIGIO,一般信号不会有指定的si_code
            if ((signum != SIGPOLL) && sig_specific_sicodes(signum))
                si.si_code = SI_SIGIO;

            /* Make sure we are called with one of the POLL_*
               reasons, otherwise we could leak kernel stack into
               userspace.  */
            BUG_ON((reason < POLL_IN) || ((reason - POLL_IN) >= NSIGPOLL));
            if (reason - POLL_IN >= NSIGPOLL)
                si.si_band  = ~0L;
            else
                si.si_band = band_table[reason - POLL_IN];
            si.si_fd    = fd;
            if (!do_send_sig_info(signum, &si, p, group))//当发送信号失败时,我们就不进行break,而是跳到了case 0去执行,从而达到了失败就发送SIGIO的目的
                break;
        /* fall-through: fall back on the old plain SIGIO signal */
        case 0:
            do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group);//通过发送SIGIO,让用户程序知道实时信号入队失败
    }
}
  1. kill_fasync
    我们先看看该函数的参数,除了 fa结构体 之外。还有 sigbandsig 我们知道就是信号值,其实该值并不是我们发送到应用层的值,它的作用只是做一个检查而已,我们在后面会再看到。但我们要注意这个 band ,我们在后面会提起他的作用
  2. kill_fasync_rcu
    该函数是一个 while 循环,可以从循环中看出每一次都会判断 fa结构体是否有效,且在循环完成后会遍历一 一个 fa结构体,从而达到发送信号给每一个挂在 fa结构体链表 上的进程。它主要就是做一些逻辑判断,很明显,该接口不允许发送 SIGURG 信号。然后直接调用 send_sigio。注意这里传入了参数 band,但是参数 **sig 并没有往下继续传递,进而还是用于逻辑判断
  3. send_sigio
    该函数更加简单,就是一个 for_each 的循环。按照笔者的理解,该循环是对进程上的每一个线程都执行一次 send_sigio_to_task。这里还需要注意,这里回判断 fown->pid 是否为空,非空情况下才发送,不然则跳过循环直接退出,如果要设置在成员,则必须在用户空间使用F_SETOWN。循环部分不是本文的主要内容,有兴趣的读者可以翻阅代码
    !!!这里需要注意到,每一个进程打开同一个设备文件时,都会生成不同的struct file结构体
  4. send_sigio_to_task
    该函数是本节的 主要内容。其中参数 reason 就是我们前面说的参数 band。那么这里笔者需要先说到另外的知识点,也就是第一小节应用层信号提到的可靠信号和不可靠信号以及F_SETSIG标志。那么可靠信号的范围是SIGRTMIN < sig_value < SIGRTMAX。按照笔者的理解,可靠信号也称为实时信号
    4.1. case 0分支
    笔者为什么要提到这个呢?我们在前面说参数 sig 并没有往下传递,那么我们往应用层发送的信号值从哪里来?代码很明显给我们答案了,其实他就是来自fown结构体signum 成员,该成员就是我们使用F_SETSIG标志设置的信号值,如果我们没有使用该标志进行设置,那么默认发送SIGIO信号,也就是执行 case 0 的分支。
    4.2. default分支
    那么如果我们在应用层使用了 F_SETSIG标志 标志设置了信号值,该信号值的范围一般是SIGRTMIN < sig_value < SIGRTMAX,也就是实时信号。那么 fown->signum 就会变成我们指定的信号值。那么此时函数会执行 default分支。在该分支中,如果发送的信号不是 SIGPOLL 且有指定的si_code时,此时si_code会被指定为SI_SIGIO(一般信号不会有指定的si_code),那么参数 reason 会被赋值给 siginfo_t结构体si_code成员 ,而且该siginfo_t结构体也会被我们发往应用层,让应用程序接收。后面笔者会说一下如何在应用层接收该结构体。那么我们的参数 band 就这样被发送往应用程了,这样我们就可以在应用层读取该值,知道驱动发生的异步事件是哪个种类的事件,比如是读事件 还是 写事件。这样我们在应用层编程就更加灵活。当然了,仅限于使用了F_SETSIG标志进程线程 设置了实时信号。
    4.3. 从default分支到case 0分支
    我们在看看 default分支break语句,会发现该语句有条件才会触发的,也就是函数 do_send_sig_info 返回 0 才触发。在 linux 中,返回 0 一般是表示执行成功。那么如果是返回非 0 值,表示不成功,那么按照C语言的语法,这个时候会往下执行,也就是执行 case 0分支发送 SIGIO 信号。这是为什呢?按照笔者的理解,并不是所有实时信号都能够成功发送,当内核信号队列满了,那么信号就有可能入队不成功,也就无法送往应用层。那么应用层此时需要知道信号到底有没有发送成功,那么我们就是通过使用 SIGIO 来通知应用层实时信号发送失败。那么这个逻辑的应用场景笔者目前没有遇到,但我们知道了这样的事情,在我们遇到特殊场景的时候也许会有用

按照笔者的理解,讲到这里应该可以理解异步信号机制的大部分了吧。那么关于驱动层面就讲得差不多了,有兴趣的读者可以继续往下阅读代码。

2.3、接收siginfo_t结构体

typedef struct siginfo_t{ 
    int si_signo;//信号编号 
    int si_errno;//如果为非零值则错误代码与之关联 
    int si_code;//说明进程如何接收信号以及从何处收到 
    pid_t si_pid;//适用于SIGCHLD,代表被终止进程的PID 
    pid_t si_uid;//适用于SIGCHLD,代表被终止进程所拥有进程的UID 
    int si_status;//适用于SIGCHLD,代表被终止进程的状态 
    clock_t si_utime;//适用于SIGCHLD,代表被终止进程所消耗的用户时间 
    clock_t si_stime;//适用于SIGCHLD,代表被终止进程所消耗系统的时间 
    sigval_t si_value; 
    int si_int; 
    void * si_ptr; 
    void* si_addr; 
    int si_band; 
    int si_fd; 
};
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};

在我们使用 sigaction 接口的时候,我们需要传入相应信号的 struct sigaction 结构体,其成员说明如下:

  • _sa_handler处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息

  • _sa_sigaction处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。第一个参数为信号值,第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值,第三个参数没有使用(posix没有规范使用该参数的标准)

  • sa_mask 指定在信号处理程序的 执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送。除非指定 SA_NODEFER 或者 SA_NOMASK标志位,那么在处理程序执行完后,被阻塞的信号开始执行。

  • sa_flags 中包含了许多标志位,包括刚刚提到的 SA_NODEFERSA_NOMASK 标志位。另一个重要的标志位是 SA_SIGINFO。当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,也就是 siginfo_t结构体 会被传入。因此,如果此时设置了 SA_SIGINFO标志,那么应该为sa_sigaction函数指针赋值。如果 不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将 导致段错误(Segmentation fault)

那么到了这里,各位读者应该知道获取 siginfo_t结构体 了。希望通过此文,可以让各位读者对于linux的异步信号机制有了更深一层的了解

三、参考链接

信号发送函数sigqueue和信号安装函数sigaction: https://www.cnblogs.com/mickole/p/3191804.html
异步信号SIGIO为什么会被截胡?https://www.cnblogs.com/arnoldlu/p/10185126.html
可靠信号与不可靠信号https://www.cnblogs.com/wsw-seu/p/8383737.html
应用层获得SIGIO信号如何区分是kill_fasync的第3个参数https://bbs.csdn.net/topics/392292366
IO多路复用、信号驱动IO以及epollhttps://www.cnblogs.com/arnoldlu/p/10264350.html

相关文章

  • linux内核之异步IO

    一、前言 在嵌入式linux中,除了前面讲到的轮询式IO还有异步IO。异步IO可以在驱动或者文件在处理某一件事情后...

  • 69 netty 深度源码解读

    1,同步与异步的/阻塞与非阻塞IO概念2,linux 用户态与内核态之间的区别3, 从linux内核角度分析非阻塞...

  • 浅谈Linux内核IO体系之磁盘IO

    转载于浅谈Linux内核IO体系之磁盘IO[https://zhuanlan.zhihu.com/p/963915...

  • 每周阅读(1/16/2017)

    透彻Linux(Unix)五种IO模型IO,一个重要的话题,同步vs.异步,阻塞vs.非阻塞。 从内核角度看I/O...

  • io_uring原理

    简介 io_uring是2019年在linux新增的异步IO接口。 它的出现是为了替代linux的旧的异步IO接口...

  • linux应用程序——netlink的部分使用方法

    一、前言 在 嵌入式linux 中,应用程序常常需要和内核做通信,其中我们熟悉的方法有系统调用,异步IO等,但这些...

  • 使用异步 I/O 提高程序性能

    AIO 简介 Linux 异步(asynchronous) I/O 是 Linux 2.6 内核的标准功能,你也可...

  • 同步 IO 和异步 IO

    在 linux 中对 io 操作就是把内核态准备就绪的数据拷贝到用户态。 啥是内核态呢,内核态就是 linux 内...

  • Linux IO学习

    Linux IO模型 linux的io分为两个阶段 等待数据准备好 从内核拷贝数据到用户态 io/阶段等待数据准备...

  • Netty和NIO-未完待续

    linux底层支持bio(阻塞io),nio(多路复用io),aio(异步非阻塞io),信号io等多种方案; jd...

网友评论

      本文标题:linux内核之异步IO

      本文链接:https://www.haomeiwen.com/subject/tzwonctx.html