LINUX.ORG.RU

Posix Threads


0

0

$ vim test.c

#include <stdio.h>
#include <pthread.h>

void* thread_func(void* p)
{
int i;
for(i=0;i<10;i++)
printf("%d\n",i);
fflush(stdout);
return NULL;
}
int main(int argc,char **argv)
{
pthread_t tid;
pthread_create(&tid,NULL,&thread_func,NULL);
if(tid<0)
printf("error\n");
return 0;
}

$ cc -lpthread test.c -o test
$ ./test
$
Числа не выводятся. В чем тут ошибка? Пробовал pthread_join после pthread_create - то же самое.

anonymous

ещё один ССЗБ

cvv ★★★★★
()


Ты неправильно обрабатываешь ощибки.

Из info libc:
The threads operations (`pthread_*') do not use ERRNO.
Instead they return an error code directly.

То есть не if(tid<0), a:

int rc = pthread_create(...);
if (rc)
{
// ошибка, в rc - ее код
}

То есть возврат 0 - это успешное завершение. Если вернули
не ноль, это ошибка. Возвращенное значение это то, что
"обычная" функция записала бы в errno.

Да, если добавить pthread_join(tid, NULL) перед return 0 то все печатается.

Onanim
()

они не успевают выводится..
у тебя программа завершается СРАЗУ после создания нити.

поставь в main какую-нить задержку и насладись выводом чисел

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

получается, что как только основной поток завершается, все "дечерние" автоматически прибиваются?

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

>получается, что как только основной поток завершается, все "дечерние" автоматически прибиваются?

ббрр - а ждать то кто будет ? ;)

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

да мало ли, может роль "главного" переходит к другому.

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

cvv :

> в зависимости от конкретной ситуации. если завершается по exit() то - всегда

в какой конкретной ситуации он(основной поток) может завершится так, чтобы порождённые потоки остались работать ?

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

>я потоки вообще не пользую но гдето в книгах читал, кажись "advanced programming for Linux"

anyway, в линухе их имеет смысл юзать только с ветки 2.6, до этого это был замаскриванный fork();

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

>до этого это был замаскриванный fork();

ну эт ты перегнул. в LinuxThreads никаких замаскированых fork() никогда небыло.

новые процессы не только fork() создаёт

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

> anyway, в линухе их имеет смысл юзать только с ветки 2.6, до этого это был замаскриванный fork();

Слишком сильное утверждение по поводу бессмысленности ибо fork замаскирован вполне качественно.

Про нюансы типа того, что fork - это замаскированный clone, я вообще молчу.

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

> в какой конкретной ситуации он(основной поток) может завершится так,
> чтобы порождённые потоки остались работать ?
>

Что-то такое я когда-то читал про LinuxThreads... но это ессно баг,
AFAIK POSIX требует завершения всего процесса.

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

> получается, что как только основной поток завершается, все "дечерние" автоматически прибиваются?

нет. но вполне возможно, что в вашей libc exit() на самом
деле означает exit_group(), а этот вызов завершает thread
group.

сделате syscall(__NR_exit, 0), все должно работать.

idle ★★★★★
()

1. tid надо инициализировать

2. я бы записал не void* thread_func(void* p) , а void thread_func(void)

3. попробуй pthread_create(&tid,NULL,thread_func,NULL);

4. сделай ldd test , по моему надо подключать доп библиотеки, аля -lpthread , -lposix

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

> > в зависимости от конкретной ситуации. если завершается по exit() то - всегда

> в какой конкретной ситуации он(основной поток) может завершится так, чтобы порождённые потоки остались работать ?

это совершенно _нормальная_ ситуация, с точки зрения ядра.

другое дело, что реализация libpthread может иметь свое
мнение на это счет.

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

binr : срочно ступайте внимательно читать литературку, man`ы, стандарты и упомянутую в треде ALP..

нельзя-же так позориться

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

>Слишком сильное утверждение по поводу бессмысленности ибо fork замаскирован вполне качественно.

fork() в этом контексте не сама ф-ция, я имел ввиду процесс ;) что так и есть ;) посему время создания потока и процесса не сильно то и отличаются до 2.6

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

2alphex_kaanoken:
> fork() в этом контексте не сама ф-ция, я имел ввиду процесс ;) что так
> и есть ;) посему время создания потока и процесса не сильно то и
> отличаются до 2.6

Сам-то понял, что сказал? :-/
Может как-нить расшифруешь по-русски?

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

ну попытался доступно объяснить, ладно - поток до 2.4(включительно) это замаскированный процесс, со всеми вытекающими - так понятней ?

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

