QA Fest — конференция №1 по тестированию и автоматизации. Финальная программа уже на сайте >>
×Закрыть

Структура консольного приложения на Си под Линукс

Всем доброго времени суток.

Была поставлена задача написать консольное приложение на си под линукс. Интерфейс приложения должен был поддерживать текстовые комманды в терминале (типа start, stop, help, select <smth>, show <smth> и.т.д.), причем после ввода соответствующей комманды должен был прерываться основной цикл программы и выполняться соответствующая подпрограмма.

Вопрос по структуре подобных приложений.

Поскольку я с таким раньше не сталкивался то, вероятнее всего, выбрал не самый лучший вариант реализации, а именно: main -> fork -> (menu process(parent), child process(main loop)). В родительском процессе выделяется кусок общей памяти в которую попадает комманда при вводе с клавиатуры, в процессе -потомке идет выполнение основного цикла с постоянной проверкой общей памяти.

Ссылка на программу github.com/...Onanko/tcp-ip-sniffer.git

Подскажите пожалуйста альтернативные, более правильные пути реализации подобного.

LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Артем, если задание еще актуально и задание звучит так, как его ниже описал Анлпей Чернявский, посмотри как это сделано в проекте KADNode.
github.com/mwarning/KadNode

В задании (да-да, в том самом) требовалось сделать по сути клиент-серверное приложение с обменом даными (желательно через Unix сокеты, но и сойдут POSIX очереди/XSI очереди/etc если правильно реализированны). Форки тут ни к чему и нужны только для запуска демона из консольного интерфейса с помощью команды start. Консольному интерфейсу же достаточно было дальше просто читать команды через fgets, отправлять демону, получать ответ, печатать ответ.

Это тестовое у вас было, что ли?)

Ну я это уже нашла, потому и уточнила, правильно ли поняла ситуацию

Большое спасибо за ответ! Теперь стало понятно как нужно было реализовать коммуникацию между процессами.

В вы это требование не указали в задании. Извини, но методов, как реализации многопоточности, так и межпроцессного обмена море и разных. И если чел не угадал, то он не подходит.
Это как бы сильно намекает на то, что вы ищете телепата.

Я же не говорю, что нужно именно так делать и другие варианты не принимаются, а лишь навел пример как можно было сделать. Его код несколько не соответствует тому, что было явно указано в задании.

Ну это я вывел из твоего поста выше. Написал бы, что это один из возможной тучи вариантов решения я бы не прицепился.

Ну мы и задание видели только в интерпритации топикстартера. хз как оно в оригинале звучало.

Честно говоря, начиная тему хотел просто узнать варианты построения приложений подобных описанному в топике.

В требованиях было необходимо сделать сервис для сбора статистики о сетевом трафике: демон — сниффер (чего я не сделал) и интерфейс коммандной строки — другой процесс для общения с демоном.

Теперь то уже понятно, что должно было получится.

Зачем форкаться? Уже давно нити придумали. pthread гуглите. И вообще, я бы подобное на Go писал.

в линуксе разница между этими двумя подходами минимальна
stackoverflow.com/a/4855101/6275324

Очень странный способ парсинга и система имен. Если я правильно понял логику, строка с IP живет в структуре с названием node, у которой есть дети-ноды под все возможные цифры, и при появлении некоторой цифры происходит что-то вообще странное:
if (current -> children[i] == NULL)
{
node* newNode = createNewNode();
current -> children[i] = newNode;
current = newNode;
}
— создаем новую ноду, записываем ее в дети текущей, а потом поверх текущей записываем свежесозданную. Логика мне непонятна. Если это все работает — расскажите как?

Работает.

current -> children[i] = newNode;
Это ж указатели, по этому не
записываем ее в дети текущей, а потом поверх текущей записываем свежесозданную
, а делаем так, что и-тый «ребенок» текущего узла ссылается на адрес новосозданного узла, а потом текущим становится последний.

а, указатель на первую таки сохраняется, это было не так просто заметить )) Но вопрос остается — зачем так сложно? Это ведь ничем не лучше чем хранить айпишник просто как строку, и идти по ней посимвольно

