LINUX.ORG.RU

Постоянная работа приложения на С

 , ,


2

4

Интересует вопрос. Есть программа. В функции main() выполняется код. Как реализовать так что бы программа работала постоянно. Она прослушивает пакеты через libpcap. Ну что то вроде как демон. Не знаю как реализовывать это на С/C++ т.к я начинающий. Заранее благодарен за подсказки или помощь.

man daemon man event loop

anonymous
()

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

XMs ★★★★★
()
int main(int arg, char *argv[])
{
    while(1)
    {
        /* do your stuff here */
    }

    return 0;
}
anonymous
()
while(daemon_live)
{
   /*твой код*/

       ...
   /*        */

   if(что-то не то)
   {
      daemon_live=false; 
   };

};
Dron ★★★★★
()
Последнее исправление: Dron (всего исправлений: 1)

Дополняю пример анонизмуса:

int main(int arg, char *argv[])
{
    while(1)
    {
        /* do your stuff here */
        sleep(10); // спим 10 миллисекунд, чтобы не грузить процессор
    }

    return 0;
}
Плюс, надо добавить реакцию на всякие там SIGKILL или SIGTERM точно не помню. Но вот эти работающие простоянно проги называются: демоны (ужос), службы, сервисы. И прибивают их при завершении работы ОС (либо просто завершения твоей сервисной программы) сигналами например, и их лучше ловить и обрабатывать и завершаться по-хорошему.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Ты превратил нормальный пример в ужос

Фрагмент /* do your stuff here */ должен содержать блокирующий системный вызов или эквивалентный вызов более высокоуровневого API, допустим библиотечную функцию которая блокируется до получения нового пакета и возвращающий результат сразу после. Либо это какой-то poll/select, ожидающий события на неблокирующем(их) сокете(ах)

Кстати в твоем примере интервал 10 секунд, а не миллисекунд, так что еще терпимо. А за busy wait в демоне с интервал 10мс надо бить по пальцам

annulen ★★★★★
()
Ответ на: комментарий от annulen

Кстати в твоем примере интервал 10 секунд, а не миллисекунд, так что еще терпимо

Смотря для чего именно :D

fornlr ★★★★★
()
Ответ на: комментарий от annulen

что накинулись, заклевали? :) пауза в любом случае необходима, если нет блокирующих вызовов, а то ТС обнаружит 100%-й зажор проца, конкретная задержка зависит от задачи, если что-то совсем фоновое, то там можно и на 5-10 секунд засыпать

I-Love-Microsoft ★★★★★
()

Скорее всего, у сетевого приложения будет какое-нибудь глобальное состояние. И простым циклом не обойдешься.

Надежнее будет тупо окружить приложение каким-нибудь шеловским скриптом, который будет перестартовывать приложение.

Ну или связаться с автором/наемным работником и попросить у него доделать фичу.

Deleted
()
Ответ на: комментарий от I-Love-Microsoft

ТС обнаружит 100%-й зажор проца

И поймет, что написал г-но

annulen ★★★★★
()
Ответ на: комментарий от annulen

Блокирующий, но он не ожидает требуемого события

На свете бывает много гитик. :) У меня есть такой демон, там правда 37 мс надо ждать, долго экспериментировал. Угадайте, зачем? Правильно, чтобы зажать скорость обмена, железка китайская вот такая кривая, хоть и рекомендовано для использования аж целой Yamaha.

vodz ★★★★★
()
Ответ на: комментарий от annulen

Бывает, но это не повод писать

Естественно. Всё у меня нормально, блокирующийся read, все дела. Просто в конце цикла usleep. :)

vodz ★★★★★
()
Ответ на: комментарий от vodz

37 мс надо ждать, долго экспериментировал

зажать скорость обмена

Всё у меня нормально, блокирующийся read, все дела. Просто в конце цикла usleep. :)