MKuznetsov:

> в какой конкретной ситуации он(основной поток) может завершится так, чтобы порождённые потоки остались работать ?

Насколько я понял, согласно POSIX'у завершение по main() pthread_exit() не должно прибивать порожненные нити.

Правда, linux threads в этом месте глючат. NTPL не пробовал.

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

> Насколько я понял, согласно POSIX'у завершение по main()
> pthread_exit() не должно прибивать порожненные нити.
>

Да, Solaris так и поступает.
А вот NPTL вроде нет, завершает все (ну про LinuxThreads вообще не будем).

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

>2. я бы записал не void* thread_func(void* p) , а void
thread_func(void)
Да-да-да... я это уже исправил, теперь все работает (с sleep или с pthread_join).
И еще:
Если сразу после проверки на ошибку начать что-то выводить в цикле, порожденный поток далеко не сразу начнет свой вывод.

int i;
pthread_t tid;
pthread_create(&tid,NULL,&thread_func,NULL);
if(!tid)
printf("error\n");
for(i=0;i<1000000;i++)
printf("coca cola crap\n");
return 0;

цифра 1 будет на 402503-й строке. Интересно...

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

> pthread_create(&tid,NULL,&thread_func,NULL);
> if(!tid)
>

Блин... сделай нормальную обработку ошибок!

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

>fork() в этом контексте не сама ф-ция, я имел ввиду процесс ;) что так и есть ;) посему время создания потока и процесса не сильно то и отличаются до 2.6

Видимо, данная ветвь дискуссии тупиковая, но все-таки два замечания

1. Процессы (даже совсем настоящие) в linux создаются довольно быстро.

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

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

void *thread_func(void *ptr) {
  /* именно такой протоип и должен быть ибо
   int  pthread_create(pthread_t  *  thread,
      pthread_attr_t * attr,
      void *(*start_routine)(void *),
      void * arg);
  */
  return NULL; /* или то что надо разбирать при pthread_join() */
}
int main(void) {
  int t;
  pthread_t tid;
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  /* результат работы нити ненужен и join() делать не будем */
  pthread_set_detachstate(&attr,PTHREAD_CREATE_DETACHED);
  if (pthread_create(&tid,&attr,thread_func,NULL) != 0 ) {
     /* обработка ошибки - только по коду завершения функции,
        нельзя полагаться на то,
        что tid - число и его значение что-то значит
     */
  }
  pthread_attr_destroy(&attr); // attr далее не используется
  for(t=0;t<1024;t++) {
    printf("loop , t=%d\n",t); 
    /* вывод будет перемешан с выводом от нитей,
       чтобы избегать этого смотри info libc,
       раздел про IO on streams, про Streams and Threads
    */
    /* перемешивание будет хаотично и зависит от параметров
       планировщика, аттрибутов нитей и загруженности машины
    */
    another_work();  
  }
  ptrhread_cancel(&tid);
  exit(0);
}

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

anonymous (16.11.2005 16:04:40):

> 1. Процессы (даже совсем настоящие) в linux создаются довольно быстро.

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

Во-вторых, главная фенечка, требуемая современными кодерами, прошедшими суровую школу Win API и Жабы, это поддержка MxN, когда не все нити ядерные. А такое невозможно в парадигме "нить = процесс".

> 2. Совершенно несложно представить себе архитектуру приложения, в которой время создания потока некритично. Самый очевидный и часто встречающийся пример - создание при старте процесса некоторого заранее заданного количества потоков, между которыми тем или иным способом будет разделена дальнейшая работа.

Совершенно верно, такая конструкция (называемая пулом) IMHO отличает профессиональные изделия от любительских поделок (впрочем, есть много исключений).

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

> Совершенно верно, такая конструкция (называемая пулом) IMHO отличает
> профессиональные изделия от любительских поделок (впрочем, есть много
> исключений).

Исключений настолько много, что наличие или отсутствие в программе
thread pooling ну никак не может служить мерилом профессионализма.
В частности Solaris Multithreaded Programming Guide отмечает, что
создание нового треда может быть дешевле, чем рестарт уже имеющегося
треда.
Каждой задаче - свой инструмент. Если в задаче не нужен thread pool
то его создание - это пустая трата времени.

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

> главная фенечка, требуемая современными кодерами, прошедшими суровую школу Win API и Жабы, это поддержка MxN, когда не все нити ядерные. А такое невозможно в парадигме "нить = процесс".

