[C++] Linux & FD_SETSIZE

Pagina: 1
Acties:
  • 171 views sinds 30-01-2008
  • Reageer

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Ik heb een server app en ik wil de set size verhogen om meer connecties aan te kunnen. Via een define dacht ik dit op te lossen, maar dat levert onder Linux een warning op. Is FD_SETSIZE niet de goede om aan te passen?

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define FD_SETSIZE 1024

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        return 0;
}

In file included from /usr/include/sys/time.h:30,
                 from test.cpp:3:
/usr/include/sys/select.h:77: warning: `FD_SETSIZE' redefined
test.cpp:1: warning: this is the location of the previous definition

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Onder FreeBSD werkt het wel goed. Daar staat ook expliciet in de man-page:
The default size of FD_SETSIZE is currently 1024. In order to accommodate programs which might potentially use a larger number of open files with select(), it is possible to increase this size by having the program define FD_SETSIZE before the inclusion of any header which includes <sys/types.h>.
FD_SETSIZE wordt in select.h ook beschermd met guards, zodat 'ie niet dubbel gedefinieerd wordt.

Het lijkt me echter niet onwaarschijnlijk dat de Linux header files wat anders in elkaar zitten. Misschien moet je per se beginnen met het includen van <sys/types.h>?

  • odysseus
  • Registratie: Augustus 2000
  • Laatst online: 27-05 19:37

odysseus

Debian GNU/Linux Sid

Je kan hier in ieder geval omheen werken door een #ifdef => #undef => #endif op te nemen in je code, ergens voor het punt dat je die #define voor FD_SETSIZE hebt staan. De preprocessor zou dan wel door moeten hebben dat dit intentioneel is, lijkt me :).

Leven is het meervoud van lef | In order to make an apple pie from scratch, you must first create the universe.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
odysseus schreef op 09 februari 2004 @ 19:11:
Je kan hier in ieder geval omheen werken door een #ifdef => #undef => #endif op te nemen in je code, ergens voor het punt dat je die #define voor FD_SETSIZE hebt staan. De preprocessor zou dan wel door moeten hebben dat dit intentioneel is, lijkt me :).
Nee, dat werkt niet, want mijn define komt eerst.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Soultaker schreef op 09 februari 2004 @ 18:27:
Onder FreeBSD werkt het wel goed. Daar staat ook expliciet in de man-page:
Het is misschien een Debian specifieke bug, want met Red Hat 9, Suse 8 ES en MacOS X gaat het wel goed.

  • 4VAlien
  • Registratie: November 2000
  • Laatst online: 26-05 14:22

4VAlien

Intarweb!

Het is toch ook alleen een warning? In dit geval kan je die gewoon negeren neem ik aan.

  • blaataaps
  • Registratie: Juli 2001
  • Niet online
Als ik eerst de includes doe, en daarna mijn eigen #define, krijg ik het volgende:
code:
1
2
setsize.c:6: warning: `FD_SETSIZE' redefined
/usr/include/sys/select.h:77: warning: this is the location of the previous definition
zodat de define de include redefined, in plaats van andersom, is dat niet precies wat je wil, of kijk ik verkeerd?

