Программирование: введение в профессию. Издание 2,91 (электронное)
АннотацияУчебник «Программирование: введение в профессию» ориентирован на самостоятельное изучение и предполагает использование систем семейства Unix (в т.ч. Linux) в роли сквозной среды для обучения. Скачать книгу
Дополнительные файлыАрхив, содержащий примеры программ, можно скачать здесь: progintro_2_91_examples.tgz. Напоминаем, что раскрыть этот архив можно командой tar -xzf progintro_2_91_examples.tgz Файл mv stud_io_inc stud_io.inc ErrataТом III
![]() |
Программирование: введение в профессиюпояснениеВы находитесь на официальном сайте Андрея Викторовича Столярова, автора учебных пособий по программированию и информационным технологиям. Если вы искали сайт замечательного писателя-фантаста Андрея Михайловича Столярова, то вам, к сожалению, не сюда. Андрей Михайлович Столяров в библиотеке Мошкова |
☞ From EVI
Thu Oct 2 13:22:56 2025
UTC
Возможная ошибка, т.3, стр. 174
В самом верху страницы приведён пример "эстафеты":
Ну-с, или я чего-то совсем не понимаю...
ответить
Re: Возможная ошибка, т.3, стр. 174
> Ну-с, или я чего-то совсем не понимаю...
Уж не знаю, совсем или не совсем, но никакой ошибки тут нет
ответить
Re: Re: Возможная ошибка, т.3, стр. 174
> Уж не знаю, совсем или не совсем
Например, с алгоритмом Петерсона всё понятно.
> никакой ошибки тут нет
Извините, потрачу ещё вашего времени. Пример целиком.
Есть два параллельных процесса. Назовём их левый процесс и правый процесс. Изначально, предположим,
turn = 0
, таким образом. Если процесс доходит до циклаwhile
(далее этот цикл буду называть спинлоком), левый заблокируется в спинлоке, а правый пойдёт дальше. После того как правый выполнит подпрограммуsection()
,turn = 0
, т.е. у переменнойturn
останется то же значение, что и было изначально и левый процесс продолжит висеть в спинлоке. А правый процесс просто повторно выполнится послеnoncritical_job()
.Если изначально
turn = 1
, тогда правый процесс будет вечно висеть в спинлоке, а левый бесконечно выполняться не блокируясь.ответить
Re: Re: Re: Возможная ошибка, т.3, стр. 174
> т.е. у переменной turn останется то же значение
Ни разу. Если мы дошли до этого присваивания, то это само по себе значит, что turn имеет значение 1. Иначе мы бы сюда не попали. И левый процесс после этого присваивания из своего спинлока вывалится, а вовсе не "продолжит в нём висеть".
В дальнейшем подобное не раскрываю.
ответить
Re: Re: Re: Re: Возможная ошибка, т.3, стр. 174
Всё, понял где случился мой затуп.
> Изначально, предположим, turn = 0 ... левый заблокируется в спинлоке, а правый пойдёт дальше.
На самом деле заблокируется правый процесс, т.к. while не if (спасибо, кэп). Блин, стыдоба какая, в мозгу что-то перемкнуло.
Большая благодарность! И ещё раз извиняюсь, что отвлёк.
ответить
☞ From Ivan (unverified) Mon Sep 29 00:11:02 2025 UTC
pause в примере на странице 127(II том)
На странице 127 в примере pressagain.c вы предлагаете заменить sleep(1) на pause(), чтобы программа не имела паразитной нагрузки и была еще более правильной. Но ведь в данном случае может возникнуть ситуация, когда после проверки условия в цикле программа теряет квант времени, и обработчик вызывается до pause(). То есть сообщение может вывестись 26 раз.
Я предполагаю, что конкретно в этом примере я что-то не так понял, потому что ниже вы в другом примере показываете, что pause() не всегда можно использовать.
ответить
Re: pause в примере на странице 127(II том)
> программа теряет квант времени
Это как, простите? Ну то есть я примерно понимаю, чтО вы имели в виду, но не в таких же выражениях 8-()
> обработчик вызывается до pause()
Вы бы как-то чётче формулировали проблему. Она тут действительно есть, но уж точно не потому, что "до pause". Там, если уж на то пошло, все вызовы обработчика, кроме самого последнего, происходят до очередного обращения к pause.
Подумаю, что тут можно сделать с текстом. "В лоб" тут поправить не получится, поскольку для совсем правильного варианта надо использовать sigsuspend, но его совершенно невозможно объяснить тем, кто сигналы видит впервые, и на кого рассчитан этот пример. А так, пардон, формально и утверждение, что можно было бы использовать sleep(3600), да и сам этот sleep(1) — "не совсем правильно". Можно было бы сделать usleep, например, на десятую долю секунды, уповая на то, что пользователь всё равно не заметит, но объяснения, почему так, наглядности примера очевидно навредят. А оставлять активное ожидание — это уже вообще беспредел.
Вообще-то, если совсем честно, в этом примере проблема проявиться не может даже теоретически, если только не допустить, что квант времени может быть равен нескольким считанным тактам — а такое допускать мешает тот простой факт, что переключение между процессами расходует несколько сотен тактов по меньшей мере, и даже просто вызов планировщика на тему "не пора ли тут что-то менять" тоже довольно жирный, так что кванты должны быть уж точно побольше. А если они побольше, с учётом того, что после возврата из блокирующего сисколла квант новый начинается, получается так, что кванта ну никак не может "не хватить" на то, чтобы сделать проверку условия и нырнуть обратно в pause, поскольку там этих тактов десятка полтора от силы. Т.е. вот это вот "слишком быстро обработчик сработал" может получиться только на входе в цикл (поскольку там перед входом есть что-то ещё), но это совершенно пофигу — чтобы проблема проявилась видимым образом, надо, чтобы ЭТО стряслось перед ПОСЛЕДНИМ вызовом pause. А оно там стрястись не может ну то есть вот вообще никак.
ответить
Re: Re: pause в примере на странице 127(II том)
> программа теряет квант времени Под теряет я имел ввиду что истечет квант времени.
И еще у вас в Errat'е указана 218 страница хотя я писал что это 212
ответить
Re: Re: Re: pause в примере на странице 127(II том)
Ну эррату-то я сейчас поправлю, но у меня встречный вопрос: вы понимаете, что такое пустая строка? В ваших комментах ваш собственный текст сливается с цитируемым, мне приходится за вас эти пустые строчки вставлять, и уже надоело. Конкретно этот ваш коммент оставляю как есть, чтобы было понятнее.
UPD: Не буду править эррату, это 218 страница, всё правильно.
ответить
Re: Re: pause в примере на странице 127(II том)
> проблема проявиться не может даже теоретически
У меня получается вывести 26 сообщений вместо 25 если я отправляю сигнал из другого окна терминала с помощью
kill -SIGINT ; kill -SIGINT ; kill -SIGINT ; ... и так далее очень длинная строка.
Можно в условие цикла поставить (n < 1) так легче воспроизвести проблему. Только в этом случае квант времени истекает не после проверки условия цикла, а уже после вызова обработчика я так понимаю. Или же в самом обработчике
ответить
Re: Re: Re: pause в примере на странице 127(II том)
Вообще это несколько странно, надо разбираться, как так выходит.
> после вызова обработчика я так понимаю.
На возврате из сисколла обработчик отрабатывает первым, так что это совсем странно. Мало того, там ещё и sigreturn вызывается, который сам по себе вполне себе сисколл.
Ну хотя в моём примере обработчик откровенно перегружен, он же ещё и write делает. Ей-богу, мне уже интересно, что там происходит на самом деле.
UPD: вообще нет там ничего странного, если подумать. Там на возврате из pause начинает (но не так чтобы заканчивает) работу обработчик, чтобы обработать тот самый сигнал, который стал причиной возврата из pause. Дальше в зависимости от семантики signal возможно два случая: если сигнал SIGINT на время работы обработчика заблокирован, и если он, соответственно, НЕ заблокирован. Cигналами нас долбят (ну, во всяком случае могут долбить) с частотой достатчной для того, чтобы флажок, требующий обработки SIGINT, был взведён "всегда" (уж в те моменты, когда процесс переходит из режима ядра в режим пользовательского исполнения — точно; ну то есть на одноядерной системе это было бы не так, но где сейчас одноядерную систему найдёшь). Это значит, что очередной обработчик SIGINT начнёт свою работу то ли после одного из системных вызовов внутри предыдущей ипостаси обработчика, то ли сразу после его завершения — на выходе из sigreturn. Эдак мы сообщений напечатать можем вообще сколько угодно, а до проверки условия в главной программе дело так и не дойдёт. Пойду-ка я кое-что проверю :-)
UPD2: Ага, так оно и есть. Слепил вспомогательную программу, которая запускает кого сказано, а из родительского процесса, выждав десятую долю секунды для приличия (чтобы дать запущенному процессу успеть exec'нуться и установить свою диспозицию для сигнала), после этого "жарит" его SIGINT'ом, пока тот не спечётся (проверяется waitpit'ом с WNOHANG). Имеем вот такое:
Напечатанное число — это сколько раз мы получаем сообщение от pressagain. Открою секрет, я там 25 заменил на 3 :-)
ответить
Re: Re: Re: Re: pause в примере на странице 127(II том)
Вау
Теперь понял, спасибо
ответить
☞ From olyx (unverified) Tue Sep 23 13:15:42 2025 UTC
Ошибка во 2 томе
Цитата из пункта 6.3.2. Сокет и его сетевой адрес "Функция возвращает ненулевое значение, если заданная строка является допустимой текстовой записью ip-адреса, и 0 в противном случае." Разве не наоборот, функция возвращает 0, если адрес допустимый и не 0 в противном случае. Далее идет обработка ошибок если !ok.
ответить
Re: Ошибка во 2 томе
В мозгу у тебя, блин, ошибка, а не во втором томе. if(!ok) срабатывает, если ok имеет значение 0 (и никакое другое). Т.е. тут ok — обыкновенная логическая ("булевская") переменная, традиционная для Си. Я бы её так не назвал, если бы она хранила что-то иное, нежели логическое значение, соответствующее успешному выполнению вызванной функции. Так что нет, не разве.
Прежде чем подобную хрень на сайт тащить, стоит, наверное, подумать, не?
ответить
☞ From Ivan (unverified) Tue Sep 23 08:49:14 2025 UTC
Задание переменной окружения прямо в команде
3 том. Стр 212(вроде где то еще), указано что можно задать переменную окружения конкретно для одной команды CVSROOT=/home/vasya/cvsroot echo $CVSROOT
У меня такой способ не работает конкретно для команды echo(выводится пустая строка). Правильно ли я понимаю что так работает потому что в моей системе echo is a shell builtin?
ответить
Re: Задание переменной окружения прямо в команде
1) Нет, builtin тут ни при чём. Просто подстановки переменных происходят во время анализа команды, а установка временного значения переменной -- уже после завершения анализа. Т.е. когда в тексте набранной вами команды происходит замена $CVSROOT на значение переменной CVSROOT, к этому моменту установка значения ещё не отработала. Если сама команда обратится к своему окружению, она там это временное значение переменной увидит, но echo к своему окружению, естественно, не обращается.
Вот вам иллюстрация на эту тему:
NB: внутри апострофов подстановка переменных не производится, так что eval так и видит без изменения то, что в апострофах, и уже сам это "вычисляет", подставляя значение переменной FOO уже из своего окружения.
2) С техническими консультациями не сюда, а тех, кто учится, я консультирую только в рамках менторинга.
ответить
Re: Re: Задание переменной окружения прямо в команде
Тоесть строка из книги
CVSROOT=/home/vasya/cvsroot echo $CVSROOT
и не должна выводить на экран значение переменнойCVSROOT
?ответить
Re: Re: Re: Задание переменной окружения прямо в команде
Да, это ошибка. Спасибо за её выявление.
ответить
☞ From EVI
Fri Sep 12 15:17:58 2025
UTC
Пример tcp_questn.c
Здравствуйте, Андрей Викторович!
Дошёл в своём обучении до написания сервера, решил задачу 6.12. Возник вопрос (пошлёте в менторинг -- пойму, обновлённые правила читал).
Если взять за основу ваш пример tcp_questn.c (за свой в силу неопытности не могу поручиться), насколько он годиться для использования в глобальной сети? Т.е. я открою порт, чтобы кто угодно мог подключиться к моему компьютеру и использовать эту программу.
Пока сам вижу только две угрозы:
1) Не может быть открыто более 1024 файловых дескрипторов, accept возвращает "Too many open files" (только что это проделал со своей программой получилось 1020 клиентов). Т.е. кто-то может заблокировать доступ к серверу. Наверное, надо делать какой-то дисконнект по таймауту и отслеживать, чтобы с одного ip-адреса можно было логиниться с некоторой задержкой, условно в 3 секунды.
2) Поскольку логи записываются в файл, то можно нагенерировать логов пока не кончится место на диске (хоть с тем же вызовом accept).
ответить
Re: Пример tcp_questn.c
> годиться
тся/ться, ну сколько можно?
По основному вопросу: с одной стороны, всерьёз я аудит исходников на безопасность провести не могу, для этого есть другие люди, и я слишком хорошо знаю, насколько в этом плане их квалификация отличается от моей. С другой стороны, если "взять за основу" даже самый идеальный с точки зрения безопасности код, выверенный и перелопаченный десятком самых продвинутых хакеров мира, вы же не его как таковой использовать будете — скорее всего, допишете какую-то свою функциональность, что-то где-то измените, и этого запросто может хватить для возникновения дыры.
Есть, впрочем, и хорошая новость. Вот то, что вы тут пишете про исчерпание дескрипторов и забивание дисков логами — против этого ваши меры вроде трёхсекундного таймаута не помогут, и вообще ничего не поможет. От DoS'ов вообще защищаться проблематично. Вот только это не так чтобы было сильно опасно: никто при этом не получит доступа к вашим данными, никто при этом не сможет запускать программы на вашей машине. Ну то есть бяка как бяка, но не так чтоб серьёзная.
На мой взгляд, основная мера защиты тут очень простая: демон, слушающий внешние порты, должен работать под своим собственным псевдопользователем. Если ему нужен привилегированный порт — сбиндить сокет на этот порт, после чего полномочия сбросить. Между прочим, практически всё серверное ПО именно так и делает. Чтобы если таки проломят — дальше этого псевдопользователя в систему не пролезли.
Ну и ещё один момент: тот компьютер, который вы используете для своей работы (рабочую станцию) не надо использовать в роли сервера. А на сервере не должно быть ничего такого, что вам сильно дорого.
ответить
Re: Re: Пример tcp_questn.c
> тся/ться, ну сколько можно?
Правила знаю, рука дрогнула, извините.
> Есть, впрочем, и хорошая новость.
> против этого ваши меры ... не помогут
> и вообще ничего не поможет.
Да, хорошая новость.
> должен работать под своим собственным псевдопользователем.
Понял, принял, благодарю!
> для своей работы (рабочую станцию) не надо использовать в роли сервера.
Здесь у меня всё красиво. Когда я только начинал читать ваш трёхтомник, обзавёлся небольшим дешёвым б/у нетбуком, с помощью которого ознакомился с Linux. Потом научился с ним работать по ssh. А потом он плавно превратился в домашний сервер. Пока не подводил.
ответить
☞ From Ivan (unverified) Thu Sep 11 12:05:25 2025 UTC
возращаемое значение brk
На странице 112 указано что
brk(0)
возвращает текущий адресс конца сегмента данных, ну или его + random brk offset, в man к brk, заголовок этой функции выглядит вообще какint brk(void *addr);
. Тоесть он же вообще не возвращает адресс, вместо этого он возращает успешность выполнения. А под ваше описание brk частично подходит функция sbrk(которая не является системным вызовом)ответить
Re: возращаемое значение brk
Цитирую ту же man-страницу, только ближе к концу:
Мне даже интересно, как авторы долбаного SUSv2 предполагали пользоваться тем, что придумали.
ответить
Re: Re: возращаемое значение brk
Интересно, много ли ещё подобных обёрток? Если я почему-либо решу писать на ассемблере, может ли мне встретиться такой системный вызов, обёртка которого не только отличается, но и в man-странице нет информации о различии между обёрткой и самим вызовом? Где в таком случае можно прочитать о настоящем возвращаемом значении и параметрах системного вызова?
ответить
Re: Re: Re: возращаемое значение brk
> Интересно, много ли ещё подобных обёрток?
До чёрта.
> Где в таком случае можно прочитать
В документации по ядру.
ответить
☞ From technocrat (unverified) Sun Aug 24 08:15:30 2025 UTC
Авторские права
Андрей Викторович, здравствуйте!
Будет ли, что-то или всё, из перечисленного:
- нарушением лицензии книги "Программирование: введение в профессию",
- ущемлением Ваших прав и интересов,
- оскорблением Ваших идей и убеждений
размещение личного конспекта выше упомянутой книги в публичном репозитории Github,
исключительно в целях личного использования и в качестве резервной копии?
Лицензию я читал, но я не уверен в том, что всё понимаю так, как Вы задумывали.
Спасибо!
С уважением!
ответить
Re: Авторские права
> - нарушением лицензии книги "Программирование: введение в профессию"
Нет, она относится только к самой книге, точнее даже к конкретным PDF-файлам. Всё остальное, что защищено авторским правом на мои книги, этой лицензией не покрывается и, как следствие, просто запрещено — поскольку я никому ни на что никаких разрешений не давал, кроме всё той же лицензии.
> - ущемлением Ваших прав и интересов,
Зависит от того, что такое "личный конспект". Если там не будет прямых заимствований из книги, т.е. фрагментов её текста (в том числе "изложенных своими словами", т.е. просто перевранных), то это вообще не имеет никакого отношения ко мне. Если таковые там будут (а они, скорее всего, будут) — это будет прямым нарушением авторских прав. И, кстати, личных неимущественных прав тоже.
Вопрос ещё в границах допустимости, т.е. где кончается обоснованное цитирование и начинается заимствование. Точно можно сказать, что если кусок текста из книги не оформлен как цитата, т.е. не обозначаны в явном виде начало цитаты, конец цитаты и/или нет ссылки на источник цитаты — то это уже просто тупо плагиат, за такое вообще следует убивать в изощрённой форме (к копирайту, заметим, это не имеет никакого отношения, это злостное нарушение "права авторства", а если отбросить юридические крючки — то злонамеренный обман публики). А вот если цитата полностью правильно оформлена в качестве таковой, то возникает вопрос, каков её предельный объём, "оправданный целью цитирования" (это, если что, прямо-таки формулировка из ГК РФ, номер статьи не помню — но правила fair use более-менее одинаковы по всему миру). Могу, впрочем, сказать совершенно точно, что если общий объём "цитат" там будет сколько-нибудь сравним с объёмом вашего собственного текста или тем паче будет его превышать — то ни о каком fair use речи уже не пойдёт, это будет производная работа, а таковые для моих книг категорически запрещены.
Так что скорее всего это будет прямым нарушением авторских прав, вытекающим из законодательства, а вовсе не из текста моей лицензии (которая тут, ещё раз повторю, вообще ни при чём). Почему приходится использовать вот это вот "скорее всего" — только потому, что понятие "личный конспект" нигде никаким образом не определено, и что лично вы под таковым понимаете, я заранее знать не могу.
> - оскорблением Ваших идей и убеждений
А вот это уже совершенно точно да, и вне всяких сомнений. Но к лицензии это, опять же, никакого отношения не имеет.
Вообще, честно говоря, мне не вполне понятно, как в принципе можно использовать гитхаб — ну то есть как можно быть настолько тупой мразью, чтобы его использовать. А ещё мне крайне неприятно, что из моих книг даже такая тупая мразь ухитряется для себя извлекать какую-то пользу (увы, это неизбежно; мои книжки даже всякая вебанутая плесень почитывает).
Поделать с этим я ничего не могу, но уж раз спросили — получите.
Хотя, конечно, как может быть размещение на гитхабе предназначено "в целях личного использования и в качестве резервной копии" — это уже вообще покрыто мраком. Вы хоть сами-то поняли, что за бред написали?
ответить
Re: Re: Авторские права
Андрей Викторович, снова здравствуйте!
Спасибо за Ваш ответ и за некоторое юридическое просвещение.
Вы личность неординарная, с общей биомассой планеты у Вас стремящееся к
нулю количество общего, я прекрасно это понимаю и уважаю.
Для системы ценностей большинства людей я значительно хуже, чем Вы написали,
хотя мне и казалось то, что свои недостатки я тоже знаю.
Спасибо за оплеуху, буду стараться быть лучше, а помогать мне в этом
будут Ваши бесценные книги, пользы из которых возможно извлечь в количестве
счётной бесконечности, что я и продолжу с удовольствием делать.
"Личный конспект", да, это запись "переваренного" для
быстрого восстановления изученного в памяти, в случае долгого перерыва в
изучении и для эффективного переключения "контекста" изучения.
На каких либо агрегаторах Git-копию "личного конспекта"
резервировать не стану, а буду просто шифровать, архивировать tar'ом и
хранить архивы на раздельных накопителях информации.
ответить
☞ From Anton (unverified) Sun Aug 24 05:14:12 2025 UTC
Xterm
Извините за глупый вопрос. Как настроить xterm так, чтобы он не отображал цвета? Только серые символы на чёрном фоне.
ответить
Re: Xterm
Это не настройки xterm, это вопрос поведения запускаемых программ.
в большинстве случаев гиганта мысли вполне спасёт. Ну или
— так будут все возможности xterm (которые намного шире, чем возможности vt100), не будет только цвета.
ответить
☞ From Александр (unverified) Tue Aug 19 14:10:33 2025 UTC
Использование регистра EAX
На странице 186 первого тома приводятся два примера замены команды loop: с использованием регистра EAX и с использованием регистра ECX. Пример с регистром ECX я понял, а вот пример с регистром EAX вызывает вопросы. Насколько я понимаю, для замены инструкции loop нужно использовать две команды: декремент счетчика и условный переход. Но если в качестве счетчика использовать регистр EAX, не исказится ли сумма, которая должна накапливаться в этом же регистре?
ответить
Re: Использование регистра EAX
Там никоим образом не имеется в виду, что те две команды, которые используют EAX, следует вставить в пример абзацем выше прямо вместо команды loop. Напротив, сказано, что вот точно так же мы можем поступить с ECX, и это есть вполне себе замена для loop.
ответить
☞ From Дмитрий (unverified) Sat Aug 16 08:14:46 2025 UTC
Сервер не отдает книги
Сервер не отдает книги, или проблема с моей стороны?
ответить
Re: Сервер не отдает книги
Буквально только что:
Так что сервер, во всяком случае, всё отдаёт.
ответить
Re: Re: Сервер не отдает книги
Присоединяюсь, не отдаёт. РФ, МТС.
ответить
Re: Re: Re: Сервер не отдает книги
Мне вот что интересно, я явным образом указал автору исходного коммента, что проверил этот момент и всё работает. Чего вы ещё от меня хотите? Или вам чисто воздух посотрясать?
На всякий случай только что проверил с машины, расположенной в России. Всё нормально отдаётся. Дальнейшие вопросы адресуйте вашему провайдеру, вашему браузеру и вашему устройству. Я тут вам помочь ничем не могу.
ответить
Re: Re: Re: Сервер не отдает книги
В России и у других мобильных операторов почему-то данный сайт перестал нормально прогружаться; не только книги скачать не удается, но и по страницам нормально полазить.
Раньше вроде где-то в гостевой про похожие же проблемы писали.
ответить
Re: Сервер не отдает книги
Насколько мне издали видно, в России больше не следует рассчитывать на мобильный интернет. Вот то есть вообще. Нету больше этого вида услуги связи.
Мне, впрочем, издали может быть плохо видно.
ответить
☞ From EVI
Fri Aug 1 07:10:23 2025
UTC
Благодарность!
Спасибо большое! Читаю в основном 2-й и 3-й тома, но иногда к 1-му возвращаюсь. К кликабельной навигации быстро привыкаешь.
А можно вас попросить добавить в будущей, если есть такая возможность, кликабельные сноски для возврата? Страницы очень длинные, кликаю на сноску в начале страницы. Потом надо обратно возвращаться.
ответить
Re: Благодарность!
> кликабельные сноски для возврата
Технически такая возможность есть, но при этом, увы, ломаются все ссылки на текст, которые помечены метками иначе как сразу после заголовка главы/параграфа. А у меня таких — овердофига, избавиться от них нереально; надо было с самого начала таких не делать, но кто ж знал-то.
Но вообще PDFные читалки обычно позволяют возвращаться на шаг назад, в том же atril надо в настройках добавить в тулбар кнопку "Back".
ответить
Re: Re: Благодарность!
> в том же atril надо в настройках добавить в тулбар кнопку "Back"
Нашёл, добавил, почему-то конкретно со сносками "Back" не работает. А вот с содержанием всё ок. :^) А у вас нормально работает?
ответить
Re: Re: Re: Благодарность!
Гы, проверил — и вправду не работает. Видимо, там это "назад" работает по номеру страницы. Программисты писали, чо.
ответить