Можно пояснить приведенные соображения кодеру, не прошедшему ни одной из этих суровых школ?

Мне казалось, что современная тенденция как раз состоит в том, что каждой нити соответствует некая сущность в ядре, которая чаще называется LWP, чем как-то еще. При переходе от восьмого Соляриса к девятому эта самая MxN (как мне казалось) была объявлена устаревшей и неправильной.

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

anonymous (*) (16.11.2005 18:02:35):

> Мне казалось, что современная тенденция как раз состоит в том, что каждой нити соответствует некая сущность в ядре, которая чаще называется LWP, чем как-то еще. При переходе от восьмого Соляриса к девятому эта самая MxN (как мне казалось) была объявлена устаревшей и неправильной.

Странно...

Мне казалось, что "невытесняющие" нити -- предмет особой гордости сантехников...

А нужна MxN тем, кто сотнями гоняет нити на однопроцессорных машинках (умолчательный подход "жабобыдлокодера").

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

Onanim:

> Каждой задаче - свой инструмент. Если в задаче не нужен thread pool то его создание - это пустая трата времени.

В общем, согласен.

Посто довольно часто постоянный рестарт нитей сопутствует просчетам в модели распараллеливания.

>В частности Solaris Multithreaded Programming Guide отмечает, что создание нового треда может быть дешевле, чем рестарт уже имеющегося треда.

Выдержка из Solaris Multithreaded Programming Guide:

The threads packages cache the threads data structure and stacks so that the repetitive creation of threads can be reasonably inexpensive. However, creating and destroying threads as the threads are required is usually more expensive than managing a pool of threads that wait for independent work.

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

2Die-Hard:

В Solaris8 (и предыдущих) Multithreaded Programming Guide это звучит:

> So, creating and destroying threads as they are required is usually
> better than attempting to manage a pool of threads that wait for
> independent work.

Да, в guide от Solaris9 это звучит так, как ты написал. Хотя следующее
за этим предложение, иллюстрирующее преимущества схемы без thread
pooling (про RPC server) оставлено без изменений :-)))

А о чем это все говорит? Да ни о чем ;-) Уж точно не не о том,
что thread pooling - показатель профессиональной "крутизны".

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

Onanim:

>> So, creating and destroying threads as they are required is usually better than attempting to manage a pool of threads that wait for independent work.

В процитированном тобой абзаце речь, вообще говоря, шла о юзерспейсовских нитках (вернее, о премуществах сантехнических unbound threads):

"Unbound thread creation is very inexpensive when compared to process creation or even to bound thread creation. In fact, the cost is similar to unbound thread synchronization when you include the context switches to stop one thread and start another.

So, creating and destroying threads as they are required is usually better than attempting to manage a pool of threads that wait for independent work."

IMHO MxN вообще mustdie, и уж, разумеется, пул из юзерспейсовских ниток -- верх идиотизма.

Мое мнение: интенсивное порождение/убиение ниток свидетельствует (как правило) о непрофессионализме разработчика (впрочем, с многочисленными исключениями: уж очень шибко MxN была пропиарена; многим мозги заканифолило).

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

> В процитированном тобой абзаце речь, вообще говоря, шла о
> юзерспейсовских нитках (вернее, о премуществах сантехнических
> unbound threads)

Мы с тобой цитируем одтн и тот же абзац, просто в двух разных редакциях.

> Мое мнение: интенсивное порождение/убиение ниток свидетельствует
> (как правило) о непрофессионализме разработчика (впрочем, с
> многочисленными исключениями

Да, именно _интенсивное_ порождение/убиение может свидетельствовать
о неудачном дизайне. Согласен б/п ;-) Но есть же и другие варианты.
Если время жизни треда достаточно долгое (скажем CPU-consuming task)
то вполне вероятно, что экономия на стоимости создания треда - это
"мелочная оптимизация", а thread-pooling нарушает принцип KISS.

> уж очень шибко MxN была пропиарена; многим мозги заканифолило)
Меня честно говоря не сильно волнуют вопросы реализации. Если она
хреновая - ну что же, тем хуже для этой операционки. Что меня
выводит из себя - это несоответствие стандартам. Если уж назвался
pthread, то будь добр соответствуй (это типа камень в LinuxThreads).
Вон Solaris threads - очень достойные threads, со своим API, и не
делают вид, что они POSIX.




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

Onanim(*) (16.11.2005 21:13:49):

Почти со все согласен, сейчас некогда подробно отвечать, может, завтра...

> Мы с тобой цитируем одтн и тот же абзац, просто в двух разных редакциях.

