LINUX.ORG.RU

А давно дескрипторы сокетов стали рандомно выделяться?

 


0

1

Я всегда думал, что они от нуля вверх растут, но откуда-то взялся fd=228184560 сразу и, что характерно, работает. Это у меня бесконечный цикл скорее всего, создающий много сокетов, или это нормальное поведение линукса?

Вопрос снимается, я сам дебил. Выделяются от 0 в сторону увеличения нормально.



Последнее исправление: hlamotron (всего исправлений: 2)

Выдали свободный — пользуйся. А суть вопроса. Это всеравно что: google -> как сделать айди по порядку

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

Кому-то другому не могли, я один. Внутри процесса пространство значений дескрипторов виртуальное и изолированное от других процессов и растёт от нуля.

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

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

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

Системный вызов select() имеет неудобное API, которое лучше работает с маленькими номерами дескрипторов. Так что обычно они с 0 вяло растут.

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

в glibс - посрать. да и вообще, даже в кернеле там если и может быть «оптимизация», то слишком незаметная на фоне скорости сетевого обмена.

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

Нет, не посрать. Во-первых надо извращаться, чтобы передать fd_set больше стандартных 8192. Во-вторых, если дескриптор и в самом деле 100-миллионный, то select() должен копировать в ядро 12 мегабайт битовых полей на каждый fd_set. А эти битовые поля еще все прочитать надо. Многие приложения даже размер буфера в read() не задают такой большой.

anonymous
()

Какой glibc? какое ядро? Вообще похоже на какой-то неинициализированный мусор.

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

Сетевой обмен ведь разный бывает — как ADSL, с которого ютуб с трудом грузится, так и гигабитные каналы, где число тактов на пакет не так и велико.

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

такой. обработка дескрипторов select'ом может быть разной только в ядре, там теоретически можно указывать их длину. но на уровне glibc эта возможность зарезана и длина дескриптора фиксирована. так что никакой разницы между «маленькими» и «большими» идентификаторами в юзерспейсе просто нет.

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

Там проблема с размером fd_set, который, как правило, лежит в стеке. Если дескриптор слишком большой (на линуксе обычно > 1024, на солярисе > 65000 с чем-то), то ты просто попортишь стек, да так, что valgrind не сразу укажет место. Особенно феерично, когда программа, которая до этого годами прекрасно работала на солярисе, начинает вдруг чудить страшным образом на линуксе

dave ★★★★★
()

А ты не проверял на предмет того, не остаются ли висеть у тебя в системе CLOSE_WAIT? Может быть, у тебя утечка?

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

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

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

эмм... а почему в стеке, собственно? это уж как программист решит. идиотов, которые пихают в стек массивы в 1024 элемента, я пока не видела.

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

Мда... Суть от тебя явно ускользнула.

Множества дескрипторов в select начинаются с нуля. И они не разреженные. Поэтому если дескрипторы будут случайно выдаваться в рамках 32-битных чисел, иногда придётся для этих множеств пятисотмегабайтные битовые поля выделять. И на каждый вызов такие массивы в ядро передавать, которое будет их сканить.

Ну и какой вообще смысл в регулируемом размере тогда?

И вообще глупо сисколами прикрываться. Никто не будет их напрямую звать, когда в libc есть POSIX-обёртки.

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

идиотов, которые пихают в стек массивы в 1024 элемента

Glibc выделяет на стеке как минимум 1024 байта в некоторых функциях. Я не проверял, но это число может быть и больше.

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

неудобное API, которое лучше работает с маленькими номерами дескрипторов

ого, прикольно. мимо проходил, не сишник, но интересно. как это?

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

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

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

ну так я и говорю про обёртки. обёртка прибита гвоздями. а в масштабах ядра всё равно выдаётся куча дескрипторов и они в произвольный момент для одного процесса могут быть отнюдь не близкими по значению. так что вопрос автора вполне имеет место быть и это не какая-то нештатная ситуация.

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

Хм... Видимо, я тогда смотрел в какую-то старую версию glibc. Сейчас гляжу в код, а getpwnam выделяет буфер в куче.

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

select() принимает битовые массивы, где каждый дескриптор - бит.

Если дескрипторы большие (например, 10 миллионов), то копирование в ядро начинает занимать заметное время.

select(): http://lxr.free-electrons.com/source/fs/select.c#L551

get_fd_set() / set_fd_set(): http://lxr.free-electrons.com/source/include/linux/poll.h#L130

линейный перебор в do_select(): http://lxr.free-electrons.com/source/fs/select.c#L432

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

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

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

вопрос ведь в том, насколько оно «заметное» со стороны юзерспейсной софтины. думаю, для 99.99% софта это вообще до фени и никто не заметит разницы.

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

У юзерспейса 2 проблемы: 1. Нужно извращаться, чтобы передать дескрипторы размером больше FD_SETSIZE: завершать программу или увеличивать FD_SETSIZE. Обычно этого не делают и получают некорректные программы, которые в лучшем случае падают. В худшем порят данные.

В примере ниже я это сделал переопределив __FD_SETSIZE (жутко непортабельно).

2. Падение производительности. Это лучше замерять на конкретном примере. Допустим, мы хотим иметь один(!) 200 миллионный дескриптор (в предположении, что select() работает примерно одинаково быстро на маленьких и больших дескрипторах). dup2() легко нам позволит такой создать.