Одним из требований было обеспечить поиск айпишника с O(log(n))

А каким образом это реализуется в данном подходе?

...а, в итоге понял. Но — определенно неочевидно, я бы совсем иначе писал

Я бы вначале погуглил опенсорс решения, и поизучал их. Одно из первых в выдаче github.com/michaelmacinnis/oh

В реале для этого пользуются собственно батником, написанным на каком угодно языке, на котором удобно общаться с командной строкой. Оптимальный вариант — пиши родной шелл.
А вот уже основному процессу, который по сути отдельное приложение, идёт сигнал. PID процесса при этом сам процесс кладёт файлом в конкретное место на файловой системе.

Итого — С тебе здесь не уместен ни коим боком. Но если требования на С — пиши на нём. Задача так понимаю учебная, а значит не заморачивайся сильно с обработкой формата ввода. Сделай чтобы понимала конкрентые команды в одной единственно возможной формулировке и возьми с полки печеньку. Реально писать интерфейс командной строки на С тебе не придётся никогда, есть готовые библиотеки на другие человеческие языки.

Интерфейс приложения должен был поддерживать текстовые комманды в терминале
GNU readline либо не-GPL альтернативы

Как про мне, не обязательно несколько процессов форкать.
Можно написать однопроцессное приложение — multi threading ( man7.org/...an3/pthread_create.3.html ).

Типова реалізація:

	pcapfd = pcap_get_selectable_fd()
	cmdfd = ...

	while(1)
	 {
		poll({cmdfd, pcapfd})

		if(POLLIN cmdfd)
			read_cmd_from(cmdfd);
		if(POLLIN pcapfd)
			pcap_dispatch();
	}

Це все в одному процесі.

IPC через shm можна робити, але не так, і для такої задачі це погана ідея.
Якщо у вас є fork, майже завжди десь має бути waitpid.

Я думал над реализаций в одном процессе, но уперся в то, что программа останавливалась и ожидала ввода с клавиатуры когда в цикле доходила до fgets(). Насколько я понимал тут нужно было вводить ограничения по времени такого ожидания, что могло привести к потере пакетов, либо добавлять второй поток/процесс.

причем после ввода соответствующей комманды должен был прерываться основной цикл программы и выполняться соответствующая подпрограмма.

Дело в том, что некоторые подпрограммы тоже зациклены, и как их прервать таким макаром я не знал

Так проблема в первую очередь или в твоем понимании задачи или в формулировке. Как можно угадать, что ты имеешь в виду, но не говоришь. Тебе ответили ровно по написанному тобой.
Сформулируй четко, получишь ответ под твою формулировку.
Часто можно и работающий поток засуспендить, но это грабли и лучше их не раскладывать.
Если у тебя требование С, то уже вопрос, а какая у тебя многопоточность и есть-ли она?
А может тебе просто на плюсах написать, там потоки уже в STL идут и кода писать самому кот наплакал. Кто ж кроме тебя знает твои требоваия.

Согласен, плохо сформулировал.
Требование С.
Присутствует 2 процесса, но, как я уже понял лучше бы использовал 2 потока в одном процессе, либо вообще попробовать сделать как говорит Jorzchyk Pushysty. Буду разбираться.

Вариантов море, можешь сделать пачку потоков и очередь заданий им.

Погугли как сделать non-blocking keyboard polling, linux.

И pcap, и комманды должны читаться в non-blocking mode.

www.tcpdump.org/pcap3_man.html
www.tcpdump.org/...ap_setnonblock.3pcap.html

A handle can be put into ``non-blocking mode’’, so that those routines will, rather than blocking, return an indication that no packets are available to read ... Non-blocking mode is often combined with routines such as select(2) or poll(2) or other routines a platform offers to wait for any of a set of descriptors to be ready to read.

Для комманд, это выглядит как bytes = read(cmdfd, buf, sizeof(buf)) ровно один раз после POLLIN, и парсинга buf[0:bytes]. Если ввод типа комманда-Enter-комманда-Enter, там всегда будет одна комманда.

Подписаться на комментарии