您当前的位置:首页 > 电脑百科 > 软件技术 > 操作系统 > linux百科

Linux signalfd 原理

时间:2023-02-17 12:20:04  来源:今日头条  作者:Linux码农

信号是什么?

 

信号是事件发送时对进程的一种通知机制。有时也称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程,大多情况下,无法预测信号到达的精确时间。

一个具有合适权限的进程不仅能够向另一进程发送信号,也可以向自身发送信号。然而,发往进程的诸多信号,通常都是源于内核。

linux 信号可由如下条件产生:

  • 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输出 Ctrl+C 通常会给进程发送一个终端信号。
  • 系统异常。比如浮点异常和非法访问段内存
  • 系统状态变化。比如 alarm 定时器到期引起的 SIGALRM 信号。
  • 运行 kill 命令或者调用 kill 函数。

服务器程序必须处理(或至少忽略)一些常见的信号,以免异常终止。

 

signalfd 是什么?

 

signalfd 是一个将信号抽象的文件描述符,将信号的异步处理转换为文件的I/O 操作。通过文件描述符就绪的方法来通知信号的到来,当有信号发生时可以对其 read,这样可以将信号的监听放到 select、poll、epoll 等监听队列中。

通过文件描述符就绪的方法来通知信号的到来,当有信号发生时可以对其read,这样可以将信号的监听放到 select、poll、epoll 等监听队列中。

signalfd 的系统调用接口

#include <sys/signalfd.h>
int signalfd(int fd, const sigset_t *mask, int flags);

 

创建并返回一个用于所受信号的文件描述符。

mask:信号的集合,这里主要是你想监听的信号的集合。

flags 可以使用以下标志位进行或(or)的结果:

  • SFD_NONBLOCK: 文件会被设置成 O_NONBLOCK,读操作不阻塞。若不设置,一直阻塞直到计数器中的值大于0。
  • SFD_CLOEXEC: 在新的文件描述符上设置 close-on-exec ( FD_CLOEXEC ) 标志,简单说就是 fork 子进程时不继承。

 

获取 signalfd 文件描述符后,我们来查看一下可以对其做哪些操作。

 

static const struct file_operations signalfd_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = signalfd_show_fdinfo,
#endif
.release = signalfd_release,
.poll = signalfd_poll,
.read = signalfd_read,
.llseek = noop_llseek,
};

 

通过上面 signalfd 实现的调用可知, 我们可以对 eventfd 进行 read、poll、close 等操作。

 

下面通过一个例子来了解下 signalfd 的使用方式,具体完整代码可通过 man signalfd 获取

 

int mAIn(int argc, char *argv[])
{
...
//初始化信号集
sigemptyset(&mask);
//添加信号到信号集中
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
//将mask所指向的信号集中所包含的信号加到当前的信号掩码中,作为新的信号屏蔽字,关闭内核的默认行为。
sigprocmask(SIG_BLOCK, &mask, NULL)
//创建 signalfd 文件描述符
sfd = signalfd(-1, &mask, 0);
for (;;) {
//阻塞等待信号发生并读取。根据读取的结果可以知道发生了什么信号
s = read(sfd, &fdsi, sizeof(fdsi));
if (fdsi.ssi_signo == SIGINT) {
printf("Got SIGINTn");
} else if (fdsi.ssi_signo == SIGQUIT) {
printf("Got SIGQUITn");
exit(EXIT_SUCCESS);
} else {
printf("Read unexpected signaln");
}
}
}

 

当没有信号时,进程阻塞在 read 调用上,当有信号发生时,结果如下:

 

$ ./signalfd_demo
^C # Control-C generates SIGINT
Got SIGINT
^C
Got SIGINT
^ # Control- generates SIGQUIT
Got SIGQUIT
$

 

每次 Control + C,进程都会捕获到一次信号,并打印具体信息。

通过如下查看,得到 signalfd 其实也是一个匿名 fd 类型。

[root@localhost ~]# ll /proc/48356/fd/
lrwx------ 1 root root 64 5月 23 11:54 3 ->anon_inode:[signalfd]

 

signalfd 源码解析

 

接下来我们通过分析源码的方式来探究 signalfd 的底层实现原理。

 