#define _BITS_TYPES_H
#include <bits/typesizes.h>
#undef _BITS_TYPES_H

#define BIG_FD       200000000
#define __FD_SETSIZE 400000000

#include <sys/select.h> /* select() */
#include <errno.h>      /* errno */
#include <stdio.h>      /* printf() */
#include <string.h>     /* strerror() */
#include <unistd.h>     /* dup2() */
#include <stdlib.h>     /* exit() */
#include <sys/resource.h> /* getrlimit() */

fd_set wr; /* larger than stack */

int main() {
    int r;
    int i;
    struct rlimit fd_limits;

    printf ("INFO: sizeof(fd_set)=%zu\n", sizeof(fd_set));

    r = getrlimit (RLIMIT_NOFILE, &fd_limits);
    if (r == -1) {
        printf ("ERROR: getrlimit failed: %i/%s\n", r, strerror(errno));
        exit (1);
    }
    printf ("initial limits: cur=%zu/max=%zu\n", (size_t)fd_limits.rlim_cur, (size_t)fd_limits.rlim_max);

    if (fd_limits.rlim_max < BIG_FD) {
        printf ("HINT: allow more open files:\n");
        printf ("      # sysctl -w sysctl -w fs.nr_open=3000000\n");
        printf ("      $ ulimit -n 2000000\n");
    }

    fd_limits.rlim_cur = fd_limits.rlim_max = BIG_FD + 64;

    r = setrlimit (RLIMIT_NOFILE, &fd_limits);
    if (r == -1) {
        printf ("ERROR: setrlimit failed: %i/%s\n", r, strerror(errno));
        exit (2);
    }

    r = dup2(STDOUT_FILENO, BIG_FD);
    if (r == -1) {
        printf ("ERROR: dup2 failed: %i/%s\n", r, strerror(errno));
        exit (4);
    }

    for (i = 0; i < 3; ++i) {
        printf ("%u: zero out\n", i);
        FD_ZERO(&wr);
        printf ("%u: set bit\n", i);
        FD_SET(BIG_FD, &wr);

        r = select (BIG_FD + 1, NULL, &wr, NULL, NULL);
        if (r == -1) {
            printf ("ERROR: select() failed: %i/%s\n", r, strerror(errno));
            exit (5);
        }
        if (FD_ISSET(BIG_FD, &wr))
            printf ("%u: yay!\n", i);
    }
    return 0;
}

Замеряем задержки:

$ gcc -O2 a.c -o a && sudo strace -r ./a
...
     0.000028 write(1, "INFO: sizeof(fd_set)=50000000\n", 30INFO: sizeof(fd_set)=50000000
) = 30
     0.000027 getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
     0.000024 write(1, "initial limits: cur=1024/max=409"..., 34initial limits: cur=1024/max=4096
) = 34
     0.000027 write(1, "HINT: allow more open files:\n", 29HINT: allow more open files:
) = 29
     0.000024 write(1, "      # sysctl -w sysctl -w fs.n"..., 47      # sysctl -w sysctl -w fs.nr_open=3000000
) = 47
     0.000026 write(1, "      $ ulimit -n 2000000\n", 26      $ ulimit -n 2000000
) = 26
     0.000026 setrlimit(RLIMIT_NOFILE, {rlim_cur=200000064, rlim_max=200000064}) = 0
     0.000024 dup2(1, 200000000)        = 200000000
     0.209725 write(1, "0: zero out\n", 120: zero out
) = 12
     0.019051 write(1, "0: set bit\n", 110: set bit
) = 11
     0.000070 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.042828 write(1, "0: yay!\n", 80: yay!
)  = 8
     0.000112 write(1, "1: zero out\n", 121: zero out
) = 12
     0.003194 write(1, "1: set bit\n", 111: set bit
) = 11
     0.000201 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.053294 write(1, "1: yay!\n", 81: yay!
)  = 8
     0.000119 write(1, "2: zero out\n", 122: zero out
) = 12
     0.003957 write(1, "2: set bit\n", 112: set bit
) = 11
     0.000058 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.044225 write(1, "2: yay!\n", 82: yay!
)  = 8
     0.000059 exit_group(0)             = ?
     0.066349 +++ exited with 0 +++

Что видим: задержка вызова select() минимум 42 (42, 53, 44) миллисекунды. Время выводится перед каждым write() сразу после select() (strace замеряет время между системными вызовами):

     0.000070 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.042828 write(1, "0: yay!\n", 80: yay!

     0.000201 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.053294 write(1, "1: yay!\n", 81: yay!

     0.000058 select(200000001, NULL, [], NULL, NULL) = 1 ()
     0.044225 write(1, "2: yay!\n", 82: yay!

Это значит, что больше, чем 24 (1/0.042) раза в секунду на одном ядре select() вызвать не получится.

Не очень-то много :)

Читателю предлагается поиграться с числом циклов и числом в BIG_FD :)

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

селект уже давно легаси. epoll и poll как бы есть.

это не считая что афтар ошибся.

i36_zubov
()

IMHO, полагаться в программе на какие-то предположения о порядке выделения дескрипторов, значит быть ССЗБ и обрекать программу на UB.

Если только из любознательности... но и любознательность можно направить на что-то заведомо более полезное.

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