[ Voor 10% gewijzigd door blaataaps op 09-02-2004 19:59 ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
blaataaps schreef op 09 februari 2004 @ 19:58:
Als ik eerst de includes doe, en daarna mijn eigen #define, krijg ik het volgende:
code:
1
2
setsize.c:6: warning: `FD_SETSIZE' redefined
/usr/include/sys/select.h:77: warning: this is the location of the previous definition
zodat de define de include redefined, in plaats van andersom, is dat niet precies wat je wil, of kijk ik verkeerd?
Je kijkt verkeerd.
Als ik redefine, dan is het fd_set type te klein, omdat die gedefined wordt met een te lage waarde van FD_SETSIZE.
4VAlien schreef op 09 februari 2004 @ 19:54:
Het is toch ook alleen een warning? In dit geval kan je die gewoon negeren neem ik aan.
Dat zou ik kunnen doen (vooral omdat de default al 1024 is onder Linux), maar het is niet echt netjes.

[ Voor 21% gewijzigd door Olaf van der Spek op 09-02-2004 20:09 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
OlafvdSpek schreef op 09 februari 2004 @ 20:05:
Dat zou ik kunnen doen (vooral omdat de default al 1024 is onder Linux), maar het is niet echt netjes.
Helaas kan je dat ook niet doen, want dan heb je precies hetzelfde probleem: dan wordt je fd_set ook te klein gedefinieerd (want de header code zet de te kleine waarde weer terug).

Je moet dus echt guards in je header file hebben staan. Hoe ziet die header file er uit, rond de beruchte redefinition?

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Soultaker schreef op 09 februari 2004 @ 20:22:
[...]

Helaas kan je dat ook niet doen, want dan heb je precies hetzelfde probleem: dan wordt je fd_set ook te klein gedefinieerd (want de header code zet de te kleine waarde weer terug).

Je moet dus echt guards in je header file hebben staan. Hoe ziet die header file er uit, rond de beruchte redefinition?
Maar onder Linux is de default 1024, dus dat is goed. Onder Windows is het 64.

code:
1
2
3
4
/usr/include/bits/types.h:
#define __FD_SETSIZE    1024
/usr/include/sys/select.h:
#define FD_SETSIZE              __FD_SETSIZE

Dus onder Debian zou ik __FD_SETSIZE kunnen nemen, maar dat is volgens mij ook niet de bedoeling.

En bij nader inzien werkt het onder Red Hat 9 ook niet. Alleen krijg ik daar gewoon geen warning.

[ Voor 7% gewijzigd door Olaf van der Spek op 09-02-2004 20:58 ]


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

OlafvdSpek schreef op 09 februari 2004 @ 18:17:
Ik heb een server app en ik wil de set size verhogen om meer connecties aan te kunnen. Via een define dacht ik dit op te lossen, maar dat levert onder Linux een warning op. Is FD_SETSIZE niet de goede om aan te passen?
Zie het comment in /usr/include/linux/posix_types.h. Verder : Het aantal open FD's per proces is ook gelimiteerd in de kernel. Aanpassen van die define betekende vroeger het hercompilen van kernel + glibc. De kernel is niet meer nodig, glibc weet ik zo 123 niet.

Ten tweede : Waarop gebruik je select ? De performance ervan is beroerd met grotere sets, en het managen ervan is helemaal een ramp (iig kwa performance). Ik zou eens serieus kijken naar poll / epoll.

[ Voor 16% gewijzigd door igmar op 10-02-2004 17:06 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Waarom is het managen en de performance met het gebruik van select een ramp? Zoveel verschil zit er toch niet tussen select() en poll(); alleen maar het gebruik van een (fixed-size) bitset of een user-allocated array? Ik neem aan dat ze in de kernel hetzelfde mechanisme gebruiken.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
igmar schreef op 10 februari 2004 @ 17:00:
Zie het comment in /usr/include/linux/posix_types.h. Verder : Het aantal open FD's per proces is ook gelimiteerd in de kernel. Aanpassen van die define betekende vroeger het hercompilen van kernel + glibc. De kernel is niet meer nodig, glibc weet ik zo 123 niet.

Ten tweede : Waarop gebruik je select ? De performance ervan is beroerd met grotere sets, en het managen ervan is helemaal een ramp (iig kwa performance). Ik zou eens serieus kijken naar poll / epoll.
Omdat de software ook onder Windows moet draaien en grote sets vaker uitzondering dan regel zijn.
Soultaker schreef op 10 februari 2004 @ 17:16:
Waarom is het managen en de performance met het gebruik van select een ramp? Zoveel verschil zit er toch niet tussen select() en poll(); alleen maar het gebruik van een (fixed-size) bitset of een user-allocated array? Ik neem aan dat ze in de kernel hetzelfde mechanisme gebruiken.
Omdat sets worden geimplementeerd met een array en je dus telkens lineaire zoektochten uitvoert als je FD_SET en FD_ISSET aanroept.

[ Voor 5% gewijzigd door Olaf van der Spek op 10-02-2004 17:58 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
OlafvdSpek schreef op 10 februari 2004 @ 17:57:
Omdat sets worden geimplementeerd met een array en je dus telkens lineaire zoektochten uitvoert als je FD_SET en FD_ISSET aanroept.
FD_SET en FD_ISSET zetten alleen maar een bitje om; dat is gewoon een goedkope bitoperatie in constante tijd.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Soultaker schreef op 11 februari 2004 @ 04:16:
FD_SET en FD_ISSET zetten alleen maar een bitje om; dat is gewoon een goedkope bitoperatie in constante tijd.
Eh?
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define FD_SET(fd, set) do { \
    u_int __i; \
    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
        if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { \
            break; \
        } \
    } \
    if (__i == ((fd_set FAR *)(set))->fd_count) { \
        if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
            ((fd_set FAR *)(set))->fd_array[__i] = (fd); \
            ((fd_set FAR *)(set))->fd_count++; \
        } \
    } \
} while(0)
Goedkoop?

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Soultaker schreef op 10 februari 2004 @ 17:16:
Waarom is het managen en de performance met het gebruik van select een ramp?
Omdat de kernel altijd de hele array doorloopt (bit == filedescriptor) , en je dat als applicatie ook moet doen. Verder moet je ook zelf je administratie bijhouden, en met elke select de hele set weer terugcopieeren.
Zoveel verschil zit er toch niet tussen select() en poll(); alleen maar het gebruik van een (fixed-size) bitset of een user-allocated array? Ik neem aan dat ze in de kernel hetzelfde mechanisme gebruiken.
De kernel gebruikt intern (ook voor select) poll-semantiek. Verder is de administratie met poll() en epoll() een stuk makkelijker.

[ Voor 16% gewijzigd door igmar op 11-02-2004 15:14 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Ah ok, we hebben het over Linux. Dan kan het kloppen dat FD_SET/FD_ISSET niet handig geïmplementeerd zijn. Een reden te meer om het gebruik van select() te motiveren met portability en poll() te gebruiken als alleen Linux of moderne UNIX'en met poll() van belang zijn. Het probleem blijft natuurlijk dat poll() geen gestandaardiseerde functie is en 'ie dus niet overal aanwezig zal zijn; Windows kent 'm bijvoorbeeld niet.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Wanneer zijn ze wel handig geimplementeerd dan?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Deze BSD code ziet er wel handig uit:
C:
1
2
#define FD_ISSET(n, p)  ((p)->__fds_bits[(n)/_NFDBITS] & __fdset_mask(n))
#define FD_SET(n, p)    ((p)->__fds_bits[(n)/_NFDBITS] |= __fdset_mask(n))

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
De code die ik postte komt uit Windows. Linux is blijkbaar ook handig tegenwoordig. Ik vraag me toch af wat de meerwaarde van poll dan is.

C++:
1
2
3
# define __FD_SET(d, set)    (__FDS_BITS (set)[__FDELT (d)] |= __FDMASK (d))
# define __FD_CLR(d, set)    (__FDS_BITS (set)[__FDELT (d)] &= ~__FDMASK (d))
# define __FD_ISSET(d, set)  (__FDS_BITS (set)[__FDELT (d)] & __FDMASK (d))

[ Voor 75% gewijzigd door Olaf van der Spek op 11-02-2004 21:05 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Inderdaad; ik heb het even gecontroleerd Linux doet het op een vergelijkbare manier. Het is dus in het algemeen wel een simpele bitoperatie.

Ik vermoed dat het in Windows wat kostbaarder is omdat nieuwe file descriptors daar niet gegarandeerd het laagst mogelijke nummer hebben. Een bitset van 1024 betekent namelijk dat je alleen file descriptors tot 1024 kunt gebruiken. Ook onder UNIX zijn er natuurlijk best situaties te construeren waarbij maar een paar file descriptors in gebruik zijn en die toch een nummer boven de 1024 hebben, maar in het algemeen kun je daar wel rekening mee houden.
Pagina: 1