AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 779002
Accepted
wobtax
wobtax
Asked: 2024-06-26 02:54:26 +0800 CST2024-06-26 02:54:26 +0800 CST 2024-06-26 02:54:26 +0800 CST

Monitorar continuamente um processo e todos os seus filhos com `top`?

  • 772

Quero executar um processo que gere filhos, por exemplo,

for i in {1..4}; do
  sh -c 'echo $$; for j in {1..3}; do
    sh -c "echo ...\$\$; sleep 1"
  done'
done

e gostaria de monitorar o uso da CPU e da memória a cada 2 segundos com o top.

  • Posso monitorar o uso de recursos com top -p <PID>, mas isso não leva em conta os filhos.
  • Posso monitorar todos os processos em execução apenas com top, mas isso é muita informação.
  • Posso pré-calcular uma lista de PIDs e depois passar todos eles para top, mas o processo poderia então gerar novos filhos, que não seriam contabilizados.

Como posso obter um topinstantâneo a cada 2 segundos apenas do processo que estou executando e de todos os processos que ele gera?

Alguém fez uma pergunta parecida aqui , mas tratava-se de resumir essas informações em um único número, após o término do processo. Minha pergunta é sobre monitorar continuamente os processos enquanto eles ainda estão em andamento.

top
  • 5 5 respostas
  • 117 Views

