views:

57

answers:

3

Hello Everyone!

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?

A: 

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.

DigitalRoss
Doesn't that count as "polling of running processes" (which the questioner wants to avoid) ?
timday
I suppose but it might still be the best approach.
DigitalRoss
Yes timday, this is what I don't want to do. But this solution is not _that_ bad, I could live with it. But I don't like polling, and I don't like algorithms that doesn't run in O(1) ;) . This is polling in O(n), and I don't _like_ it, but it _will_ work I'm sure. If the linker hack won't work, I'm afraid I have to go on with this solution. DigitalRoss, thanks for thinking outside the box ;)
netom
I tried using inotify on /proc, unfortunately, it does not work :D What a shame.
netom
Because of the inotify bug, and the lack of any pluggable/hookable stuff for process creation, I accept this solution, as this is what I will do: write a daemon that iterates through processes, and sets their priorities.
netom
+1  A: 

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.

timday
I will definitely check this possibility, thank you. Now I have to figure out how to replace / hack libc somewhere _very_ early in the boot process, without modifying files that come with Ubuntu packages (I still want to upgrade the system when something new comes out).
netom
Surely you'd just prepare a modified libc, package it as a .deb, and install it (replacing the default libc) on the systems you want be be able to apply these policies on ? My assumption is virtually everything dynamically links libc these days, so existing binaries should just pick up the new ldd/ld-linux.so quite happily.
timday
Ok, this is fine, but I don't want to lose the simplicity of automatic updates. Is there any way to "patch" existing binaries? I mean to replace certain functions, with, say, a linker script or such thing? I'm really not an expert in linker specific issues.
netom
Neither am I. If a new libc came out with security fixes, your systems would have the choice of installing it and losing your modified linker, or waiting for you to get your own version of the update out. It'd be nice for you if the linker was more separated from the rest of libc in a separate package as the chances are libc updates (which are fairly rare anyway) might not need to touch the linker... but things aren't actually so. And overwriting a particular libc's linker with one compiled from a previous version of libc seems like a bad idea (risk of completely broken system?)
timday
+1  A: 

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.

Jeremy Collake
I admit that polling is _necessary_, _sometimes_. But in my opinion, in this case, it would be slow(er) and very race condition-ish. I have several hundreds or thousands of processes and polling them every second or so is definitely slower than firing a hook at process start.
netom
Yes, I certainly agree, so long as you are looking only at process creation and destruction. If you were tracking changes to virtual memory usage, for instance, then obviously you'd have to poll because getting a notification for every change would result in a crapload of notifications per second for that single metric. You get my point ;).
Jeremy Collake
Yes, absolutely. :)
netom
"This is classic programmer 'perfection' thinking. As engineers, we strive for perfection. This is the real world though and sometimes compromises must be made.", :D Yes, you are right. This "real world" stuff involves buggy and incomplete kernel, so I must stay with polling. "Good enaugh".
netom