Ну да, я понимаю.

Но я во второй раз процитировал как раз ту редакцию, на которую ссылался ты.

> ...именно _интенсивное_ ...

Да, я это и имел в виду.

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

>Мне казалось, что "невытесняющие" нити -- предмет особой гордости сантехников...

Я довольно долго общался с SUNовским support'ом по поводу программы, которая после смены версии некоторого инструментального средства научилась виснуть. Происходило что-то типа deadlock, когда одна из пользовательских нитей ждала на мютексе некоторого события, которое должно было состояться в другой пользовательской нити, и завершиться разблонированием того самого мютекса. Так вот, если эти две нити были ассоциированы с одним LWP, то состояние оказывалось устойчивым, и тот thread, на который возлагались особые надежды, и который должен был разблокировать mutex, никогда не получал управления. На первый взгляд, такое должно происходить на кажлом шагу, ибо на mutex'е ждет LWP целиком, но библиотека во всех человеческих случаях способна не допустить столь примитивной взаимной блокировки. Описываемый случай оказался не вполне человеческим.

Дело было далеко не вчера, так что я могу что-то путать.

Ответ сантехников сводился к тому, что вылечить ситуацию в рамках модели MxN невозможно, и нужно использовать новую thread library, которая 1:1. Что в конце концов и было проделано. Нитей в программе было что-то около двух сотен и каждая их них слушала свою Message Queue. Потеря производительности, насколько я помню, была пренебрежимо мала.

Кстати, было замечено, что после многих часов интенсивной работы многонитевого приложения unbound threads куда-то испаряются, и почти каждая пользовательская нить получает свой LWP.

Но давайте все-таки вернемся к linux и к моему вопросу о месте модели MxN в его современных реализациях. Она действительно используется в системах на ядре 2.6? Используется по умолчанию, или нужно предпринять специальные усилия? И, самое главное, где про это почитать приминительно к Linux?

anonymous
()

Объясните, вы тут говорите что при нормальной реализации pthreads,
потоки должны жить, даже если главный умрет?

но если процесс закончил работы, не должны ли потоки тоже завершиться(ведь именно это происходит при неявном вызове exit при return из main)?

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

> но если процесс закончил работы, не должны ли потоки тоже
> завершиться(ведь именно это происходит при неявном вызове exit при
> return из main)

При вызове exit() или return из main AFAIK должен завершиться весть
процесс (все треды).
При вызове pthread_exit() из main IMHO должен завершиться только
main thread, остальные продолжают работать. На Solaris так и
происходит. На Linux (NPTL) вроде как после pthread_exit() в main
завершается весь процесс.

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

anonymous (*) (17.11.2005 9:39:20):

> ...ибо на mutex'е ждет LWP целиком, но библиотека во всех человеческих случаях способна не допустить столь примитивной взаимной блокировки. Описываемый случай оказался не вполне человеческим. ... Ответ сантехников сводился к тому, что вылечить ситуацию в рамках модели MxN невозможно, и нужно использовать новую thread library, которая 1:1. Что в конце концов и было проделано.

С трудом верится...

Если семантика unbound threads различается в зависимости от того, принадлежат ли они одному LWP или разным, то это IMHO просто баг.

> Но давайте все-таки вернемся к linux и к моему вопросу о месте модели MxN в его современных реализациях. Она действительно используется в системах на ядре 2.6?

Нет.

После бурных дискуссий было решено, что это лишнее. NPTL реализует 1:1.

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

> При вызове pthread_exit() из main IMHO должен завершиться только main thread, остальные продолжают работать. На Solaris так и происходит. На Linux (NPTL) вроде как после pthread_exit() в main завершается весь процесс.

Имеет (имело?) место быть "оригинальное" толкование POSIX'a (POSIX 9945-1:1996 section 16.2.5.2, lines 249 - 251) разработчиками Linux Threads: "The process shall exit with an exit status of 0 after the last thread has been terminated." Они почему-то решили, что сие означает, что main() должна заснуть на pthread_exit(), пока остальные нити не окончатся.

В NPTL это дело пофиксили, и "официально" pthread_exit() в NPTL соответствует POSIX'у.

НО!

Имели место несколько багов, "благодаря" которым в некоторых версиях libc6/ядра при определенных условиях (а иногда и почти всегда) pthread_exit() в main() убивал всех. Сейчас, вроде, починили.

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

> В NPTL это дело пофиксили, и "официально" pthread_exit() в NPTL
> соответствует POSIX'у.