Почему бы вместо этого извращения не сделать неблокирующийся read + libevent, который умеет ratelimit? И никаких sleep'ов.

Sorcerer ★★★★★
()
Ответ на: комментарий от Aswed

А почему вредные?

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

i-rinat ★★★★★
()
Ответ на: комментарий от Sorcerer

Почему бы вместо этого извращения

Я бы ещё месяц бы эскпериментировал. Мне не нужно было точная скорость, судя по всему китаец забыл припаять управление потоком (не у меня, а вообще в этой модели железки, так как на форуме все плакали), потому мне нужно было чтобы была задержка после порции данных - подождать из расчёта 0.6 мс на каждый байт, отправив первые 64 байта сразу, при наличии следующих. Эксперимент показал, что при 36 мс оно глючит, а при 38+ уже только тормозит.

vodz ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Стоит упомянуть, что программы с непрерываемым циклом в С++ - это Undefined Behavior. Т.е. нужно, чтобы где-то был break у твоего while(1).

anonymous
()

Делал когда-то русский перевод английской статьи Blocking I/O, Nonblocking I/O, And Epoll.

Это матчасть, без которой не стоит и браться за свой Event Loop.

Также надо узнать о следующих понятиях:

  • event loop
  • асинхронный I/O
  • task-based parallelism
  • continuation passing style - для начального уровня лучший способ реализовать task-based parallelism

В pcap есть готовый event loop.

Почему не надо while(1), читать здесь

quiet_readonly ★★★★
()
Последнее исправление: quiet_readonly (всего исправлений: 1)
Ответ на: комментарий от hexandr

Во-первых, как уже было правильно замечено, основной код работает в условно бесконечном цикле. То есть, в условие цикла ставится функция либо флаг, которые обычно (в комбинации со всеми операторами в условном выражении) дают true, и лишь в некоторых, единичных случаях — false. Эти случаи приходятся лишь на события извне (команда пользователя, сигнал процессу, специальная последовательность входящих данных), и у них нет зависимости от событий внутри приложения. Например, цикл со счётчиком, завершающий работу по достижении счётчиком определённого значения, конечен. Цикл, который принимает от пользователя данные и прерывает работу только после получения определённой последовательности — можно рассматривать и так, и так, в зависимости от природы входных данных и способа использования цикла.

Далее, если рассматривать демонов, у них есть ещё ряд особенностей. Помимо очевидного, вроде того, что они не привязаны в терминалу, есть некоторые архитектурные особенности, которые зависят лишь от полёта фантазии разработчиков в попытках добавить стабильности. Например, общение с пользователем и приём сигналов может лечь на один процесс, а вся логика работы — на дочерние, запускаемые первым. Если один из них упадёт, первый процесс получит SIGCHLD, и упавший будет перезапущен. Они могут управляться сигналами, пайпами, dbus-ом и ещё много вариантов, как. Здесь всё зависит от задачи, которую ты решаешь.

Как организовать тело цикла, решать уже тебе. Тут выше уже написали про poll/select, epoll, да ещё кучу полезных ссылок кинули. Читай, думай, что из этого тебе подойдёт

XMs ★★★★★
()

Хороший пример есть в книге «Хакинг, искусство эксплоита», посмотри там про сниферы.

u0atgKIRznY5
()

Также есть в книге «Программирование боевого софта под линукс».

u0atgKIRznY5
()
/* gcc main.c -o mydaemon */

#include <unistd.h>

int main(int argc, char* argv[]) 
{
    int created = daemon(0,0);

    if (created == 0) {
	/* Daemon created */

        int isWorker = 1;

        while (isWorker) {
	    /* Add your code here */

	    /* Sleep some time */
	    usleep(100);
	}
	return 0;

    } else {
	/* Cannot create daemon */
	return -1;
    }
}

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

Pro100User
()
Ответ на: комментарий от Pro100User

Это самое простое

И снова здравствуйте? (c) Зачем вы ещё один пример добавили со sleep()? Топик читать пробовали, не?

