2015-11-06 3 views
-1

Я пытаюсь построить регистратор процессов в C на Linux, но проблема с его исправлением. Я бы хотел, чтобы у него было 3 колонки: USER, PID, COMMAND. Я использую вывод ps aux и пытаюсь динамически присоединить его к массиву. То есть для каждой строки ps aux выходов я хочу добавить строку в свой массив.Почему значения в моем динамическом массиве 2D-символов перезаписываются?

Это мой код. (Для того, чтобы сохранить выход Короче говоря, я Grep только возвышенным. Но это может быть что угодно.)

#define _BSD_SOURCE 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

int main() 
{ 
    char** processes = NULL; 
    char* substr = NULL; 
    int n_spaces = 0; 
    int columns = 1; 

    char line[1024]; 
    FILE *p; 
    p = popen("ps -eo user,pid,command --sort %cpu | grep sublime", "r"); 
    if(!p) 
    { 
     fprintf(stderr, "Error"); 
     exit(1); 
    } 

    while(fgets(line, sizeof(line) - 1, p)) 
    { 
     puts(line); 
     substr = strtok(line, " "); 
     while(substr != NULL) 
     { 
      processes = realloc(processes, sizeof(char*) * ++n_spaces); 

      if(processes == NULL) 
       exit(-1); 

      processes[n_spaces - 1] = substr; 

      // first column user, second PID, third all the rest 
      if(columns < 2)//if user and PID are already in array, don't split anymore 
      { 
       substr = strtok(NULL, " "); 
       columns++; 
      } 
      else 
      { 
       substr = strtok(NULL, ""); 
      } 
     } 
     columns = 1; 
    } 
    pclose(p); 

    for(int i = 0; i < (n_spaces); i++) 
     printf("processes[%d] = %s\n", i, processes[i]); 

    free(processes); 

    return 0; 

} 

Выход для цикла в конце выглядит следующим образом.

processes[0] = user 
processes[1] = 7194 
processes[2] = /opt/sublime_text/plugin_host 27184 

processes[3] = user 
processes[4] = 7194 
processes[5] = /opt/sublime_text/plugin_host 27184 

processes[6] = user 
processes[7] = 27194 
processes[8] = /opt/sublime_text/plugin_host 27184 

processes[9] = user 
processes[10] = 27194 
processes[11] = /opt/sublime_text/plugin_host 27184 

Но из puts(line) я получаю, что массив должен содержать фактически это:

user  5016 sh -c ps -eo user,pid,command --sort %cpu | grep sublime 
user  5018 grep sublime 
user  27184 /opt/sublime_text/sublime_text 
user  27194 /opt/sublime_text/plugin_host 27184 

Таким образом, по-видимому, все значения перезаписи, и я не могу понять, почему ... (Кроме того, я не понимаю, откуда взялось значение 7194 в processes[0] = 7194 и processes[4] = 7194).

Что я здесь делаю неправильно? Как-то можно сделать вывод похожим на результат puts(line)?

Любая помощь будет оценена!

+3

Здесь нет двухмерного массива. У вас есть 1D массив указателей. Для каждого указателя выделите кусок памяти и скопируйте туда строку чтения. – Lundin

ответ

1

Возвращаемое значение strtok является указателем на строку, которую вы токенизируете. (Лексема была сделана нулевым байтом пути перезаписи первого сепаратора после него с '\0'.)

Когда вы читаете новую строку с fgets, вы перезаписать содержимое этой строки и все жетоны, а не только те из последний разбор, укажите фактическое содержимое строки. (Указатели на предыдущие токены остаются в силе, но содержимое в этих местах изменяется.)

Есть несколько способов исправить это.

  • Вы можете сделать маркеры, в которых сохраняются массивы символов и strcpy проанализированное содержимое.
  • Вы можете дублировать синтаксические маркеры с (нестандартным) strdup, который выделяет память для строк в куче.
  • Вы можете прочитать массив строк, так что маркеры действительно уникальны.
+0

Спасибо за помощь! Я пробовал путь «strdup», и он работает! Еще один вопрос: какой будет самый быстрый (с точки зрения времени выполнения) метод? – drZaius

+0

Трудно сказать, какой метод является самым быстрым.Повторное выделение занимает некоторое время (и поэтому перераспределение «процессов» для каждой строки должно быть медленным), но в вашем примере я ожидаю, что внешние вызовы будут иметь более важное значение для производительности, чем то, как вы храните строки. Вариант 'strdup', безусловно, самый гибкий mathod, но не забывайте« освобождать »дублированные строки позже. –

+0

Где я могу освободить дублированные строки? После цикла while (как и 'free (процессы)')? Есть ли альтернатива 'popen()' и, возможно, альтернатива 'ps' для повышения производительности? Возможно, без использования внешних вызовов? – drZaius

1

strtok возвращает указатель в строку, которую он обрабатывает. Эта строка всегда хранится в переменной line. Таким образом, все ваши указатели в массиве указывают на то же место в памяти.

Как писал @Lundin в комментарии, здесь нет двумерного массива, а всего лишь одномерный массив указателей.

Смежные вопросы