> Имели место несколько багов, "благодаря" которым в некоторых версиях
> libc6/ядра при определенных условиях (а иногда и почти всегда)
> pthread_exit() в main() убивал всех. Сейчас, вроде, починили.

А, понятно.
Проверил из интереса - на SLES9 все OK, на SUSE9.2 вроде как ашипка.
Ну да ладно, с таким багом жить можно.

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

что-то я не понял. Т.е. если main создает несколько нитей и говорит этим нитям pthread_detach, а сам говорит pthread_exit, то main завершает работу, а нити продолжают работать в фоновом режиме ? своего рода fork с завершением родительского процесса для перехода в фоновый режим?

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

AFAIK нет. А с чего ты так решил? Даже после pthread_exit() в main
процесс продолжает существовать с тем же PID, и для стороннего
наблюдателя ничем особенным не отличаеться не должен.
В частности, если кто-то ждет этот процесс на waitpid() или wait()
ожидание должно продолжиться (процесс-то жив, PID есть).

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

> > Т.е. если main создает несколько нитей и говорит этим нитям
> > pthread_detach, а сам говорит pthread_exit, то main завершает
> > работу, а нити продолжают работать в фоновом режиме ?
>
> AFAIK нет.

ДА! уж поверьте мне :) я ведь уже писал об этом.

> Даже после pthread_exit() в main процесс продолжает существовать
> с тем же PID, и для стороннего наблюдателя ничем особенным не
> отличаеться не должен.

верно

> В частности, если кто-то ждет этот процесс на waitpid() или wait()
> ожидание должно продолжиться (процесс-то жив, PID есть).

опять верно.

тем не менее, при вызове  sys_exit (но не sys_exit_group), основной
поток (thread group leader) прекрасно завершает работу, и переходит
в TASK_ZOMBIE.

и вот из этого состояния его никаким wait'ом не вытащить, пока все
остальные потоки не завершатся, или один из потоков не сделает exec
(что, вообще-то приведет к завершению всех потоков, так что это то
же самое).

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

разумеется, я говорю о NPTL.

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

> > > Т.е. если main создает несколько нитей и говорит этим нитям
> > > pthread_detach, а сам говорит pthread_exit, то main завершает
> > > работу, а нити продолжают работать в фоновом режиме ?
> >
> > AFAIK нет.
>
> ДА! уж поверьте мне :) я ведь уже писал об этом.

Ну и зачем так орать-то? Я честно говоря так и не понял, с чем
ты не согласен. Еще раз, для тех, кто в танке:
Если процесс вызвал pthread_exit() из main, это ничего не значит.
Его родитель как ждал на wait(), так и будет ждать. Уж поверь мне ;-)
Для тех, кто на конкретном бронепоезде, еще раз привожу оригинальный
вопрос:

!> Re: Posix Threads
!> что-то я не понял. Т.е. если main создает несколько нитей и говорит
!> этим нитям pthread_detach, а сам говорит pthread_exit, то main
!> завершает работу, а нити продолжают работать в фоновом режиме ?
!> своего рода fork с завершением родительского процесса для перехода
!> в фоновый режим?
!> anonymous (*) (19.11.2005 20:08:45)

Мое "AFAIK нет" относится к словам "в фоновом режиме". А точнее к
идее этого anonymous'а о том, что pthread_create/pthread_exit из main
это то же самое, что fork/exit из родителя.

Да, очевидно "чукча не читатель" (C)

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

> и вот из этого состояния его никаким wait'ом не вытащить, пока все
> остальные потоки не завершатся, или один из потоков не сделает exec
> (что, вообще-то приведет к завершению всех потоков, так что это то
> же самое).
>

Ты уверен? Точно exec с exit не путаешь? :-)))

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

> ДА! уж поверьте мне :)
>
> Ну и зачем так орать-то?

да все боялся, что вы не услышите ...

> Мое "AFAIK нет" относится к словам "в фоновом режиме".
> А точнее к идее этого anonymous'а о том, что pthread_create/pthread_exit
> это то же самое, что fork/exit из родителя.

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

> Если процесс вызвал pthread_exit() из main, это ничего не значит.
> Его родитель как ждал на wait() так и будет ждать.

кажется, я с этим и не спорил, a наоборот, подтвердил ?

> Еще раз, для тех, кто в танке:
> Для тех, кто на конкретном бронепоезде

я так и знал что я от туда! (глухие рыдания). но наконец-то и
к нам пришло просветлениe.


> Да, очевидно "чукча не читатель" (C)

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

модераторы, удаляйте смело, спорить не буду :)

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