signalfd ( signalfd4 )

SYSCALL_DEFINE3(signalfd, int, ufd, sigset_t __user *, user_mask, size_t, sizemask)
{
...
return do_signalfd4(ufd, &mask, 0);
}
SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask, size_t, sizemask, int, flags)
{
...
return do_signalfd4(ufd, &mask, flags);
}
static int do_signalfd4(int ufd, sigset_t *mask, int flags)
{
struct signalfd_ctx *ctx;
sigdelsetmask(mask, sigmask(SIGKILL) | sigmask(SIGSTOP));
signotset(mask);
//内核新创建signalfd
if (ufd == -1) {
//创建一个signalfd_ctx内核结构
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
//设置信号集合
ctx->sigmask = *mask;
//获取一个匿名句柄,file->f_op 设置为 signalfd_fops
ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK)));
} else { //已经创建signalfd
//合法性检查
struct fd f = fdget(ufd);
//设置为新的值
ctx->sigmask = *mask;
//唤醒阻塞在当前进程的信号等待队列
wake_up(¤t->sighand->signalfd_wqh);
fdput(f);
}
return ufd;
}

 

signalfd 的操作就是创建或者修改内核结构 signalfd_ctx,signalfd 本身也是一个匿名句柄。

对于 signalfd_ctx 内核结构,就只有一个字段,该字段记录用户设置的信号集合。

struct signalfd_ctx {
sigset_t sigmask;
};

signalfd_read

static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int nonblock = file->f_flags & O_NONBLOCK;
count /= sizeof(struct signalfd_siginfo);
//存放读取到的数据结构
siginfo = (struct signalfd_siginfo __user *) buf;
do {
//从信号队列中获取一个信号,然后填充到info中
ret = signalfd_dequeue(ctx, &info, nonblock);
//把获取到的信号填充到返回给用户的数据结构中
ret = signalfd_copyinfo(siginfo, &info);
siginfo++;
total += ret;
nonblock = 1;
} while (--count);
return total ? total: ret;
}
static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info,
int nonblock)
{
ssize_t ret;
DECLARE_WAITQUEUE(wait, current);
spin_lock_irq(¤t->sighand->siglock);
//从挂起信号队列中获取信号
ret = dequeue_signal(current, &ctx->sigmask, info);
switch (ret) {
case 0: //若没有信号,判断是否需要阻塞
if (!nonblock)
break; //阻塞,跳出,往下走进行休眠
ret = -EAGAIN; //非阻塞,往下走到default,函数返回
default:
spin_unlock_irq(¤t->sighand->siglock);
return ret;
}
//把当前进程加入信号等待队里中
add_wait_queue(¤t->sighand->signalfd_wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
//从挂起信号队列中获取信号
ret = dequeue_signal(current, &ctx->sigmask, info);
//存在信号,跳出循环,退出
if (ret != 0)
break;
//检查当前进程是否有信号处理,返回不为0表示有信号需要处理
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
spin_unlock_irq(¤t->sighand->siglock);
schedule(); //进程调度,进入休眠
spin_lock_irq(¤t->sighand->siglock);
}
spin_unlock_irq(¤t->sighand->siglock);
//把当前进程从等待队列中删除
remove_wait_queue(¤t->sighand->signalfd_wqh, &wait);
__set_current_state(TASK_RUNNING);
return ret;
}

 

signalfd 的读操作很简单,主要操作如下:

  • 查看信号队列中是否有信号,若有信号取出信号,并返回给用户。
  • 若句柄是阻塞类型的,在没有信号的情况下,则进程进入休眠,直到有信号到来。
  • 若句柄是非阻塞类型的,则直接返回 EAGAIN。
  •  

signalfd_poll

static __poll_t signalfd_poll(struct file *file, poll_table *wait)
{
struct signalfd_ctx *ctx = file->private_data;
__poll_t events = 0;
//把一个wait等待队列挂到当前进程的信号等待队列signalfd_wqh,其回调函数为ep_poll_callback
poll_wait(file, ¤t->sighand->signalfd_wqh, wait);
...
return events;
}


 

