Linux下自动调整进程优先级
我正在尝试编写一个程序,根据配置文件(基本上是路径-优先级对)自动设置进程优先级。
我认为最好的解决方案是用一个内核模块来替换 execve() 系统调用。不幸的是,系统调用表在内核版本中没有导出> 2.6.0,所以如果没有真正丑陋的黑客攻击,就不可能替换系统调用。
我不想想要执行以下操作:
- 用 shell 脚本替换二进制文件,该脚本启动并重新启动二进制文件。 - 修补/重新编译我的 Ubuntu 内核 - 进行丑陋的黑客攻击,例如读取内核可执行内存并猜测系统调用表位置 - 轮询正在运行的进程,
我真正希望:
- 能够根据任何进程的可执行路径和配置文件来控制其优先级。规则适用于任何用户。
你们中有人对如何完成这项任务有任何想法吗?
I'm trying to write a program that automatically sets process priorities based on a configuration file (basically path - priority pairs).
I thought the best solution would be a kernel module that replaces the execve() system call. Too bad, the system call table isn't exported in kernel versions > 2.6.0, so it's not possible to replace system calls without really ugly hacks.
I do not want to do the following:
-Replace binaries with shell scripts, that start and renice the binaries.
-Patch/recompile my stock Ubuntu kernel
-Do ugly hacks like reading kernel executable memory and guessing the syscall table location
-Polling of running processes
I really want to be:
-Able to control the priority of any process based on it's executable path, and a configuration file. Rules apply to any user.
Does anyone of you have any ideas on how to complete this task?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
如果您已经选择了轮询解决方案,那么您想要实现的大部分功能已经存在于Automatic Nice Daemon。您可以根据进程名称、用户和组为进程配置良好级别。甚至可以根据迄今为止使用的 CPU 时间动态调整进程优先级。
If you've settled for a polling solution, most of the features you want to implement already exist in the Automatic Nice Daemon. You can configure nice levels for processes based on process name, user and group. It's even possible to adjust process priorities dynamically based on how much CPU time it has used so far.
有时轮询是必要的,甚至最终更优化——不管你信不信。这取决于很多变量。
如果轮询开销足够低,那么它远远超过了开发您自己风格的内核挂钩以获取所需更改通知所增加的复杂性、成本和风险。也就是说,当挂钩或通知事件可用或可以轻松注入时,如果情况需要,当然应该使用它们。
这是典型的程序员“完美”思维。作为工程师,我们力求完美。但这是现实世界,有时必须做出妥协。讽刺的是,在某些情况下,更完美的解决方案可能效率较低。
我为 Windows 开发了一个类似的“进程和进程优先级优化自动化”工具,称为 Process Lasso(不是广告,它是免费的)。我也做出了类似的选择,并制定了混合解决方案。内核模式挂钩可用于 Windows 中某些与进程相关的事件(创建和销毁),但它们不仅不会在用户模式下公开,而且对于监视其他进程指标也没有帮助。我认为任何操作系统都不会本机通知您任何进程指标的任何更改。这么多不同的钩子的开销可能比简单的轮询大得多。
最后,考虑到流程更改的高频率,与通知事件/挂钩相比,一次处理所有更改(间隔轮询)可能更好,后者可能需要每秒处理更多次。
你远离脚本是对的。为什么?因为它们很慢(呃)。当然,Linux 调度程序通过降低 CPU 绑定线程的优先级并奖励(升级)I/O 绑定线程的优先级,在处理 CPU 绑定线程方面做得相当好 - 因此,即使在高负载下,脚本也应该我想要有反应。
Sometimes polling is a necessity, and even more optimal in the end -- believe it or not. It depends on a lot of variables.
If the polling overhead is low-enough, it far exceeds the added complexity, cost, and RISK of developing your own style kernel hooks to get notified of the changes you need. That said, when hooks or notification events are available, or can be easily injected, they should certainly be used if the situation calls.
This is classic programmer 'perfection' thinking. As engineers, we strive for perfection. This is the real world though and sometimes compromises must be made. Ironically, the more perfect solution may be the less efficient one in some cases.
I develop a similar 'process and process priority optimization automation' tool for Windows called Process Lasso (not an advertisement, its free). I had a similar choice to make and have a hybrid solution in place. Kernel mode hooks are available for certain process related events in Windows (creation and destruction), but they not only aren't exposed at user mode, but also aren't helpful at monitoring other process metrics. I don't think any OS is going to natively inform you of any change to any process metric. The overhead for that many different hooks might be much greater than simple polling.
Lastly, considering the HIGH frequency of process changes, it may be better to handle all changes at once (polling at interval) vs. notification events/hooks, which may have to be processed many more times per second.
You are RIGHT to stay away from scripts. Why? Because they are slow(er). Of course, the linux scheduler does a fairly good job at handling CPU bound threads by downgrading their priority and rewarding (upgrading) the priority of I/O bound threads -- so even in high loads a script should be responsive I guess.
您可能会考虑另一个攻击点:将系统的动态链接器替换为修改后的一个应用你的逻辑的。 (请参阅这篇论文,了解一些很好的示例,了解很大程度上被忽视的链接器黑客技术)。
这种方法会出现问题的是纯静态链接的二进制文件。我怀疑现代系统上有很多东西实际上并没有动态链接某些东西(例如 busybox-static< /a> 是明显的例外,尽管当一切都出现严重错误时,您可能会将在控件之外获得最小 shell 的能力视为一项功能),所以这可能不是什么大问题。另一方面,如果优先级策略旨在为过载的共享多用户系统带来一些顺序,那么您可能会看到聪明的用户准备应用程序的静态链接版本,以避免链接器强加的优先级。
There's another point of attack you might consider: replace the system's dynamic linker with a modified one which applies your logic. (See this paper for some nice examples of what's possible from the largely neglected art of linker hacking).
Where this approach will have problems is with purely statically linked binaries. I doubt there's much on a modern system which actually doesn't link something dynamically (things like busybox-static being the obvious exceptions, although you might regard the ability to get a minimal shell outside of your controls as a feature when it all goes horribly wrong), so this may not be a big deal. On the other hand, if the priority policies are intended to bring some order to an overloaded shared multi-user system then you might see smart users preparing static-linked versions of apps to avoid linker-imposed priorities.
当然,只需迭代 /proc/nnn/exe 即可获取正在运行的映像的路径名。仅使用带斜杠的,其他是内核进程。
检查一下您是否已经处理了该优先级,否则在配置文件中查找新的优先级并使用 renice(8) 调整其优先级。
Sure, just iterate through /proc/nnn/exe to get the pathname of the running image. Only use the ones with slashes, the others are kernel procs.
Check to see if you have already processed that one, otherwise look up the new priority in your configuration file and use renice(8) to tweak its priority.
如果您想将其作为内核模块,那么您可以考虑制作自己的二进制加载器。请参阅以下内核源文件作为示例:
它们可以让您初步了解从哪里开始。
您可以修改 ELF 加载程序来检查 ELF 文件中的附加部分,并在找到时使用其内容来更改调度优先级。然后,您甚至不需要管理单独的配置文件,只需向您想要以这种方式管理的每个 ELF 可执行文件添加一个新部分即可。有关如何向 ELF 文件添加新部分的信息,请参阅 binutils 工具的 objcopy/objdump。
If you want to do it as a kernel module then you could look into making your own binary loader. See the following kernel source files for examples:
They can give you a first idea where to start.
You could just modify the ELF loader to check for an additional section in ELF files and when found use its content for changing scheduling priorities. You then would not even need to manage separate configuration files, but simply add a new section to every ELF executable you want to manage this way and you are done. See objcopy/objdump of the binutils tools for how to add new sections to ELF files.
作为一个想法,请考虑在抱怨模式下使用apparmor。这会将某些消息记录到系统日志中,您可以收听这些消息。
As an idea, consider using apparmor in complain-mode. That would log certain messages to syslog, which you could listen to.
如果相关进程是通过执行已知路径的可执行文件来启动的,则可以使用 inotify 监视该文件上的事件的机制。执行它将触发
I_OPEN
和I_ACCESS
事件。不幸的是,这不会告诉您哪个进程导致事件触发,但您可以检查哪个
/proc/*/exe
是相关可执行文件的符号链接,并且renice< /code> 有问题的进程 ID。
例如,这里是 Perl 中使用 Linux::Inotify2 的粗略实现(其中,在 Ubuntu 上,由
liblinux-inotify2-perl
包提供):您当然可以将 Perl 代码保存到文件中,例如
onexecuting
,在前面添加第一行#!/usr/bin/env perl
,使文件可执行,将其放在您的$PATH
上,然后使用onexecuting /bin/ls renice.
然后,您可以使用此实用程序作为实施各种重新调整可执行文件策略的基础。 (或做其他事情)。
If the processes in question are started by executing an executable file with a known path, you can use the inotify mechanism to watch for events on that file. Executing it will trigger an
I_OPEN
and anI_ACCESS
event.Unfortunately, this won't tell you which process caused the event to trigger, but you can then check which
/proc/*/exe
are a symlink to the executable file in question andrenice
the process id in question.E.g. here is a crude implementation in Perl using Linux::Inotify2 (which, on Ubuntu, is provided by the
liblinux-inotify2-perl
package):You can of course save the Perl code to a file, say
onexecuting
, prepend a first line#!/usr/bin/env perl
, make the file executable, put it on your$PATH
, and from then on useonexecuting /bin/ls renice
.Then you can use this utility as a basis for implementing various policies for renicing executables. (or doing other things).