5 respostas

  • Voted
  1. Marcus Müller
    2024-06-26T18:05:30+08:002024-06-26T18:05:30+08:00

    Já que você mencionou em um comentário que geralmente seria viável preparar algo antes de lançar o processo mãe, e porque seu exemplo lança um contêiner, que provavelmente já faz isso:

    Basta usar cgroups Linux para monitorar (e se desejar, controlar e limitar) os recursos usados ​​pelo seu grupo de processos relacionados.

    É bem simples: se o seu processo estiver em um cgroup, então seus filhos serão gerados no mesmo. Então, coloque seu processo mãe em um cgroup próprio (que é "basicamente gratuito", por padrão um novo cgroup herda tudo do cgroup pai). Em seguida, pesquise regularmente os processos nesse cgroup a partir dos controladores cgroupsv2 e exiba-os. O jogo inteiro fica assim:

    #!/usr/bin/python3
    # Copyright 2024 Marcus Müller
    # SPDX-License-Identifier: EUPL-1.2
    #
    # I was about to write this in *sh, but honestly, doesn't pay.
    # Let's do this in python. I'm not sure you'd understand shell script
    # better than python, anyways, and we're about to handle a few strings,
    # so, let's simply not do that in a shell language
    
    from subprocess import run
    from time import sleep
    import argparse
    
    
    def pids_of_cgroup(cgroup: str) -> list:
        with open(f"/sys/fs/cgroup/{cgroup}/cgroup.procs",
                  "r",
                  encoding="ascii") as procs:
            return [int(line) for line in procs.readlines()]
    
    
    def cgroup_from_pid(pid: int) -> str:
        with open(f"/proc/{pid}/cgroup", "r", encoding="ascii") as cgr_file:
            line = cgr_file.readline().strip()  # just one line
        return line.split(":")[-1]  # last element
    
    
    def run_program(pids: list, program: str = None) -> None:
        if program is None:
            prog = "top"
        else:
            prog = program
        cmdline = [prog, "-p", ",".join(str(pid) for pid in pids), "-n", "1"]
        if prog == "htop":
            cmdline += ["--tree", "--readonly"]  # tree view
    
        run(cmdline)
    
    
    def do_job(mother: int, htop: bool = False, interval: float = 2.0):
        # First argument is the parent PID, we use that to get the cgroup:
        cgroup = cgroup_from_pid(mother)
        program = "htop" if htop else "top"
        first_iter = True
        try:
            while True:
                if not first_iter:
                    sleep(interval)
                first_iter = False
                pids = pids_of_cgroup(cgroup)
                run_program(pids, program)
        except KeyboardInterrupt:
            pass  # User pressed ctrl+c, quit normally
        except FileNotFoundError:
            print(f"CGroup {cgroup} ceased to exist; quitting.")
    
    
    if __name__ == "__main__":  # if we're called as freestanding program
        argparser = argparse.ArgumentParser(
            description="show process stats of all child processes")
        argparser.add_argument("PID",
                               help="Mother process PID", type=int)
        argparser.add_argument(
            "-H", "--htop", help="Use htop instead of top", action='store_true')
    
        def positive_float(in_str: str) -> float:
            val = float(in_str)
            if val <= 0:
                raise ValueError(f"'{in_str}' has non-positive value {val}")
            return val
    
        argparser.add_argument("-t", "--interval",
                               help="Time between updates",
                               type=positive_float,
                               default=2.0)
        args = argparser.parse_args()
        do_job(args.PID, htop=args.htop, interval=args.interval)
    
    
    

    Salve isso em um arquivo, torne o arquivo executável ( chmod 755 filename) e execute-o com o PID do processo pai ( /path/to/filename 1234).

    Isso quase certamente funcionará com qualquer coisa que já seja um contêiner (por exemplo, contêineres flatpak, podman e docker, contêineres apptainer,…).

    É super fácil começar algo em um cgroup próprio:

    systemd-run --user --scope program_you_wanted_to_run
    

    (claro, se o programa for realmente um serviço, remova --scopee execute como serviço; se for para ser executado como sistema, não como programa de usuário, remova o --user. Idealmente, se for um serviço, você ' d apenas escrever as 6 linhas de configuração para torná-lo um serviço systemd completo com seu próprio escopo)

    • 3
  2. David G.
    2024-06-26T08:00:14+08:002024-06-26T08:00:14+08:00

    Há uma série de opções.

    1. Para qualquer campo visível, você pode filtrar nesse campo. Um candidato óbvio é TTY=pts/*number*. Observe que você precisará ativar a exibição do TTY. Inicia-se esta filtragem pressionando oou usando Opara adicionar outro filtro. Você pode isolar um comando para ser a única coisa em um tty com vários comandos, incluindo screen commandand xterm -e command, and ssh -t localhost command(que também tem a vantagem de não perder a saída).
    2. Você pode colocar todo o topo em um modo de visualização em árvore usando Ve, em seguida, fechar os nós pais indesejados com v. Isso deve permitir que você o restrinja o suficiente apenas para ver seu comando, embora possa levar algum tempo para restringi-lo. Talvez seja necessário combinar isso com outras coisas, já que normalmente há apenas uma ou duas árvores completas.
    3. Você pode executar o comando em seu próprio usuário e restringir a exibição com u.

    Essas técnicas, ou uma combinação delas, podem fornecer os resultados desejados.

    Editar: para facilitar a configuração, você pode querer escrever algo como tty;sleep 10no topo do seu script... ou mesmo em outro script que execseja o seu script de destino.

    • 2
  3. aviro
    2024-06-26T23:40:14+08:002024-06-26T23:40:14+08:00

    Se você não se importa em executar seu programa em um namespace de usuário diferente usando unshare, poderá filtrar topde acordo com o namespace do usuário.

    $ unshare -r
    $ readlink /proc/$$/ns/user
    user:[4026532793]
    

    Você também executa unshare -r <COMMAND>e procura o namespace do usuário com o pid.

    Agora, para visualizar todos os processos no namespace do usuário em top:

    1. Pressione fpara escolher um campo

    2. Usando as teclas de seta Down/ Up, navegue até nsUSER(USER namespace Inode).

    3. Ative-o pressionando a tecla de espaço

    4. Pressione “q” para voltar à topvisualização principal.

    5. Pressione “O” para acionar o filtro e filtrar os processos de acordo com o namespace do usuário

      add filter #1 (case sensitive) as: [!]FLD?VAL nsUSER=4026532793
      
    6. Feito.

    • 2
  4. wobtax
    2024-06-26T23:41:02+08:002024-06-26T23:41:02+08:00

    O que eu criei para minha própria pergunta não é tão legal quanto as outras pessoas postaram:

    #!/bin/bash
    # Tested with `top` from `procps-ng 4.0.3` and `pstree 23.6`.
    
    # Print the ID of the given process together with IDs of all its descendants
    get_pid_with_descendants() {
      pid="$1"
      pstree -ap "$pid" | sed -r 's/,([0-9]+).*|./\1/g'
    }
    
    # Monitor the given PID and its descendants every k seconds
    monitor_with_top() {
      pid="$1"
      k="$2"
    
      # While the process is running...
      while kill -s 0 "$pid" 2>/dev/null; do
        pids=( $(get_pid_with_descendants $pid) )
    
        # Print a top screen for all processes in this list
        top -bn1 "${pids[@]/#/-p }"
    
        # Wait k seconds
        sleep "$k"
      done
    }
    
    

    Em seguida, a execução monitor_with_top <my_PID> 2imprime a tela superior esperada a cada 2 segundos.

    ( Editar: ah, certo, como o aviro apontou, topsó funcionará com até 20 PIDs passados ​​dessa maneira. Seguindo a resposta de Marcus Müller, você pode usar htopwith --tree --readonlyem vez disso.)

    • 2
  5. Best Answer
    aviro
    2024-06-28T01:47:54+08:002024-06-28T01:47:54+08:00

    Como também queria um recurso semelhante, escrevi um script que faz isso.

    A vantagem do script é que ele toproda continuamente, então ele realmente mostra o consumo de CPU do último intervalo (3 segundos por padrão, mas pode ser alterado com o -d secsparâmetro).

    As respostas anteriores rodavam topuma vez a cada ciclo, e nesse caso topmostra apenas o consumo de CPU naquele exato momento. A média de 3 segundos é mais precisa.

    Também resolve o problema de topnão conseguir receber mais de 20 pids.

    Este script requer configuração topno "Modo Forest View" 1 : Você precisará iniciar top, pressionar Vpara alternar o modo florestal e depois pressionar Wpara gravar sua configuração atual em seu $HOME/.toprcarquivo de configuração 2 .

    O script 3 é executado topcontinuamente (por padrão), com todo o processo em execução no host, e faz algumas análises sedpara apresentar apenas o pid necessário e seus filhos.

    Aqui está o código. Não fiz nenhum teste sério e não me preocupei em pensar em casos extremos, apenas fiz funcionar. Talvez eu melhore isso no futuro.

    #!/bin/bash
    
    usage() {
      echo "Usage: toptree.sh <PID> [top options]"
      echo "You need to make sure your 'top' is configured to a tree mode"
      exit
    }
    
    cleanup() {
      ps -p $toppid >& /dev/null && kill $toppid
      rm -rf "$FIFO_DIR"
      trap - EXIT INT TERM
    }
    
    trap cleanup EXIT INT TERM
    
    parse_top() {
      local pid=$1
      ps -p $toppid >& /dev/null || exit # top is not running anymore
      sed -un -e $'/^top/,/PID/{p; /PID/{q}}' # print the header of top  
      local parent=$( 
        sed -nu \
          -e "/^ *$pid/{p; q}" \
          -e '/^top/{q 1}' # if we reached the next top cycle,
                           # it means we didn't find the parent process
      ) || exit 0  # sed didn't find the process
                   # The process is probably finished
    
      # Next, we want to find only the children of the process
      # Since this is in tree form, we want only processes
      # with greater indentation than the parent.
      # we catch the spaces before the branch (`-) of the parent
      local indent=$(sed -nr 's/.*[^ ](  +)`-.*/\1/p' <<< "$parent")
      
      [ -z "$indent" ] && exit # Didn't find the branch marker
                               # Happens if top is not in tree mode
      
      sed "s/$indent\`-/ \`-/" <<< "$parent" # Remove indentation
      
      # Now we're printing all the children
      # until we reach a process in a another process tree
      # (ie, the branch level is equal or smaller than parent) 
      # than the parent we're monitoring
    
      sed -unr "
        s/ $indent\`-/ \`-/p
        t next # if indentation >  parent's, next cycle
        q      # if indentation <= parent's, outside branch. quit.
        :next" 
      echo 
    }
    
    is_pid() {
      local pid=$1
      if [ -n "$pid" ] && [ "$pid" -eq "$pid" ] 2>/dev/null
      then
        if ! ps -p $pid >& /dev/null
        then
          echo $pid is not running
          exit 1 
        fi
      else
        echo $pid should be a number
        usage
      fi
    }
    
    
    PID=$1
    is_pid "$PID"
    
    shift
    FIFO_DIR=$(mktemp -d /tmp/toptree.XXXXXX)
    FIFO="$FIFO_DIR/fifo"
    mkfifo "$FIFO" 
    
    top -b -w 150 "${@}" > "$FIFO" & toppid=$!
    
    while parse_top $PID; do :; done < "$FIFO"
    

    Exemplo:

    $ toptree.sh
    Usage: toptree.sh <PID> [top options]
    You need to make sure your 'top' is configured to a tree mode
    
    $ toptree.sh 17349 -d 2 -n 2
    top - 20:29:27 up 90 days,  6:10, 396 users,  load average: 0.16, 0.14, 0.10
    Tasks: 1926 total,   1 running, 1925 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  0.4 us,  2.6 sy,  0.0 ni, 96.6 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
    KiB Mem:  39455878+total, 17768724 used, 37679008+free,    47308 buffers
    KiB Swap: 39427174+total,        0 used, 39427174+free. 11275700 cached Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S   %CPU  %MEM     TIME+ COMMAND
    17349 aviro     20   0   12096   8136   2976 S  0.000 0.002   0:00.32 `- bash
    15817 aviro     20   0    6988   2620   2388 S  0.000 0.001   0:00.00    `- bash
    15820 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15821 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15818 aviro     20   0    6988   2620   2388 S  0.000 0.001   0:00.00    `- bash
    15822 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15823 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15819 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00    `- sleep
    
    top - 20:29:29 up 90 days,  6:10, 396 users,  load average: 0.16, 0.14, 0.10
    Tasks: 1926 total,   1 running, 1925 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  0.2 us,  0.5 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem:  39455878+total, 17768724 used, 37679008+free,    47308 buffers
    KiB Swap: 39427174+total,        0 used, 39427174+free. 11275700 cached Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S   %CPU  %MEM     TIME+ COMMAND
    17349 aviro     20   0   12096   8136   2976 S  0.000 0.002   0:00.32 `- bash
    15817 aviro     20   0    6988   2620   2388 S  0.000 0.001   0:00.00    `- bash
    15820 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15821 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15818 aviro     20   0    6988   2620   2388 S  0.000 0.001   0:00.00    `- bash
    15822 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15823 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00        `- sleep
    15819 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00    `- sleep
    

    Observe que você pode adicionar qualquer sinalizador ao script que seria passado para top. Por exemplo, se você quiser ver as linhas de comando completas, adicione -c:

    $ toptree.sh 19140 -c -n1
    top - 20:40:52 up 90 days,  6:21, 398 users,  load average: 0.24, 0.13, 0.10
    Tasks: 1930 total,   1 running, 1929 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  0.8 us,  2.8 sy,  0.0 ni, 96.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem:  39455878+total, 17782400 used, 37677638+free,    45796 buffers
    KiB Swap: 39427174+total,        0 used, 39427174+free. 11276956 cached Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S   %CPU  %MEM     TIME+ COMMAND
    19140 aviro     20   0    6988   2624   2388 S  0.000 0.001   0:00.00 `- bash -c sleep 5 & sleep 5
    19141 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00    `- sleep 5
    19142 aviro     20   0    2500   1268   1188 S  0.000 0.000   0:00.00    `- sleep 5
    

    1 Gostaria que tophouvesse mais opções de linha de comando, que permitissem iniciar o modo de visualização da floresta a partir da linha de comando sem ter que alterar a visualização padrão, mas é o que é.

    2 Ao emitir o comando 'W' logo antes de sair do topo, você poderá reiniciar mais tarde exatamente no mesmo estado

    3 Poderia ter sido mais elegante fazer isso em python, mas tudo bem.

    • 1

relate perguntas

  • Por que a quebra da CPU do topo (opção 1) no modo não irix padrão mostra resultados conflitantes?

  • Significado da parte swap no comando top

  • Como os diferentes modos de CPU do top correspondem uns aos outros?

  • Por que o total de tarefas listadas na saída do comando 'top' não é igual a executar + dormir?

  • limpe os filtros adicionados no `top`

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve