Wie kann ein Stapel die Priorität NUR der cmd, auf der er ausgeführt wird, verringern?

557
cdlvcdlv

Ich habe einen Stapel, der eine Reihe von Befehlen ausführt, die lang und CPU-intensiv sein werden. Es macht mir nichts aus, wann es fertig ist, also würde ich es vorziehen, nur leere CPU-Zyklen zu verwenden. Wenn ich die Priorität des Prozesses setze, der den Stapel (einige cmd.exe) im selben Stapel ausführt, sollte jeder untergeordnete Prozess seine Priorität erben. Ich weiß, dass Sie eine Prozesspriorität mit ändern könnenwmic . Zum Beispiel:

wmic process where name="cmd.exe" CALL setpriority "idle" 

macht zwar den Job, ABER er verringert aber auch jede andere cmd.exeInstanz, die dann ausgeführt wird, was nicht immer geeignet ist.

Gibt es eine Möglichkeit, zu ermitteln, in welchem ​​Prozess der Batch ausgeführt wird, und NUR seine Priorität auf zu setzen idle?

Zur Verdeutlichung: Ich suche nach einer allgemeinen Methode, die Sie in jede Charge dieser Art einbeziehen .batkönnen, oder eine Charge, die als abgesenkt bezeichnet werden kann.

Dies ist ein Problem, das ich manchmal finde, aber in einem bestimmten, in dem .batich gerade schreibe, übergebe ich eine variable Anzahl von Dateien, die einzeln verarbeitet werden sollen.

1

3 Antworten auf die Frage

2
TOOGAM

Ein Ansatz besteht darin, zunächst die PID (kurz für "Prozess-ID") zu finden.

Die WMIC-Prozessrolle unterstützt mehr als nur das Festlegen der Priorität. Sie können auch Details erhalten. zB Folgendes (läuft in einer einzelnen Zeile):

WMIC process where name="cmd.exe" get Caption,CommandLine,Name,ParentProcessId,ProcessId /FORMAT:LIST

(Natürlich soll dies über eine herkömmliche Eingabeaufforderung ausgeführt werden, wie das Beispiel aus Ihrer Frage. Die Verwendung von PowerShell würde ein zusätzliches Escaping erfordern.)

Möglicherweise verfügen Sie jedoch über mehrere Kopien von CMD.EXE, die Probleme verursachen können. Dies ist möglicherweise einfacher, wenn Sie den Start der Batchdatei selbst kontrollieren können. Die StackOverflow.com-Frage von Dean, „Prozess-ID von Exe in Bat File ausführen“ bietet mehrere Lösungen, um die PID in eine Umgebungsvariable zu bringen. Ich schlage vor, Sie schauen sich diese Lösungen an, um festzustellen, welche für Sie am angenehmsten aussieht, aber hier ist ein möglicher Ansatz:

C:\> wmic Process WHERE "CommandLine LIKE '%%MyProgWMICflag%%'" Get ParentProcessID /format:list

Das sollte Ihnen die ParentProcessID für den WMIC-Befehl zeigen, die die PID des Programms ist, das WMIC (vermutlich Ihre Shell) aufgerufen hat.

Dies basiert auf der Antwort von foxidrive auf der obigen Hyperlink-Seite mit Lösungen zum Erhalt des PID. (Diese Antwort zeigt auch, wie Sie dies in eine Umgebungsvariable umwandeln.)

Fügen Sie dann die ProcessId in Ihre WHERE-Klausel ein. z.B:

WMIC process where "name='cmd.exe' AND ProcessId='12345'" CALL setpriority "idle"

Hinweis: Dies ist nur ein Ansatz. Möglicherweise gibt es einige andere Merkmale, die Sie für besser testen können. Sie können eine Liste der verfügbaren Eigenschaften anzeigen, die Sie wie folgt überprüfen können:
WMIC /OUTPUT:"Results.txt" Process Get /FORMAT:LIST