该函数的操作就是把 wait对象直接挂到当前进程的信号等待队列signalfd_wqh 中,对比 timerfd 来讲,区别在于 timerfd 的 wait 对象是挂到 timerfd_ctx->wqh 链表中。(详情参看定时器timerfd原理)

 

signalfd 与 epoll 的结合

 

  • 当 epoll_ctl 调用 signalfd_poll 时,会把生成的 wait 对象挂到当前进程的信号等待队列 signalfd_wqh 中,其中 wait 的回调函数为ep_poll_callback 。
  • 当触发信号时,内核会遍历 signalfd_wqh 上的 wait 对象,然后调用回调函数 ep_poll_callback,在该回调函数中会把触发的事件发送到用户态,然后唤醒由于调用 epoll_wait 而休眠的进程,唤醒后的进程调调用 ead 去取信号。


Tags:Linux   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Linux获取Redis 性能指标方法
一、监控指标&Oslash; 性能指标:Performance&Oslash; 内存指标: Memory&Oslash; 基本活动指标:Basic activity&Oslash; 持久性指标: Persistence&Oslash; 错误指标:Error二、监...【详细内容】
2024-04-11  Search: Linux  点击:(9)  评论:(0)  加入收藏
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08  Search: Linux  点击:(9)  评论:(0)  加入收藏
从原理到实践:深入探索Linux安全机制
Linux 是一种开源的类Unix操作系统内核,由Linus Torvalds在1991年首次发布,其后又衍生出许多不同的发行版(如Ubuntu、Debian、CentOS等)。前言本文将从用户和权限管理、文件系统...【详细内容】
2024-03-27  Search: Linux  点击:(24)  评论:(0)  加入收藏
在Linux系统中,如何处理内存管理和优化的问题?
本文对 Linux 内存管理和优化的一些高级技巧的详细介绍,通过高级的内存管理技巧,可以帮助系统管理员和开发人员更好地优化 Linux 系统的内存使用情况,提高系统性能和稳定性。在...【详细内容】
2024-03-26  Search: Linux  点击:(16)  评论:(0)  加入收藏
Linux 6.9-rc1 内核发布:AMD P-State 首选核心、BH 工作队列
IT之家 3 月 25 日消息,Linus Torvalds 宣布,Linux 6.9 内核的首个 RC(候选发布)版 Linux 6.9-rc1 发布。▲ Linux 6.9-rc1Linus 表示,Linux 内核 6.9 看起来是一个“相当正常”...【详细内容】
2024-03-25  Search: Linux  点击:(15)  评论:(0)  加入收藏
Linux发行版 Ubuntu 迎更新 界面设计灵感来自 Windows 11
近日,一位第三方开发者推出了一款名为“Wubuntu”的特殊Linux发行版。这款系统源自主流的Ubuntu版本,但在界面设计上却借鉴了微软最新的Windows 11风格,甚至在其中融入了微软标...【详细内容】
2024-02-27  Search: Linux  点击:(51)  评论:(0)  加入收藏
Win + Ubuntu 缝合怪:第三方开发者推出“Wubuntu”Linux 发行版
IT之家 2 月 26 日消息,一位第三方开发者推出了一款名为“Wubuntu”的缝合怪 Linux 发行版,系统本身基于 Ubuntu,但界面为微软 Windows 11 风格,甚至存在微软 Windows 徽标。据...【详细内容】
2024-02-27  Search: Linux  点击:(53)  评论:(0)  加入收藏
Linux中磁盘和文件系统工作原理解析
在Linux系统中,一切皆文件的概念意味着所有的资源,包括普通文件、目录以及设备文件等,都以文件的形式存在。这种统一的文件系统管理方式使得Linux系统具有高度的灵活性和可扩展...【详细内容】
2024-02-20  Search: Linux  点击:(58)  评论:(0)  加入收藏
Linux子系统概览
inux操作系统是一个模块化的系统,由多个子系统组成。这些子系统协同工作,使Linux能够执行各种任务。了解Linux的子系统有助于更好地理解整个操作系统的运作机制。以下是Linux...【详细内容】
2024-02-01  Search: Linux  点击:(84)  评论:(0)  加入收藏
Linux内核:系统之魂与交互之源
内核,作为任何基于Linux的操作系统的心脏,扮演着至关重要的角色。它不仅是计算机系统软件与硬件之间的桥梁,更是确保系统稳定、高效运行的关键。内核提供了一系列核心功能,为上...【详细内容】
2024-02-01  Search: Linux  点击:(72)  评论:(0)  加入收藏
▌简易百科推荐
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08    IT之家  Tags:Linux   点击:(9)  评论:(0)  加入收藏
从原理到实践:深入探索Linux安全机制
Linux 是一种开源的类Unix操作系统内核,由Linus Torvalds在1991年首次发布,其后又衍生出许多不同的发行版(如Ubuntu、Debian、CentOS等)。前言本文将从用户和权限管理、文件系统...【详细内容】
2024-03-27  凡夫编程  微信公众号  Tags:Linux安全   点击:(24)  评论:(0)  加入收藏
在Linux系统中,如何处理内存管理和优化的问题?
本文对 Linux 内存管理和优化的一些高级技巧的详细介绍,通过高级的内存管理技巧,可以帮助系统管理员和开发人员更好地优化 Linux 系统的内存使用情况,提高系统性能和稳定性。在...【详细内容】
2024-03-26  编程技术汇  微信公众号  Tags:Linux   点击:(16)  评论:(0)  加入收藏
Linux 6.9-rc1 内核发布:AMD P-State 首选核心、BH 工作队列
IT之家 3 月 25 日消息,Linus Torvalds 宣布,Linux 6.9 内核的首个 RC(候选发布)版 Linux 6.9-rc1 发布。▲ Linux 6.9-rc1Linus 表示,Linux 内核 6.9 看起来是一个“相当正常”...【详细内容】
2024-03-25    IT之家  Tags:Linux   点击:(15)  评论:(0)  加入收藏
轻松实现Centos系统的软件包安装管理:yum指令实战详解
yum 是一种用于在 CentOS、Red Hat Enterprise Linux (RHEL) 等基于 RPM 的 Linux 发行版上安装、更新和管理软件包的命令行工具。它可以自动解决软件包依赖关系,自动下载并...【详细内容】
2024-02-27  凡夫贬夫  微信公众号  Tags:Centos   点击:(59)  评论:(0)  加入收藏
Win + Ubuntu 缝合怪:第三方开发者推出“Wubuntu”Linux 发行版
IT之家 2 月 26 日消息,一位第三方开发者推出了一款名为“Wubuntu”的缝合怪 Linux 发行版,系统本身基于 Ubuntu,但界面为微软 Windows 11 风格,甚至存在微软 Windows 徽标。据...【详细内容】
2024-02-27    IT之家  Tags:Ubuntu   点击:(53)  评论:(0)  加入收藏
Linux中磁盘和文件系统工作原理解析
在Linux系统中,一切皆文件的概念意味着所有的资源,包括普通文件、目录以及设备文件等,都以文件的形式存在。这种统一的文件系统管理方式使得Linux系统具有高度的灵活性和可扩展...【详细内容】
2024-02-20  王建立    Tags:Linux   点击:(58)  评论:(0)  加入收藏
Linux子系统概览
inux操作系统是一个模块化的系统,由多个子系统组成。这些子系统协同工作,使Linux能够执行各种任务。了解Linux的子系统有助于更好地理解整个操作系统的运作机制。以下是Linux...【详细内容】
2024-02-01    简易百科  Tags:Linux   点击:(84)  评论:(0)  加入收藏
Linux内核:系统之魂与交互之源
内核,作为任何基于Linux的操作系统的心脏,扮演着至关重要的角色。它不仅是计算机系统软件与硬件之间的桥梁,更是确保系统稳定、高效运行的关键。内核提供了一系列核心功能,为上...【详细内容】
2024-02-01  松鼠宝贝    Tags:Linux内核   点击:(72)  评论:(0)  加入收藏
如何确保Linux进程稳定与持久
在Linux系统中,进程的稳定性与持久性对于维持系统的持续运行至关重要。然而,由于各种原因,进程可能会面临崩溃或系统重启的情况。为了确保关键进程能够持续运行,我们必须采取一...【详细内容】
2024-01-19  松鼠宝贝    Tags:Linux进程   点击:(90)  评论:(0)  加入收藏
站内最新
站内热门
站内头条