vodz ★★★★★
()
Ответ на: комментарий от vodz

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

Pro100User
()
Ответ на: комментарий от Pro100User

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

Pro100User
()
Ответ на: комментарий от Pro100User

решение со sleep не является неверным и в подавляющем ряде случаев необходимым и самым простым решением. Другие решения - как правило сложнее в реализации, и если человек не знает что такое демон, то понять event loop и прочие модели ему на данном этапе абсолютно ни к чему.

Pro100User
()
Ответ на: комментарий от Pro100User

И твое право не отвечать мне.

Безусловно, как и право отвечать.

Топик не читал, это мое право.

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

Что, таки начали читать тред? Может и до моего примера, когда мне sleep понадобился доберётесь...

vodz ★★★★★
()
Ответ на: комментарий от vodz

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Некошерно паузу слипом делать. Кошерно при помощи select с обработкой ошибок.

anonymous
()

Учись пользоваться поиском

Элементарный гуглинг по simple daemon sample on c language выводит на

https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux

и прямо в топовом ответе есть пример главного файла. Про fork, setsid и umask прочитаешь в манах.

anonymous
()
Ответ на: комментарий от Iron_Bug

таки есть подозрение, что вы эту железку как-то неправильно готовите

Я не виноват, все вопросы к китайцу. Это midi-usb. Оно должно само следить за буферами. И, как уже говорил, эта проблема не только у меня, все у кого была - все плакали на форумах, причём у них то в виндозе, а у меня в Linux-е, то есть и дрова не при чём. 37 - это тупая эмпирика, 0.6 мс/байт.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)

смотря какие у тебя требования, то есть можно конечно daemon() заюзать - тогда читай man daemon.

К слову - в POSIX кажется нет daemon() вроде в 4.4BSD, короче может где то не работать так как надо.

в таком случае надо

switch(pid = fork()) {
  case 0:
    if(setsid() < 0) { /* detach i.e. became a session lead */
      fprintf(stderr, "Cannot detach a session: %s\n", strerror(errno));
      /* exit code here */
    }
    /* let's ignore SIGCHLD and SIGHUP signals */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);
    break;
  default:
    if(pid < 0) {
      fprintf(stderr, "Cannot fork(): %s\n", strerror(errno));
/*exit code here */
    } else {
      /* parent exit code here */
    }
  }

а потом -

switch(pid = fork()) {
  case 0:
    /* change mask for newly created files */
    umask(S_IXUSR | S_IXGRP | S_IXOTH | S_IROTH | S_IWOTH);
    /* close all filedescriptors */
    for(r = sysconf(_SC_OPEN_MAX); r > 0; r--) close(r);
    break;
  default:
    if(pid < 0) {
      fprintf(stderr, "Cannot fork(): %s\n", strerror(errno));
      /* exit here */
    } else {
      /* parent - exit code here */
    }
    break;
  }

ну и далее цикл твой, и еще полезно - если надо конечно, сменить euid и прочая, вплоть до cwd - лень тут вбрасывать - не emacs все таки.

ну и повесить обработчик на SIGTERM чтоб по нему твой демон корректно завершался (не знаю как в systemd, но в нормальных init системах демонам посылают SIGTERM для завершения).

alwayslate ★★
()
Ответ на: комментарий от alwayslate

А вы действительно понимаете то, что скопипастили?

/* close all filedescriptors */

Это daemon() не делает, а только для 0,1,2. И слава богу.

/* let's ignore SIGCHLD and SIGHUP signals */

Это вообще вредные советы. SIGCHLD тут вообще не при чём, ибо останется один потомок, а отец завершится. SIGHUP не будет послан при успешном setsid(), а если надо реагировать на управляющий сигнал, то и обработчик надо рисовать.

vodz ★★★★★
()
Ответ на: комментарий от vodz

А вы действительно понимаете то, что скопипастили?