Es kommt also darauf an, die tatsächliche "cmd.exe" - PID zu ermitteln. Das ist fast eine Antwort. Aber im Moment kann ich mir keinen Weg dazu vorstellen. Ich werde dieses Denken in den Hintergrund schicken. Vielleicht erscheint eine Idee. cdlvcdlv vor 6 Jahren 0
Nun, deshalb habe ich erwähnt, dass die [DeanOs StackOverflow.com-Frage "Prozess-ID des Exe in Bat-Datei ausführen"] (https://stackoverflow.com/questions/21618914/getting-process-id-of-exe-running -in-bat-file) bietet mehrere Lösungen. Ich habe keinen Vorteil darin gesehen, zu raten, welcher Ansatz (in dieser Antwort erwähnt) für Sie am günstigsten ist, und dann versuchen Sie, die Informationen aus dieser einen Methode zu kopieren, während Sie die Details der anderen möglichen Lösungen auslassen. Ich habe ein bisschen weiter gespielt und ein paar Infos hinzugefügt (siehe "Ein möglicher Ansatz") TOOGAM vor 6 Jahren 0
Ich habe [diese Frage] (https://stackoverflow.com/questions/7486717/finding-parent-process-id-on-windows) gefunden und daran gearbeitet, aber der %% MyProgWMICflag %%-Trick ist schlau! cdlvcdlv vor 6 Jahren 0
0
user902300

Sie müssten Ihre Charge mit START (von einer anderen Charge) ausführen - dann kann der Start mit einer separaten Priorität festgelegt werden

START "" batch.bat /B /LOW 
Vielleicht habe ich es nicht gut erklärt. Ich weiß, dass Sie Prozesse mit niedriger Priorität auf diese Weise starten können, aber das würde mich zwingen, für jedes ".bat" dieser Art, das ich geschrieben habe, einen Launcher zu erstellen. Was ich gesucht habe, ist eine allgemeine Methode, die Sie in jede Charge wie diese aufnehmen können. Oder eine Charge, die ein beliebiger ".bat" als abgesenkt bezeichnen kann. cdlvcdlv vor 6 Jahren 0
@cdlvcdlv Ich wurde mit https://www.prnwatch.com/prio/ empfohlen - vielleicht hilft es Ihnen user902300 vor 6 Jahren 0
0
cdlvcdlv

Dies ist eine Funktion, die Sie von einem anderen verwenden können, um die Priorität Ihrer Charge festzulegen. Wenn Sie es in ein Verzeichnis kopieren, können Sie es wie gewohnt jederzeit aufrufen. Danke an TOOGAM, das gab mir die Werkzeuge und Methoden, um dies zu schreiben. Die Variable stellt sicher, dass der genaue Prozess genau das ist, was Sie benötigen, und nicht eine andere Instanz Ihrer Charge (unwahrscheinlich, aber mögliches Szenario).SetMyPrio.bat MyPrio.bat%PATH%UniqueString

@echo off  GOTO :EndOfReminder  <HELP>  USAGE:  From any batch file or command prompt, write CALL MyPrio [/?|/h] [<priority>|get] to set the priority of the caller cmd. Default for <priority> is idle.  /?, /h This help.  <priority> If you want your priority change to  idle -- no parameter, 64, idle or "idle" below normal -- 16384 or "below normal" normal -- 32, normal or "normal" above normal -- 32768 or "above normal" high priority -- 128 or "high priority" real time -- 256, realtime or "realtime" get Returns current priority as errorlevel  TO DO - Parameter error control. - Support for Unicode characters in help. </HELP>  :EndOfReminder setlocal set Priority=%~1  if /I ["%Priority%"]==["/h"] ( call :PrintHelp & exit /b ) if ["%Priority%"]==["/?"] ( call :PrintHelp & exit /b )  set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%  if ["%Priority%"]==["get"] for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get Priority /format:LIST`) do exit /b %%a  if not defined Priority set Priority=idle for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a wmic process where "ProcessId='%myPID%'" CALL setpriority "%Priority%" > nul 2>&1  endlocal goto :eof  :PrintHelp setlocal DisableDelayedExpansion call :eln 0 set "levelerror=^%%errorlevel^%%" for /F "delims=" %%a in ('find /v /n "" "%~f0"') do ( set "line=%%a"  REM We store errorlevel in tmpel because setlocal will set errorlevel to 0... call set tmpel=%levelerror% setlocal EnableDelayedExpansion REM ... and now we restore it REM %levelerror% was expanded to %errorlevel% before running the iteration REM and the CALL SET allows actual errorlevel be stored in tmpel REM Finally, we set errorlevel again call :eln !tmpel!  set "line=!line:*]=!"  if /i "!line!"=="</HELP>" exit /b 0 if errorlevel 1 echo(!line! if /i "!line!"=="<HELP>" call :eln 1 REM errorlevel is the only value that survives the following endlocal endlocal ) exit /b 1  :eln REM Sets errorlevel to %1 exit /b %1 

Bearbeiten (2018-06-12)

  • Änderte den Namen in MyPrio, zusätzliche Optionen /?, /hund get.
  • Optionen /?und /hdrucken Sie den Text, den Sie in der Erinnerung schreiben, zwischen <HELP>und </HELP>. Diese Methode kann in jeder anderen Charge, die Sie benötigen, wiederverwendet werden.

Wenn Sie zu MyPrio.batBeginn eines Stapels einen Aufruf machen, wird beim Start des Batches, egal ob Sie eine Befehlszeile verwenden oder Dateien aus Windows Explorer ziehen und ablegen (die zu Parametern werden), jeder Teil (und untergeordnete Prozesse) des Batches ausgeführt Ihre gewünschte Priorität.

Zum Beispiel:

@echo off call MyPrio  REM Here comes a set of commands that will run with idle priority.  call MyPrio normal  REM Here may come some interactive part to request information needed to continue.  call MyPrio  REM Back to idle (or put any other priority you prefer).  call MyPrio normal  REM You may want to set the priority to normal at the end just in case you call the batch from a command line. 

BEMERKUNGEN

Ich erstellte einen Stapel PrioTest.bat, der MyPrio.batmit jeder möglichen Option zum Testen benannt wurde, und ordnete sie so an, dass bei jedem Anruf die Priorität auf eine andere geändert wurde. Ich habe eine Subroutine ( :test1) erstellt, die:

1. Ruft die aktuelle Priorität ab und speichert sie unter myPrioBefore.
2. Ruft MyPriomit dem Testparameter auf.
3. Ruft eine neue Priorität ab und speichert sie unter myPrioAfter.
4. Startet ein verstecktes ping -t localhost.
5. Ruft Priorität und PID des pingProzesses ab und speichert sie unter pingPriound pingPID.
6. Druckt die Werte myPrioBefore, den Anruf an MyPrio, myPrioAfterund pingPrio.
7. Tötet den Start ping(sicher sein, dass es kein anderer ist).

@echo off  setlocal  set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM% for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a for %%a in (64 16384 32 32768 128 256 idle "below normal" none normal "idle" "normal" "above normal" realtime "high priority" "realtime") do call :test1 %%a CALL MyPrio normal  endlocal goto :eof  :test1 for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioBefore=%%b if [%1]==[none] ( call MyPrio ) else ( call MyPrio %1 ) for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioAfter=%%b start "" /b ping -t localhost > nul for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get Priority /format:LIST`) do set pingPrio=%%b for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get ProcessID /format:LIST`) do set pingPID=%%b echo myPrioBefore==%myPrioBefore% / CALL MyPrio %1 / myPrioAfter==%myPrioAfter% / pingPrio==%pingPrio% taskkill /f /pid %pingPID% > nul exit /b 

Dies ist die Ausgabe der Testerstapel:

myPrioBefore==4 / CALL MyPrio 64 / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio 16384 / myPrioAfter==6 / pingPrio==6 myPrioBefore==6 / CALL MyPrio 32 / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio 32768 / myPrioAfter==10 / pingPrio==8 myPrioBefore==10 / CALL MyPrio 128 / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio 256 / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio idle / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio "below normal" / myPrioAfter==6 / pingPrio==6 myPrioBefore==6 / CALL MyPrio none / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio normal / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio "idle" / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio "normal" / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio "above normal" / myPrioAfter==10 / pingPrio==8 myPrioBefore==10 / CALL MyPrio realtime / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio "high priority" / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio "realtime" / myPrioAfter==13 / pingPrio==8 

Wie Sie sehen, myPrioAfterstimmt es nicht mit der in dem Aufruf angeforderten überein und manchmal ist die Priorität von pingnicht gleich dem übergeordneten cmdElement. Daher müssen mehr Werte vorhanden sein (siehe fünfte Zeile: Aktuelle Priorität ist 10, Sie fordern 128, neu Priorität ist 13 und Kindprozess '8, Entschuldigung?). Auch realtimescheint keine Wirkung zu haben (Admin-Rechte erforderlich nehme ich an).