如何在不轮询的情况下等待NMI中断?

huangapple go评论61阅读模式
英文:

How to wait for an NMI interrupt without polling?

问题

To wait for an NMI interrupt, currently I do:

while true; do
  RES=$(cat /proc/interrupts | grep NMI)
  RES=$(echo "$RES" | sed 's/[^0-9]*//g')
  RES=$(echo "$RES" | sed 's/^0*//')

  if [ "$RES" != "" ]; then
    echo "DONE"
    exit 0
  fi

  sleep 1
done

Is there a nicer way to do this without polling or sleeping?

要等待NMI中断,目前我这样做:

while true; do
  RES=$(cat /proc/interrupts | grep NMI)
  RES=$(echo "$RES" | sed 's/[^0-9]*//g')
  RES=$(echo "$RES" | sed 's/^0*//')

  if [ "$RES" != "" ]; then
    echo "DONE"
    exit 0
  fi

  sleep 1
done

是否有更好的方法可以在不轮询或休眠的情况下完成这个任务?

英文:

To wait for an NMI interrupt, currently I do:

while true; do

  RES=$(cat /proc/interrupts | grep NMI)
  RES=$(echo "$RES" | sed 's/[^0-9]*//g')
  RES=$(echo "$RES" | sed 's/^0*//')

  if [ "$RES" != "" ]; then

    echo "DONE"
    exit 0
  
  fi

  sleep 1

done

Is there a nicer way to do this without polling or sleeping?

答案1

得分: 1

等待非屏蔽中断(NMI)而不进行轮询可以通过使用事件驱动方法来实现。您可以使用信号处理程序捕获NMI信号,并在接收到信号时执行所需的操作,而不是不断检查/proc/interrupts文件。

很遗憾,您不能在用户空间脚本中直接捕获NMI中断,因为NMI中断是为内核级别代码保留的。但是,您可以编写一个内核模块来处理NMI中断,并在NMI发生时向您的用户空间脚本发送一个信号(例如SIGUSR1)。

以下是您可以执行的操作:
首先:编写一个内核模块来处理NMI中断,然后当NMI发生时,让内核模块发送一个信号(例如SIGUSR1)给您的用户空间脚本。最后,在您的脚本中,设置一个信号处理程序来捕获内核模块发送的信号并执行所需的操作。请参考下面的示例:

handle_signal() {
    echo "NMI interrupt received"
    echo "DONE"
    exit 0
}

trap 'handle_signal' SIGUSR1
start_nmi_module
while true; do
    sleep 1
done

回答评论:
如果您无法修改内核或向机器添加模块,那么您当前的方法可能是最佳选择。但是,您可以进行一些优化以减少轮询的负载:

以下是使用awk的示例:

while true; do
  RES=$(awk '/NMI/ {for (i=2; i<=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}' /proc/interrupts)

  if [ "$RES" != "" ] && [ "$RES" -ne "0" ]; then
    echo "DONE"
    exit 0
  fi

  sleep 5
done

此脚本使用awk读取/proc/interrupts文件,并将所有CPU的NMI中断计数相加。如果总计不为零,则脚本以"DONE"退出。睡眠间隔增加到5秒,因为这将减少频率和系统负载。

回答第二个问题:
很遗憾,如果没有内核修改或访问内核模块的权限,您在实时处理NMI中断方面的选项有限。但是,您可以尝试根据需要更改睡眠间隔,以在延迟和负载之间取得平衡。例如,使用较小的睡眠间隔,如0.1秒或0.5秒。这将减少处理中断的延迟,而不会过多增加系统负载。

以下是一个示例,使用0.1秒的较小睡眠间隔:

while true; do
    RES=$(awk '/NMI/ {for (i=2; i<=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}' /proc/interrupts)

    if [ "$RES" != "" ] && [ "$RES" -ne "0" ]; then
        echo "DONE"
        exit 0
    fi

    sleep 0.1
done

请记住,这仍然是轮询,延迟和负载之间始终存在权衡。仔细选择睡眠间隔,以找到适合您特定情况的平衡。

英文:

Waiting for an NMI interrupt without polling can be achieved by using an event-driven approach. Instead of constantly checking the /proc/interrupts file, you can use a signal handler to catch the NMI signal and execute the required actions when the signal is received.
Sad but, you cannot directly catch an NMI interrupt in a user-space script, as NMI interrupts are reserved for kernel-level code. However, you can write a kernel module to handle NMI interrupts and pass a signal to your user-space script when an NMI occurs.

Here is what you can do..
First: Write a kernel module to handle NMI interrupts. then when an NMI occurs, let the kernel module send a signal (e.g., SIGUSR1) to your user-space script. At last in your script, set up a signal handler to catch the signal sent by the kernel module and execute the required action. And check example below:

handle_signal() {
echo &quot;NMI interrupt received&quot;
echo &quot;DONE&quot;
exit 0
}

trap &#39;handle_signal&#39; SIGUSR1
start_nmi_module
while true; do
    sleep 1
done

Answering the comment:

hmm, if you are unable to modify the kernel or add modules to the machines, then yes, your current method is likely the best option. But, you can make a few optimizations to reduce the load because of polling:

Example using awk:

while true; do
  RES=$(awk &#39;/NMI/ {for (i=2; i&lt;=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}&#39; /proc/interrupts)

  if [ &quot;$RES&quot; != &quot;&quot; ] &amp;&amp; [ &quot;$RES&quot; -ne &quot;0&quot; ]; then
    echo &quot;DONE&quot;
    exit 0
  fi

  sleep 5
done

This script uses awk to read the /proc/interrupts file and sum the NMI interrupt counts for all the CPU. If the total count is not zero, the script exits with "DONE". The sleep interval has been increased to 5 seconds, because it will reduce the frequency and system load.

Answering second question:

So sad but, without kernel modifications or access to kernel modules, your options for handling NMI interrupts in real-time are limited.
But, you can try to strike a balance between delay and load by changing the sleep interval according. Example, use a smaller sleep interval such as 0.1 or something like that or maybe 0.5 seconds. This would decrease the delay handling, the interrupt while not excessively increasing the system load.

while true; do


RES=$(awk &#39;/NMI/ {for (i=2; i&lt;=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}&#39; /proc/interrupts)

  if [ &quot;$RES&quot; != &quot;&quot; ] &amp;&amp; [ &quot;$RES&quot; -ne &quot;0&quot; ]; then
    echo &quot;DONE&quot;
    exit 0
  fi

  sleep 0.1
done

Remember that this is still a polling and there will always be a trade between response time and load. Choosing carefully the sleep interval you can find the balance for your specific case.

答案2

得分: 1

以下是翻译好的部分:

愚蠢的解决方案:由于内核也会记录非屏蔽中断(NMI),请通过 syslogd 将内核消息重定向到一个单独的文件(或FIFO),然后在该文件中使用 grep 查找NMI事件:

如果没有输出,您的进程将会阻塞;只有当有一些输出时,grep 才会执行某些操作。请确保在输出到文件或FIFO之前不要缓冲这些行。

英文:

Stupid solution: As the kernel will log NMIs, too, redirect kernel messages to a separate file (or FIFO) via syslogd, and then grep for the NMI event in that file:

If there is no output, your process will block; only if there is some output, the grep will do something. Make sure the lines are not buffered before output to the file or FIFO.

huangapple
  • 本文由 发表于 2023年4月16日 23:06:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76028550.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定