дак сам и писал.

Это daemon() не делает, а только для 0,1,2. И слава богу.

ну и зачем остальное ? stderr, stdin, stdout и все остальное лишнее надо закрывать.

SIGCHLD тут вообще не при чём, ибо останется один потомок, а отец завершится.

вы таки верите в то что это все прям вот последовательно будет происходить ? я - нет.

alwayslate ★★
()
Ответ на: комментарий от alwayslate

и все остальное лишнее надо закрывать.

Кому лишние?! Открыли значится сокет, соединились с клиентом, форкнули обработчик и закрыли дескрипторы чтобы жизнь мёдом не казалось. Ага...

вы таки верите в то что это все прям вот последовательно будет происходить

Это было б хорошим вопросом, если б ещё вы могли объяснить, как SIGCHLD может получить потомок исходя из этого кода.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Кому лишние?! Открыли значится сокет, соединились с клиентом, форкнули обработчик и закрыли дескрипторы чтобы жизнь мёдом не казалось. Ага...

так, а причем тут этот кейс вообще ? я так понял человек хочет запилить демона, а потом уже в режиме «демона» то есть уже после всех этих вещей открывать сокет и тд и тп.

Это было б хорошим вопросом, если б ещё вы могли объяснить, как SIGCHLD может получить потомок исходя из этого кода.

ну вы бы посмотрели что там таки не один форк...

alwayslate ★★
()
Ответ на: комментарий от alwayslate

уже после всех этих вещей открывать сокет и тд

Ээээ... Это совсем-совсем вредный совет, вы ещё биндиться на привилегированный порт в потомке скажите надо, чтоб уж совсем не учитавшись логами было не понять почему оно умирает тихо, либо так и висит под рутом и взламываемо. :)

ну вы бы посмотрели что там таки не один форк...

То что там два, это хорошо и правильно. Вот только опять же - кому посылается? Родитель один завершается, первый потомок становится новым родителем, у которого потомок остаётся жить, а он сам опять же - умирает. Я кажется понял, где вы тормозите, вы думаете, что fork() порождает 2 потомка? Так?

Ну и чтобы два раза не вставать, зачем в коде последний break ?

vodz ★★★★★
()
Ответ на: комментарий от vodz

Ээээ... Это совсем-совсем вредный совет, вы ещё биндиться на привилегированный порт в потомке скажите надо, чтоб уж совсем не учитавшись логами было не понять почему оно умирает тихо, либо так и висит под рутом и взламываемо. :)

ну ok, я описал свой случай и выдрал все это из контекста. тк в моем случае создается unix сокет с нужными правами, uid/gid. да и, для логов тупо и просто потом пользуюсь syslog просто для того чтоб не пояснять это я куски все эти выдрал и частично заменил.

Я кажется понял, где вы тормозите, вы думаете, что fork() порождает 2 потомка? Так?

нет, лень сейчас снова это все открывать. суть в том что exit code это не exit() и определенные действия таки нужно проделать в умирающем родителе, и потенциально может прилететь sigchild который в *моем* кейсе можно игнорировать. да, это можно было также выдрать тк в данном случае не нужно.

Ну и чтобы два раза не вставать, зачем в коде последний break ?

артифакт. оно там не надо. копировал из emacs сюда и редактировал здесь уже.

alwayslate ★★
()
Ответ на: комментарий от alwayslate

нет, лень сейчас снова это все открывать. суть в том что exit code это не exit() и определенные действия таки нужно проделать в умирающем родителе, и потенциально может прилететь sigchild который в *моем* кейсе можно игнорировать. да, это можно было также выдрать тк в данном случае не нужно.

А может быть вы тоже скопипастили код у такого же бедолаги, который это нихрена не понимает? Только это вас может чуть обелить :)

Видите ли, SIG_IGN у sigchld - действие по умолчанию, единственно, когда это требуется выставлять, то это когда ранее, в этом же процессе был установлен signal(SIGCHLD, sigchld_handler), который больше не нужен.

