nixp.ru v3.0

29 марта 2024,
пятница,
09:46:48 MSK

anonymous написал 5 августа 2005 года в 15:25 (665 просмотров) Ведет себя неопределенно; открыл 1814 темы в форуме, оставил 5575 комментариев на сайте.

Приветствую.

Изучаю написание сетевого демона под линукс. Сейчас мой демон имеет структуру:

sd = socket();

bind(sd, … );

if ( listen(sd, 5) == -1)

{ … }

for (;;) {

ns = accept(sd, …);

pid=fork();

if (pid == 0) /* child */

{

close(sd);

recv();

close(ns);

exit(0); /* exit status of child */

}

close(ns); /* parent */

}

Вопрос такой: при этой архитектуре будут ли у демона серьезные ограничения по числу одновременно обрабатываемых запросов? Если да, то что нужно менять в структуре приложения? Поможет ли здесь select()/poll() ?

Спасибо!

iliya

Будут ограничения по памяти (см. описание fork()).

Вот select() помочь может, poll() в этом случае хуже.

Можно посмотреть в сторону pthread.

Longobard

селект маздай :)

roman_m по такому принципу как у тебя — это так устроен апач первый (он создает тучу процессов, каждый обслуживает отдельного клиента). Второй апач создает процесс, в каждом процессе сколько-то потоков (прописывается в конфиге), а уже потоки обслуживают клиентов. У меня (в недописанном еще HTTP сервере Flexer, который я забросил уже писать ибо работа и учеба) как раз используется такой же вариант. Только там все хитрее, создается пул процессов и потоков (чтоб не тратить потом время на их создание отдельно), когда появляется клиент новый — то его данные передаются свободному процессу/потоку из пула.

rgo
LONGOBARD
селект маздай :)

Ага, но это смотря зачем, а точнее как этот select пользуется. Пока потоков не слишком много, то действительно лучше потоки, по всем параметрам (простота кода, безопасность за счёт _attribute__((__thread_local_)), быть может, скорость). А когда количество потоков/процессов становится достаточно большим, то ядро начинает притормаживать — ему же надо регулярно приоритеты пересчитывать для всех потоков/процессов, есть и другие куски ядра, время выполнения которых прямо пропорционально количеству задач. То есть, если это апач, у которого не так много одновременно живущих соединений и на каждое соединение надо запускать php интерпретатор, или там cgi процесс — то это один вариант. А ежели irc сервер, в котором соединений может быть пара тысяч и при этом работа с отдельно взятым соединением проста, то уж лучше select, и, может быть, переадресация запроса другому потоку/процессу, если, конечно, хочется многопроцессорность пользовать.

Где-то я какой-то фак читал (может из pthread?), там был подобный вопрос, и рекомендации были такие: пускай будет один поток, который listen, и пачка, которые select. Там речь шла именно о тысячах соединений.

ЗЫ какой attribute прикольный стал :)

Steck

Правильно сказали смотри в сторону нитей. Создавай для кажого соеденения отсоедененную нить. Вот пример накидал. Есть желание посмотри может поможет чем

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define HOSTLEN 256
#define BACKLOG 1
int server_bind(int port, int back)
{
      struct sockaddr_in sock;
      struct hostent *hp;
      char hostname[HOSTLEN];
      int fd;
      fd = socket(AF_INET,SOCK_STREAM,0);
      if(fd <0){
            perror("socket");
            return -1;
      }
      memset(&sock,0,sizeof(struct sockaddr));
      gethostname(hostname,HOSTLEN);
      hp=gethostbyname(hostname);
      bcopy((void *)hp->h_addr,(void *)&sock.sin_addr,hp->h_length);
      sock.sin_port = htons(port);
      sock.sin_family = AF_INET;
//      saddr.sin_addr.s_addr=htonl(INADDR_ANY);
      if(bind(fd,(struct sockaddr *)&sock,sizeof(struct sockaddr)) !=0)
      {
            perror("Bind");
            return -1;
      }
      if(listen(fd,back) != 0)
      {
            perror("listen");
            return -1;
      }
      return fd;
}
setup(pthread_attr_t *attrp)
{
      pthread_attr_init(attrp);
      pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
}
void *h_call(void *fdptr)
{
        FILE *fp;
        char request[BUFSIZ];
        int fd,c;
        fd = *(int *)fdptr;
        free(fdptr);
        fp = fdopen(fd,"r+");
      while(1){        
      fgets(request,BUFSIZ,fp);
        fprintf(stderr,"got a call on %d: request = %s\n",fd,request);
      fprintf(fp,"YOU SEND: %s\n",request);
      }
        fclose(fp);
}
main(int argc, char *argv[])
{
      int sock,fd;
      int *fdptr;
      pthread_t work;
      pthread_attr_t attr;
      if(argc == 1) {
            fprintf(stderr,"USAGE: serv port\n");
            exit(1);
      }
      sock = server_bind(atoi(argv[1]),BACKLOG);
      if (sock == -1)
      {
            perror("making socket");
            exit(2);
      }
      setup(&attr);
      while(1)
      {
            fd = accept(sock,NULL,NULL);
            fdptr = (int *)malloc(sizeof(int));
            *fdptr = fd;
            pthread_create(&work,&attr,h_call,fdptr);
      }
}
anonymous

Спасибо за ответы.

Касательно тредов — в других конференциях писали, что треды хорошо реализованы в ядре 2.6, я же жестко привязан только к ядру 2.4.18, возможны грабли?

И второе — в моем демоне обслуживание одного соединения не должно (теоретически :) ) занимать более 1сек., быть может форки не так сильно и навредят?

Steck

Попробуй треды и форки. Погоняй и посмотри что лучше.

rgo

глянь сюда ещё http://httpd.apache.org/docs/2.0/ru/misc/perf-tuning.html — полезно знать (особенно секция «accept Serialization — multiple sockets»).

http://cebka.pp.ru/mywiki/ru%2eunix%2eprog%2eservers%2efaq

Последние комментарии

ecobeingecobeing.ru
Экология и вегетарианство на благо всем живым существам Планеты.