[cdev] обсуждение реализации ps

Grigoriy A. Sitkarev sitkarev на komitex.ru
Пн Мар 15 22:23:20 MSK 2010


Отвечу по-порядку.

Анатолий Кораблёв пишет:

> Все таки мне совершенно не понятен смысл перегружать структуру process 
> указателем на следующй элемент списка (struct process *next) и можно сказать 
> усложнять читаемость кода.
> Как оговаривалось с субботу может появиться необходимость расширить 
> функционал программы и создавать динамический массив процессов после 
> применения фильтра - тогда как раз структура process в текущем виде может 
> оказаться более внятной.
> При наличиии отдельной структуры для связного списка сама структыра 
> программы становится более понятной для чтения (четко видно что есть связный 
> список структур-процессов а не какая то двусмысленная структыра которая для 
> каждого процесса определяет некоторый следующий процесс) и более гибкой 
> (позволяет легко расширить этот список до более сложных структыр не 
> переписывая половину кода). Поэтому позволю себе не согласиться с тем, что 
> уплотнение структуры "связный список процессов" до одной структуры является 
> оправаднным (выигрыш в быстродействии минимален а читаемость кода резко 
> ухудшается и появляется двусмысленность структуры).

Правильно делаете что сомневаетесь.

В Си программах это идиоматический приём - включение в структуру 
описателя указателей, которые позволяют формировать различные структуры 
(списки, деревья и т.д.). В таком случае сразу видно что из них будет 
формироваться список или много списков. А глобальная переменная, которая 
будет указывать на начало списка, снабжается комментарием.

struct process *plist; /* list of processes data */

Си упаковывает структуры в том порядке, в каком они перечислены в 
определении. Сейчас ваша struct tnode аналогична структуре:

struct process {
	int pid;
	char state;
	...
	struct process *next;
};

и она здесь лишняя.

А надо бы:

struct process {
	struct process	*next;
	int		pid;
	char		state;
	...
};

Это идиома в Си и никакой двусмысленности здесь нет, также как и 
ухудшения читаемости.

Если тебе надо будет хранить этот элемент в ещё одном списке, то вы 
добавите ещё один указатель.

Список бы тогда вы обходили так:

struct process *pp;

for (pp = plist; pp != NULL; pp = pp->next) {

	...
}

что тоже является идиомой. И никаких счётчиков не надо.

Когда вы будете делать сортированный массив, то вы будете делать массив 
указателей - struct process **. Если там будет абстрактный void ** то 
это тоже оправдано только в случае если вы пишете абстрактный тип 
"массив указателей". Уловили в чём соль?

С производительностью тоже дело такое, indirections ведут к тому что кеш 
используется неэффективно. И на самом деле производительность там 
заметно возрастает. По этой же причине часто самые простые алгоритмы 
оказываются быстрее чем сложные. Честно - массив это самая эффективная 
структура, всё дело в кеше.


> С удовольствием уже сейчас услышу про получение количества тиков в секунду и 
> про использование lookup table - не целыми же днями готовиться к экзамену - 
> ночью можно и код пописать.

Про тики я вам расскажу, а вот как вам завернуть в цикл распечатку 
заголовка и таблицы подумаете сами. Подумайте, какие данные вам нужны 
для этого, какими полями они должны быть представлены в структуре, 
сформируйте такую структуру, статически инициализируйте её. Потом 
напишите в цикле уже вывод.

По поводу тиков идея такая ещё появилась:

а) в /proc/uptime есть два числа, в первом секунды uptime системы во 
втором секунды в течении которых система простаивала без загрузки.
б) в /proc/stat в строке, начинающейся с "cpu " дано четыре числа. Сумма 
  этих чисел - количество тиков прошедших с момента инициализации 
системы, т.е. времени отсчёта uptime.

Если мы возьмём (uptime / total_ticks) то получим приблизительно сколько 
у нас тиков в секунду. Это в теории. На практике при чтении файлов и 
выковыривании полей у нас могут быть задержки, и надо проверить что 
промежуток времени, в течении которого читались файлы из /proc, был 
небольшой. Считать надо аккуратно, да ещё и прикинуть погрешность и если 
она велика, заново пересчитать (перечитать) значения.

Далее, примерно прикинуть, по результату, какое CONFIG_HZ могло быть в 
ядре. В нашем случае можно ограничиться случаями 100 Hz, 250 Hz и 1000 
Hz. Хотя для других архитектур там могут быть и другие значения.


> Что касается предыдущих замечаний - все замечания выписаны отдельно и будут 
> исправлены в обязательном порядке - просто есть привычка сначала 
> реализовывать функционал и порадоваться за то, что хоть что то работает, а 
> затем уже "причесывать" код.

Хорошо, если так.

--
Г.А.






Подробная информация о списке рассылки cdev