LINUX.ORG.RU

Не то зомби, не то - нет ... ???


0

0

Написал tcp-сервак. Для каждого подключения я делаю fork и оно варится самостоятельно. После завершения сеанса - close(socket) и exit(0). Все хорошо, все работает, только после каждого коннекта к серваку, дочерние процессы переходят в состояние Z (зомби), а после окончания работы родителя - они пропадают. Кто-нибудь может обьяснить почему так происходит и как этого избежать. Система - Slackware 9.0.

Исходники:
"tcp_serv.c" :

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdarg.h>
#include<string.h>
#include<errno.h>
#include<netdb.h>
#include<fcntl.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "lib/skel.h"



main(int argc, char* argv[])
{
struct sockaddr_in peer;
int peerlen = sizeof(peer);
SOCKET s;
SOCKET ns;
int pid;


s = tcp_server(NULL, argv[1]);
while(1) {
ns = accept(s, (struct sockaddr *)&peer, &peerlen);
if (!isvalidsock(ns))
error(1, errno, "Ошибка вызова accept\n");

if ((pid=fork()) == -1)
error(1, errno, "Ошибка вызова fork\n");

if (pid==0) {
/* Дочерняя часть */
if (close(s))
error(1, errno, "Ошибка вызова close\n");

send(ns, "Hello world!\n", 13, 0);

if (close(ns))
error(1, errno, "Ошибка вызова close\n");

exit(0);
}
/* Родительская часть */
if (close(ns))
error(1, errno, "Ошибка вызова close\n");
}

exit(0);
}


"skel.h" :

#in#ifndef __SKEL_H__
#define __SKEL_H__ 1

#define FALSE 0
#define TRUE 1
#define NLISTEN 5

typedef int SOCKET;

#define set_errno(e) errno = (e)
#define isvalidsock(s) ( (s) >= 0 )

void error(int status, int err, char* format, ...);
static void set_address(char* hname, char* sname, struct sockaddr_in *sap, char* protocol);
SOCKET tcp_server(char* hname, char *sname);

void error(int status, int err, char* format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);

if (err)
fprintf(stderr, "%s (%d)\n", strerror(err), err);
if (status)
exit(status);
}


static void set_address(char* hname, char* sname, struct sockaddr_in *sap, char* protocol)
{
struct servent* sp;
struct hostent* hp;
char* endptr;
short port;

bzero(sap, sizeof(*sap));
sap -> sin_family = AF_INET;
if (hname != NULL) {
if (!inet_aton(hname, &sap -> sin_addr)) {
hp = gethostbyname(hname);
if (hp == NULL)
error(1,0,"Неизвестный хост: %s\n", hname);
sap -> sin_addr = *(struct in_addr *)hp -> h_addr;
}
}
else
sap -> sin_addr.s_addr = htonl(INADDR_ANY);
port = strtol(sname, &endptr, 0);
if (*endptr == '\0')
sap -> sin_port = htons(port);
else {
sp = getservbyname(sname, protocol);
if (sp == NULL)
error(1,0,"Неизвестный сервис %s\n", sname);
sap -> sin_port = sp -> s_port;
}
}

SOCKET tcp_server(char* hname, char *sname)
{
struct sockaddr_in local;
SOCKET s;
const int on = 1;

set_address(hname, sname, &local, "tcp");
s = socket (AF_INET, SOCK_STREAM, 0);
if (!isvalidsock(s))
error(1, errno, "Ошибка вызова socket ");

if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)))
error(1, errno, "Ошибка вызова setsockopt ");

if (bind(s, (struct sockaddr *)&local, sizeof(local)))
error(1, errno, "Ошибка вызова bind ");

if (listen(s, NLISTEN))
error(1, errno, "Ошибка вызова listen ");

return(s);
}
#endif /* __SKEL_H__ */

★★

Все правильно. Пока не сделаешь в папе wait(), дочки будут зомбями.

Либо делай waitpid() из обработчика сигнала, либо игнорируй SIGCHLD (могут быть проблемы с портабильностью, НЕ POSIX-way), либо делай двойной форк (дочка форкается и выходит, папа ее ждет wait()ом, а полезную работу делает внучка) - стандартный Юних-вэй, но немного тормозит - либо прокидывай односторонний пайп к папе, и жди всех на select()е (тогда и сокет для accept()а надо селектом слушать).

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

Сделал так: ... if ((pid=fork()) == -1) error(1, errno, "Ошибка вызова fork\n"); if (pid==0) { /* Дочерний процесс */ if ((pid_2=fork()) == -1) error(1, errno,"Ошибка вызова fork\n"); if (pid == 0) { /* Процесс - внучка */ if (close(s)) error(1, errno, "Ошибка вызова close\n"); send(ns, "Hello world!\n", 13, 0);

if (close(ns)) error(1, errno, "Ошибка вызова close\n");

exit(0); }

if (close(s)) error(1, errno, "Ошибка вызова close\n"); exit(0); } /* Родительский процесс */ wait(&ch_stat); if (close(ns)) error(1, errno, "Ошибка вызова close\n"); ... Зомбей не появляется, но теперь двоятся данные, отсылаемые серваком. Такое впечатление, что внучки - близняшки :-).

spiro ★★
() автор топика
Ответ на: комментарий от Die-Hard

Сделал так:
...
if ((pid=fork()) == -1)
error(1, errno, "Ошибка вызова fork\n");

if (pid==0) {
/* Дочерний процесс */
if ((pid_2=fork()) == -1)
error(1, errno,"Ошибка вызова fork\n");

if (pid == 0) {
/* Процесс - внучка */
if (close(s))
error(1, errno, "Ошибка вызова close\n");

send(ns, "Hello world!\n", 13, 0);

if (close(ns))
error(1, errno, "Ошибка вызова close\n");

exit(0);
}

if (close(s))
error(1, errno, "Ошибка вызова close\n");
exit(0);
}
/* Родительский процесс */
wait(&ch_stat);
if (close(ns))
error(1, errno, "Ошибка вызова close\n");
...
Зомбей не появляется, но теперь двоятся данные, отсылаемые серваком.
Такое впечатление, что внучки - близняшки :-).

spiro ★★
() автор топика

> теперь двоятся данные
Т.е.?

В дочке ns закрой, может, поможет? - хотя, вряд ли, она сразу дохнет...
Может, дочка не дохнет, и держит ns открытым? - посмотри, может, с логикой
где промахнулся?

У меня подобные кострукции во многих местах работают.

Пара замечаний:
Лучше waitpid() юзай - ты же pid знаешь- будет более thread-безопасно.

Закрывай ненужные дескрипторы после форков.

Вообще, вот тут некий дискашен был на сходную тему:
http://www.linux.org.ru/view-message.jsp?msgid=235834


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