Forum: PC-Programmierung laufende Prozesse mit Commandline abfragen


von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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
von imonbln (Gast)


Lesenswert?

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.

von Purzel H. (hacky)


Lesenswert?

Allenfalls ist Processexplorer (download bei Microsoft) hilfreich um das 
Ganze ohne Commandline, dafuer viel praeziser anzuschauen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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

von imonbln (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

imonbln schrieb:
> 3.) Einfach mehr Hardware

offensichtlich die einzige Herangehensweise die MS und ihre Jünger 
kennen: Bewerfen wir das Problem mit GHz

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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).

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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?

von c-hater (Gast)


Lesenswert?

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...

von c-hater (Gast)


Lesenswert?

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...

von Michael R. (Firma: Brainit GmbH) (fisa)


Angehängte Dateien:

Lesenswert?

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...

von Roger S. (edge)


Angehängte Dateien:

Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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!

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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
Noch kein Account? Hier anmelden.