Оно, конечно, при двойном fork-е, когда выживает только один, SIGCHLD системой может быть послан, когда дед переживёт отца внука. Вот только в вашем коде signal(SIGCHLD, SIG_IGN) вызывается в отце, и потому совершенно бесполезен, а с учётом SIG_DFL - бесполезен в квадрате. Зато с маниакальным упорством пытаетесь доказать полную чушь вместо того, чтобы отправиться делать RTFM.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 4)
Ответ на: комментарий от vodz

А может быть вы тоже скопипастили код у такого же бедолаги, который это не понимает? Только это вас может чуть обелить :)

да вот не пытаюсь обелится. SIGHUP кстати тоже могет быть в оооочень редких случаях. и он так «хорошо» сделает.

Видите ли, SIG_IGN у sigchld - действие по умолчанию, единственно, когда это требуется выставлять, то это когда ранее, в этой же программе был установлен signal(SIGCHLD, sigchld_handler), который больше не нужен.

Бла бла, а вдруг как в случае с неким сокетом (это про ваше - зачем все дескрипторы закрывать) оно уже было ? (не поленился открыл - было). Далее, Ignored оно в linux, вроде в POSIX об этом не написано (хотя тут я может и не прав тк уже не помню всего).

Оно, конечно, при двойном fork-е когда выживает только один, SIGCHLD системой может быть послан, когда дед переживёт отца внука.

щито ???

таки

SIGCHLD 20,17,18 Ign Child stopped or terminated

в случае - я вызвал fork(), вернулся в родителя, и пошел завершать работу, но внезапно меня шедулер вытеснил, поехал выполнять потомка и там что то пошло не так, присылает мне сигнал и привет (мне уже на том моменте нафиг не сдалось делать то что раньше было), проще его игнорировать, нежели усложнять обработчик и вообще весь код вводя какие нить глобальные состояния, и завершить работу.

может в linux это смысла нет (но есть в этом сомнение), но не единым linux жив мир.

вообщем посмотрите man 7 signal да.

PS придирка на уровне (как я понял) оно уже и так ignored и нехрен делать лишний сисколл. ну правда смешно. ну а если у вас идея в стиле на каждый чих 33 раза форкаться - это еще смешнее.

alwayslate ★★
()
Ответ на: комментарий от vodz

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

о да, дада, я дебил и придурок, не читаю маны, копипастой занимаюсь и тд и тп. супер. ok. кстати что значит делать RTFM ? делать Read The Fucking Manual ? это простите как ?

alwayslate ★★
()
Ответ на: комментарий от alwayslate

Бла бла,

Что за ***? Нафиг.

тоже могет быть в оооочень редких случаях

Неужели вы не чувствуете, что вот такие высказывания вас и выдают, стоит только нарваться на того, кто знает и спросит объяснить, когда наступает этот случай? :)

в случае - я вызвал fork(), вернулся в родителя, и пошел завершать работу, но внезапно меня шедулер вытеснил, поехал выполнять потомка и там что то пошло не так, присылает мне сигнал и привет

У вас ровно два варианта: либо повесить обработчик и попытаться что-то сделать (но мало что удастся, если подумать), либо игнорить, что и есть по умолчанию. Ибо если уж сам fork завершился успешно, то пусть потомок сам и жалуется в лог, что у него пошло не так. Родителю=копии не возможно толком узнать проблемы сына, так как он имеет после fork-а другое адресное пространство, хоть и на первых порах и копию.

может в linux это смысла нет (но есть в этом сомнение), но не единым linux жив мир.

Оно и видно, что «документацию не читай, беги отвечай». Ладно, всё равно не спится: это таки posix. Сами посудите, накой убивать отца, если сын завешился? А хоронить кто будет? :)

(как я понял)

Увы, полностью мимо.

vodz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.