Como um lote pode diminuir a prioridade APENAS do cmd que o executa?

1

Eu tenho um lote que executa uma série de comandos que serão longos e intensivos da CPU. Eu não me importo quando será terminado, então eu preferiria usar apenas ciclos de CPU ociosos. Se eu definir a prioridade do processo que está executando o lote (alguns cmd.exe ) no mesmo lote, qualquer processo filho deverá herdar sua prioridade. Eu sei que você pode alterar uma prioridade de processo usando wmic . Por exemplo:

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

de fato fará o trabalho MAS também diminuirá qualquer outra cmd.exe instância que estiver sendo executada, o que pode nem sempre ser adequado.

Existe uma maneira de identificar qual processo está executando o lote e definir APENAS sua prioridade para idle ?

Para esclarecer: estou procurando um método geral que você possa incluir em qualquer lote desse tipo ou um lote que qualquer .bat possa chamar para diminuir.

Esse é um problema que às vezes eu acho, mas, em um .bat específico que estou escrevendo agora, passo um número variável de arquivos para serem processados um de cada vez.

    
por cdlvcdlv 10.06.2018 / 23:29

3 respostas

0

Este é um trabalho SetMyPrio.bat MyPrio.bat que você pode usar de outro para definir a prioridade do seu lote. Se você copiá-lo para um diretório em seu %PATH% , poderá chamá-lo sempre que precisar, etc., como de costume. Graças ao TOOGAM , isso me deu as ferramentas e o método para escrever isso. A variável UniqueString garante que o processo identificado seja exatamente o que você precisa e não qualquer outra instância do seu lote (cenário improvável, mas possível).

@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

Editar (2018-06-12)

  • Alterou o nome para MyPrio , opções adicionadas /? , /h e get .
  • Opções /? e /h imprimem o texto que você escreve no lembrete entre <HELP> e </HELP> . Este método pode ser reutilizado em qualquer outro lote que você precise.

Se você fizer uma chamada para MyPrio.bat no início de um lote, sempre que iniciar, independentemente de uma linha de comando ou arrastando e soltando arquivos do Windows Explorer (que se tornam parâmetros), todas as partes dele (e processos filho) será executado com a prioridade desejada.

Por exemplo:

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

COMENTÁRIOS

Eu fiz um lote chamado PrioTest.bat para testar MyPrio.bat com todas as opções possíveis e as ordenei para que qualquer chamada altere a prioridade para outra diferente. Eu fiz uma sub-rotina ( :test1 ) que:

1. Obtém a prioridade atual e salva em myPrioBefore .
2. Chama MyPrio com o parâmetro de teste.
3. Obtém nova prioridade e salva em myPrioAfter .
4. Inicia um ping -t localhost oculto.
5. Obtém prioridade e PID do processo ping e os salva em      pingPrio e pingPID .
6. Imprime os valores de myPrioBefore , a chamada para MyPrio ,      myPrioAfter e pingPrio .
7. Elimina o ping iniciado (tendo certeza de que não é outro).

@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

Esta é a saída do lote do testador:

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

Como você pode ver, myPrioAfter não corresponde ao solicitado na chamada e às vezes , a prioridade de ping não é igual ao pai cmd , portanto, deve haver mais para os valores (olhe para a quinta linha: a prioridade atual é 10, você solicita 128, a nova prioridade é 13 e o processo filho '8, desculpe?). Além disso, realtime parece não ter efeito (é necessário assumir os direitos de administrador).

    
por 11.06.2018 / 21:01
2

Uma abordagem é começar encontrando o PID (que é a abreviatura de "ID do processo").

A função Processo WMIC suporta mais do que apenas definir a prioridade. Você também pode obter detalhes. por exemplo. o seguinte (executado em uma única linha):

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

(Naturalmente, isso deve ser executado em um prompt de comando tradicional, como a amostra da sua pergunta. O uso do PowerShell exigiria escape adicional).

No entanto, você pode ter várias cópias do CMD.EXE, o que pode causar algum problema. Isso pode ser mais fácil se você tiver algum controle sobre como seu arquivo em lotes é iniciado. A pergunta StackOverflow.com do Dean, “recebendo o ID do processo do exe em execução no Bat File ” fornece várias soluções para obter o PID em uma variável de ambiente. Eu sugiro que você olhe sobre essas soluções para determinar qual delas parece mais agradável para você, mas aqui está uma abordagem possível:

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

Isso deve mostrar o ParentProcessID para o comando WMIC, que será o PID do programa que chama WMIC (presumivelmente seu shell).

Isto é baseado na resposta do foxidrive na página hyperlinkada acima de soluções de obtenção do PID. (Essa resposta também mostra isso em uma variável de ambiente, se você quiser.)

Em seguida, inclua o ProcessId na sua cláusula WHERE. por exemplo:

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

Nota: Esta é apenas uma abordagem. Pode haver alguns outros traços comuns que você acha que é melhor testar. Você pode ver uma lista de propriedades disponíveis para verificar usando algo como:% WMIC /OUTPUT:"Results.txt" Process Get /FORMAT:LIST

    
por 11.06.2018 / 00:21
0

você precisaria executar o lote com START (de outro lote) - então iniciar pode ser definido para ter separado prioridade

START "" batch.bat /B /LOW
    
por 10.06.2018 / 23:33