Hallo zusammen, folgende Aufgabe: Windows Server 2016, ca. ein Dutzend Perl-Skripte, welche von einem (in C# programmierten) Windows-Dienst gestartet und überwacht werden. Für die Überwachung hole ich mir regelmäßig (einmal pro Sekunde) über WMI die Liste der laufenden Prozesse, filtere auf "perl.exe", und finde dann anhand der Commandline raus um welches Skript es geht. Das funktioniert soweit alles sehr gut und verläßlich, ABER: der WMI Provider Host (WMIPrvSE.exe) verbraucht ziemlich viel Rechenzeit... meine bisherigen Recherchen haben ergeben, dass das (Windows-typisch) einfach "so ist". Dieses ganze WMI-Teufelszeug ist ein einziger Krampf (intern sind das sowas wie Web-Services...) Mit "normalen" Mitteln komme ich zwar an das Executable (perl.exe) aber offensichtlich nicht so einfach an die Commandline bzw. die Argumente, zumindest nicht ohne erhebliche Klimmzüge (und ausweichen auf irgendwelche C++-DLLs mit fraglicher Portabilität) Hat jemand eine Idee wie man das effizienter (und trotzdem sauber) lösen kann? danke, Michi
Michael R. schrieb: > Hat jemand eine Idee wie man das effizienter (und trotzdem sauber) lösen > kann? Durch ein sinnvolleres Gesamtkonzept natürlich. Du startest doch die Prozesse selber. Damit weißt du doch, welche da sind und was die jeweils tun. Es gibt also keinerlei Notwendigkeit, die Prozessliste des Systems immer und immer wieder zu durchsuchen. Alles was man tun muss, ist: sich beim Start die Handles der gestarteten Prozesse (oder noch besser: die Referenzen der Process-Objektinstanzen) zu merken und sie später dann einfach zu benutzen, wenn man Zugriff auf die Prozesse benötigt.
c-hater schrieb: > Michael R. schrieb: > >> Hat jemand eine Idee wie man das effizienter (und trotzdem sauber) lösen >> kann? > > Durch ein sinnvolleres Gesamtkonzept natürlich. > > Du startest doch die Prozesse selber. Damit weißt du doch, welche da > sind und was die jeweils tun. Es gibt also keinerlei Notwendigkeit, die > Prozessliste des Systems immer und immer wieder zu durchsuchen. Alles > was man tun muss, ist: sich beim Start die Handles der gestarteten > Prozesse (oder noch besser: die Referenzen der Process-Objektinstanzen) > zu merken und sie später dann einfach zu benutzen, wenn man Zugriff > auf die Prozesse benötigt. Ist leider so nicht möglich: Gewisse Prozesse dürfen parallel manuell gestartet werden, andere nicht. Diese müssen identifiziert und dann gekillt werden. Da ich das auch an anderer Stelle (in einem anderen kontext) benötige, ist die eigentliche Frage: gibt es eine saubere Alternative zu WMI, welche mir Prozesse + Argumente liefert?
:
Bearbeitet durch User
Michael R. schrieb: > Ist leider so nicht möglich: Gewisse Prozesse dürfen parallel manuell > gestartet werden, andere nicht. Diese müssen identifiziert und dann > gekillt werden. Dann musst du die Prozesse welche nicht parallel laufen dürfen mit ein Lock ausstatten. flock ist im allgemein dein freund für so was. Einfach ein Exklusiv lock auf das Perlscript selbst öffnen und beim beenden wird der Lock automatisch wieder abgeräumt. Wenn das Script denn nicht bekommt, gibt es schon eine Instanz und es darf nicht das zweite mal starten. Die Idee mit den Prozess scann alle Sekunde ist kein hinreichender Schutz das dein Script nicht doch 2 mal läuft.
Allenfalls ist Processexplorer (download bei Microsoft) hilfreich um das Ganze ohne Commandline, dafuer viel praeziser anzuschauen.
Zwölf M. schrieb: > Allenfalls ist Processexplorer (download bei Microsoft) hilfreich um das > Ganze ohne Commandline, dafuer viel praeziser anzuschauen. PE kenn ich natürlich, aber ich brauch das wirklich in C# imonbln schrieb: > Dann musst du die Prozesse welche nicht parallel laufen dürfen mit ein > Lock ausstatten. Nö, ich will einfach in C# Prozesse mit Argumenten haben, ohne den aufgeblasenen und ineffizienten WMI-Overhead. Ist das zu viel verlangt? Nur damit ihr mal ein Gefühl kriegt: WMIPrvSE.exe verursacht 15 - 25% CPU-Last auf einem Quad-XEON @2.4 GHz
Michael R. schrieb: > imonbln schrieb: >> Dann musst du die Prozesse welche nicht parallel laufen dürfen mit ein >> Lock ausstatten. > > Nö, ich will einfach in C# Prozesse mit Argumenten haben, ohne den > aufgeblasenen und ineffizienten WMI-Overhead. Ist das zu viel verlangt? Dein Requirement lautet: "Gewisse Prozesse dürfen parallel manuell gestartet werden, andere nicht", daher müssen die Prozesse welche nicht parallel Laufen dürfen, sich darum kümmern das sie es nicht tun. Das mittel der Wahl hierfür währen Locks. Es ist gegenwärtig vielleicht nicht dein Hauptproblem. Aber deine Architektur hat hier einen Bug, der je nach Anwendungsfall, einiges an Performance kosten kann. Michael R. schrieb: > Nur damit ihr mal ein Gefühl kriegt: WMIPrvSE.exe verursacht 15 - 25% > CPU-Last auf einem Quad-XEON @2.4 GHz Und? Das ist im worst case 1 Prozessor der zu ist. Wenn das ein Problem ist, musst du halt ein Tot sterben, welchen entscheidest du. Auch wenn dir das nicht gefällt, es wird wohl die wahl zwischen 1.) die Prozesse selbst starten und den Handle speichern. 2.) Eine C++ Dll welche ich übrings nicht als schlechtes Design empfinde, für systemnahe Dienste. 3.) Einfach mehr Hardware, was unter Umständen sogar billiger ist als die Entwickler Stunden.
Michael R. schrieb: > Ist leider so nicht möglich: Gewisse Prozesse dürfen parallel manuell > gestartet werden, andere nicht. Diese müssen identifiziert und dann > gekillt werden. Das ist doch simpel (Wenn du durch ein sinnvolles Konzept bereits eine Liste "deiner" Prozesse hast): alles was nicht dein ist, aber die gleichen Exe als Codebasis hat, wird gekillt. > Da ich das auch an anderer Stelle (in einem anderen kontext) benötige Aha, irgendwie habe ich sowas schon fast geahnt... > ist die eigentliche Frage: gibt es eine saubere Alternative zu WMI, > welche mir Prozesse + Argumente liefert? Ja, direkt des Win32-API benutzen. Ich kann dir aber vorhersagen, dass das (zumindest von C# aus und beim gegenwärtig verfolgten Ansatz) sicher nicht nennenswert performanter wird als WMI. Du sparst zwar den Overhead der WMI-Abstraktion, hast aber dafür den Overhead, der durch den extrem häufigen Wechsel zwischen managed und unmanaged Code erforderlich wird. Der ist leider durch Eigenarten einer Enumeration zwingend erforderlich. Abhilfe wäre eine Wrapper-DLL in C für diesen speziellen Zweck. Dann hatte man das aufwendige Gerödel beim Wechsel zum managed Code immer nur einmal pro komplettem Lauf der Enumeration. Aber auch das würde nix am völlig unsinnigen Grundkonzept ändern. Der grundsätzliche Unsinn ist nämlich: Wenn du die Ergebnisse der Enumeration auswertest, kann sich die Sachlage bereits längst wieder geändert haben. Neue Prozesse könnten gestartet worden sein, die noch nicht in deiner letzten Enumeration enthalten sind und Prozesse aus deiner Enumeration könnten bereits längst terminiert haben, wenn du sie endlich zu sehen bekommst. D.h.: race conditions sind reichlich vorprogramiert. Kein vernünftiger Programmierer würde auf so einen Ansatz ein System aufsetzen, was zuverlässig funktionieren soll...
imonbln schrieb: > 3.) Einfach mehr Hardware offensichtlich die einzige Herangehensweise die MS und ihre Jünger kennen: Bewerfen wir das Problem mit GHz
Man könnte auch ein Kommandozeilenprogramm in reinem C/C++ schreiben, das analog zu ps/tasklist arbeitet und die gewünschten Informationen auf stdout ausgibt. Dann hat man keine Wurschtelei mit "managed"/"unmanaged" Code und obendrein ein Werkzeug, das besser ist als das windows-eigene "tasklist". Was ein aufgerufenes Programm auf stdout ausgibt, wird man mit dem .Net-Geraffel ja doch wohl ohne allzu üble Performanceeinbußen abgreifen können. Eine Möglichkeit, die Mehrfachstartprobleme einzugrenzen, wäre eine Änderung des Programmes, das die Perl-Skripte aufruft. An der Stelle, wo die Skripte aufgerufen werden, wird ein Abrufen der Taskliste eingebaut, und das jeweils gewünschte Skript nur dann tatsächlich gestartet, wenn es nicht schon läuft. Das Beenden eines laufenden Skriptes (d.h. Perl-Prozesses) kann überwacht werden, indem beim Starten die Prozess-ID aufgehoben wird und mit den üblichen API-Funktionen der Zustand dieses Prozesses abgefragt wird. Mit der Win32-API-Funktion OpenProcess kann ein Handle für einen Prozess erzeugt werden, auf dieses Handle kann beispielsweise mit WaitForSingleObject bzw. WaitForMultipleObjects gewartet werden. Als Alternative zum Warten kann mit GetExitCodeProcess überprüft werden, ob der Prozess noch läuft, in diesem Fall ist der Rückgabewert STILL_ACTIVE. Das ist nicht ganz geschickt, denn der Prozess könnte den gleichen Wert (259) auch als Rückgabewert liefern ... was sich damit nicht unterscheiden lässt (siehe Kommentar zu GetExitCodeProcess).
c-hater schrieb: > Das ist doch simpel (Wenn du durch ein sinnvolles Konzept bereits eine > Liste "deiner" Prozesse hast): alles was nicht dein ist, aber die > gleichen Exe als Codebasis hat, wird gekillt. Falsch. Perl.exe (oder irgendein Interpreter.exe) dürfen natürlich laufen, und tun es auch. Nur gewisse Skripte nicht. c-hater schrieb: > Ja, direkt des Win32-API benutzen. hast du da was für mich? c-hater schrieb: > Aber auch das würde nix am völlig unsinnigen Grundkonzept ändern. Der > grundsätzliche Unsinn ist nämlich: Wenn du die Ergebnisse der > Enumeration auswertest, kann sich die Sachlage bereits längst wieder > geändert haben. Damit kann ich leben. Das wird einmal pro Sekunde ausgewertet, und "böse" tasks gekillt. Meine eigenen, von mir gestarteten Prozesse hab ich ohnehin im Griff. c-hater schrieb: > Kein vernünftiger Programmierer Danke für die Blumen :-) Ich würde das allerdings anders formulieren: kein vernünftiger programmierer würde mit C#.net arbeiten (aber ich bin unvernünftig, und muss mich der normativen Kraft des Faktischen beugen) Auch wenn ich meine Frage wiederhole: Nö, ich will einfach in C# Prozesse mit Argumenten haben, ohne den aufgeblasenen und ineffizienten WMI-Overhead. Ist das zu viel verlangt? Lautet die Antwort wohl: ja, das ist zu viel verlangt...
Rufus Τ. F. schrieb: > Man könnte auch ein Kommandozeilenprogramm in reinem C/C++ schreiben, > das analog zu ps/tasklist arbeitet und die gewünschten Informationen auf > stdout ausgibt. Dann hat man keine Wurschtelei mit "managed"/"unmanaged" > Code und obendrein ein Werkzeug, das besser ist als das windows-eigene > "tasklist". Hmmm.... dann könnte ich ja eigentlich auch gleich meine gesamtes Tool in C/C++ schreiben? Vermutlich nicht die dümmste idee, oder?
Michael R. schrieb: > Falsch. Perl.exe (oder irgendein Interpreter.exe) dürfen natürlich > laufen, und tun es auch. Nur gewisse Skripte nicht. Dann sorge dafür, dass es eine systemweit verfügbare Aufzeichnung über den Start entsprechender Scripte gibt und nutze dann diese Aufzeichnung. >> Ja, direkt des Win32-API benutzen. > > hast du da was für mich? Microsoft hat: ToolHelp32 heißt das einschlägige API. Fängt an mit CreateToolhelp32Snapshot(...) usw. Googlen kannst du hoffentlich selbst... Und was den (weitaus intelligenteren) ereignisorientierten Ansatz mit der Aufzeichnung von gestarteten "relevanten" Prozessen betrifft: Da wäre ein Shell-Hook das Mittel der Wahl. Bitte beachten: auch ein solcher Ansatz löst nicht das Problem der möglichen race conditions. Wohl aber das Problem der nutzlosen Verschwendung von Rechenzeit durch idiotisches Polling von Informationen... > Damit kann ich leben. Das wird einmal pro Sekunde ausgewertet Das reduziert nur die Wahrscheinlichkeit für races. Löst aber das Problem ansonsten in keinster Weise. Und übrigens: Wenn nur einmal pro Sekunde die Prozessliste enumeriert wird, braucht man selbst mit WMI ganz sicher keine 25% der Rechenleistung eines heutigen Systems. Nichtmal näherungsweise. Fazit: Irgendwas passt da tierisch nicht in deinen Angaben...
Michael R. schrieb: > offensichtlich die einzige Herangehensweise die MS und ihre Jünger > kennen: Bewerfen wir das Problem mit GHz Nein, das hat nix mit MS oder deren Jüngern zu tun. Das gleiche Phänomen findest du bei jedem Zielsystem, vom vollwertigen OS bis runter zum LED-Blinken auf irgendeinem verschissenen µC, bei OpenSource genauso wie bei ClosedSource. Das Problem heisst: Wenn die Performance der Software sich dem Optimum nähern soll, wächst der Aufwand zu ihrer Erstellung exponentiell mit der Annäherung an das Optimum. Verhindert wird das Erreichen des Optimums also letztlich durch exakt zwei Mechanismen: 1) Kostendruck (nur kommerzielle Softwareerstellung) 2) Faulheit/Unfähigkeit der Programierer Wobei 2) indirekt natürlich auch auf 1) einwirkt: Fleißige und fähige Programmierer als Lohnsklaven zu halten, kostet halt auch 'nen Schein mehr als die Haltung einer Herde von leicht ersetzbaren Durchschnittsperformern...
c-hater schrieb: > Das Problem heisst: Wenn die Performance der Software sich dem Optimum > nähern soll, wächst der Aufwand zu ihrer Erstellung exponentiell mit der > Annäherung an das Optimum. Optimum? ich rede hier nicht von Optimum. ich erwarte nicht dass eine dermaßen simple Aufgabenstellung statt in 6 µs in 5.87 µs erledigt ist. Es ist nicht das erste Mal, dass ich über die beängstigend schlechte Performance von C# stolpere. Aber lassen wir das, es gibt spannenderes: c-hater schrieb: > Dann sorge dafür, dass es eine systemweit verfügbare Aufzeichnung über > den Start entsprechender Scripte gibt und nutze dann diese Aufzeichnung. Können vor Lachen: Wie soll ich das Öffnen einer DOS-Shell und das Ausführen von "perl.exe wunderskript.pl" systemweit überwachen? c-hater schrieb: > Microsoft hat: ToolHelp32 Danke! c-hater schrieb: > idiotisches immer freundlich bleiben! c-hater schrieb: > Und übrigens: Wenn nur einmal pro > Sekunde die Prozessliste enumeriert wird, braucht man selbst mit WMI > ganz sicher keine 25% der Rechenleistung eines heutigen Systems. > Nichtmal näherungsweise. Das ist mal ein guter Ansatz. ich hab versucht mein Problem zu isolieren (siehe Anhang) und mir wird grad etwas flau im Magen: meine (weitestgehend leerlaufende) Win2016er VM:
1 | D:\Temp>WMI-Test.exe |
2 | 68 Tasks 0 Perls => 180 msec |
3 | 68 Tasks 0 Perls => 170 msec |
4 | 68 Tasks 0 Perls => 165 msec |
5 | 68 Tasks 0 Perls => 162 msec |
6 | 68 Tasks 0 Perls => 141 msec |
2016er Server beim Kunden:
1 | 499 Tasks 13 Perls => 1611 msec |
2 | 498 Tasks 13 Perls => 1548 msec |
3 | 498 Tasks 13 Perls => 1477 msec |
4 | 498 Tasks 13 Perls => 1450 msec |
5 | 498 Tasks 13 Perls => 1454 msec |
6 | 498 Tasks 13 Perls => 1420 msec |
7 | 498 Tasks 13 Perls => 1423 msec |
8 | 498 Tasks 13 Perls => 1451 msec |
9 | 498 Tasks 13 Perls => 1458 msec |
10 | 498 Tasks 13 Perls => 1494 msec |
WmiPrvSE.exe 30% Auslastung Was läuft da falsch? Hab ich hier irgendwo milli- und femtosekunden verwechselt? Andererseits schließt sich hier der Kreis: ich rede NICHT vom Optimum, aber ich darf doch erwarten dass das simple Holen einer Prozess-Liste auf einem durchaus modernen Server nicht eineinhalb Sekunden dauert, oder? Was machen die mit der ganzen schönen rechenzeit? ich vermute, gefühlte 317 mal ein XML-Format ins andere umwandlen...
Hallo Michael, evtl. hilft es durch die WMI query zu filtern, und nur die Properties abzufragen welche Dich auch wirklich interessieren. Als weitere Möglichkeit: mittels NtQueryInformationProcess / PEB die commandline extrahieren. Da ist der eleganteste Weg über ein C++/CLI assembly (siehe Demo Projekt im Anhang). Cheers, Roger
Roger S. schrieb: > Hallo Michael, > > evtl. hilft es durch die WMI query zu filtern, und nur die Properties > abzufragen welche Dich auch wirklich interessieren. Hmm... ja, werd ich probieren... wobei ich überall lese, man soll nur ja nicht, weil es dann noch langsamer wird (aber noch langsamer geht ja gar nicht ;-) > Als weitere Möglichkeit: mittels NtQueryInformationProcess / PEB die > commandline extrahieren. Da ist der eleganteste Weg über ein C++/CLI > assembly (siehe Demo Projekt im Anhang). Wow, das ist mal ne Ansage! herzlichsten Dank, werd ich mir ASAP reinziehen! lg Michi
:
Bearbeitet durch User
Hallo Roger, Roger S. schrieb: > evtl. hilft es durch die WMI query zu filtern, und nur die Properties > abzufragen welche Dich auch wirklich interessieren. es ist kaum zu glauben, aber alleine das Filtern der WMI Query drückt die Ausführungszeit von 1.5 Sekunden auf praktisch Null. Ich mag mich jetzt nicht darüber auslassen, warum die hier eine SQL-Syntax verwenden. ich bin zu alt um alles verstehen zu müssen... Aber: Mein Problem ist damit gelöst! Herzlichsten Dank nochmal!
Kommando retour: ich hatte einen Fehler in meinem Testprogramm (falsche Stopwatch ausgegeben) Die Variante mit "Filtern der WMI-Query" ist tendenziell sogar noch langsamer :-( Womit meine Annahme von gestern widerlegt wäre: Es geht doch noch langsamer... es ist einfach unfassbar was MS hier bietet. ich werd mich jetzt also mit der C++-Variante beschäftigen (müssen)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.