Intereting Posts
Можно ли использовать сокет, если его файл доступен только для чтения? Ошибка Makefile: имя пустой переменной Перенаправление stdout и stderr определенных строк Как выбрать специфические файлы в grep Нужна помощь в скрипте bash для отправки электронной почты в одной командной строке mv несколько файлов одного и того же каталога без указания одного и того же пути или каждого ошибка в порядке egrep Как передать значение, которое должно быть в кавычках для команды? Порядок открытия документов в Наутилусе ловушка для отладочного сигнала вызывалась дважды, прежде чем функция оболочки будет выполняться, когда функция «functrace» включена Переменная, назначенная внутри команды ssh, не возвращает правильное значение Не удается войти в OpenSuse: «Нет серверов входа в систему» uuencode висит? Каков самый короткий способ выбрать первый доступный порт в определенном диапазоне с помощью Bash? Какой основной пакет программы?

Что означает стек в связи с процессом?

Из книги « Расширенное программирование в среде Unix» я прочитал следующую строку, касающуюся потоков в Unix-подобных системах

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

Что автор имеет в виду здесь? Я программирую Java и знаю, что каждый поток получает свой собственный стек. Поэтому я смущен концепцией общих stacks .

В контексте процесса Unix или linux фраза «стек» может означать две вещи.

Во-первых, «стек» может означать последние, вначале записи вызывающей последовательности потока управления. Когда процесс выполняется, main() сначала вызывается. main() может вызывать printf() . Код, сгенерированный компилятором, записывает адрес строки формата и любые другие аргументы printf() в некоторые ячейки памяти. Затем код записывает адрес, по которому поток управления должен возвращаться после завершения printf() . Затем код вызывает переход или ветвь в начало printf() . Каждый поток имеет один из этих стеков записей активации функции. Обратите внимание, что многие процессоры имеют аппаратные инструкции для настройки и поддержки стека, но другие процессоры (IBM 360 или что-то другое) фактически используют связанные списки, которые могут быть разбросаны по адресному пространству.

Во-вторых, «стек» может означать ячейки памяти, к которым ЦП пишет аргументы функций, и адрес, на который должна возвращаться вызываемая функция. «Стек» относится к непрерывной части адресного пространства процесса.

Память в процессе Unix или Linux или * BSD – очень длинная строка, начинающаяся примерно с 0x400000 и заканчивающаяся примерно на 0x7fffffffffff (на процессорах x86_64). Адресное пространство стека начинается с наибольшего числового адреса. Каждый раз, когда вызывается функция, стек функций активирует записи «растет»: код процесса помещает аргументы функции и обратный адрес в стек активационных записей и уменьшает указатель стека, специальный регистр ЦП, используемый для отслеживания где в адресном пространстве стека находятся текущие значения переменных процесса.

Каждый поток получает часть «стека» (адресное пространство стека) для собственного использования в качестве стека записей активации функции. Где-то между 0x7fffffffffff и нижним адресом каждый поток имеет область памяти, зарезервированную для использования в вызовах функций. Обычно это выполняется только по соглашению, а не по оборудованию, поэтому, если ваш поток вызывает функцию после вложенной функции, нижняя часть стека этого потока может перезаписать верхнюю часть стека другого потока.

Таким образом, каждый поток имеет кусочек области памяти «стека», и из этого вытекает терминология «общего стека». Это связано с тем, что адресное пространство процесса представляет собой единый линейный блок памяти и два использования термина «стек». Я почти уверен, что некоторые старые JVM (действительно древние) на самом деле имели только один поток. Любые потоки внутри кода Java действительно выполнялись одним реальным потоком. Новые JVM, JVM, которые ссылаются на реальные потоки для выполнения Java-потоков, будут иметь тот же «общий стек», о котором я рассказывал выше. Linux и Plan 9 имеют системный вызов процесса (clone () для Linux, rfork () в Plan 9), который может настраивать процессы, которые разделяют части адресного пространства и, возможно, разные пространственные адресные пространства, но этот стиль потоковой передачи никогда на самом деле не поймал.

Что автор имеет в виду здесь? Я программирую Java и знаю, что каждый поток получает свой собственный стек. Поэтому я смущен концепцией общих стеков.

Использование множественного числа немного странно и, похоже, вводит в заблуждение, возможно, дело в том, что множественные стеки многопоточной программы используют одно и то же адресное пространство.

Как описывает Брюс Эдигер, «стек» относится к одной области смежного адресного пространства, в которой данные помещаются LIFO-мудрый. Тем не менее, каждый собственный поток в процессе имеет такую ​​смежную область – между ними нет ни одного разделенного. Для создания потока требуется выделение дополнительного стека того же размера (это фиксированное число, например, по умолчанию для linux – 8 МБ), поэтому чрезмерно многопоточные приложения могут потреблять чрезмерные объемы памяти.

Java использует собственные потоки для реализации потоков Java, но управляет «стеком» для каждого из потоков java, независимо от ядра. Это означает, что для этой цели необходимо использовать некоторую глобальную память; см. третью часть ответа сверху:

https://stackoverflow.com/questions/5483047/why-is-creating-a-thread-said-to-be-expensive

Конечно, это относится к конкретной реализации (openjdk), но, предположительно, все они должны это сделать (выделяйте кучу изначально для внутреннего использования как «стека потоков»).

Поскольку отдельный стек, используемый каждым собственным потоком, управляется ядром, я не согласен с импликацией Брюса, что это часть какого-то общего стека, принадлежащего процессу в целом, – опять же, единственный (иначе говоря, не-поток) процесс имеет только один стек, но основной поток в многопоточном процессе не разделяет пространство стека с другими потоками.

«Общий стек» – это тот, в котором хранятся все данные, начиная с одного адреса – это тот смысл, в котором вложенные вызовы функций используют один и тот же стек. Однако, используя измененную версию примерной программы, упомянутой ниже Брюсом, из версии linux страницы man man страницы pthread_create :

 ./a.out one two three In main() stack starts near: 0x7fff17b80f98 Thread 1: top of stack near 0x7f11ac6d3e78; argv_string=one Thread 2: top of stack near 0x7f11abed2e78; argv_string=two Thread 3: top of stack near 0x7f11ab6d1e78; argv_string=three 

Эта программа берет адрес локальной переменной в поточной функции; добавление, которое я добавил, заключалось в том, чтобы делать то же самое в main() до создания потоков. Запуск выполняется в новейшей 64-битной Linux-системе; обратите внимание, что начальные адреса для потоков ТОЧНО 8 МБ друг от друга, и они очень далеки от вершины основного потока потоков. Контрастируйте это с помощью вложенных вызовов функций, например:

 #include <stdio.h> void eg (int n) { char *p; printf("#%d first variable at %p\n", n, &p); if (n < 3) eg(n+1); } int main(int argc, const char *argv[]) { char *p; printf("main() first variable at %p\n", &p); eg(1); return 0; } , #include <stdio.h> void eg (int n) { char *p; printf("#%d first variable at %p\n", n, &p); if (n < 3) eg(n+1); } int main(int argc, const char *argv[]) { char *p; printf("main() first variable at %p\n", &p); eg(1); return 0; } 

Пример выполнения:

 main() first variable at 0x7fffef0aaf68 #1 first variable at 0x7fffef0aaf38 #2 first variable at 0x7fffef0aaf08 #3 first variable at 0x7fffef0aaed8 

Это всего лишь 48 байт друг от друга – другими словами, они помещаются один за другим параллельно (с другими маленькими битами фактических данных) без неиспользуемого пространства между ними. Если мы используем это в многопоточной программе, без вложенной рекурсии, но вызываем eg() один раз в каждом потоке:

 Thread 1: top of stack near 0x7f4bd5061e78; argv_string=one Thread 2: top of stack near 0x7f4bd4860e78; argv_string=two Thread 3: top of stack near 0x7f4bd405fe78; argv_string=three #3 first variable at 0x7f4bd405fe48 #1 first variable at 0x7f4bd5061e48 #2 first variable at 0x7f4bd4860e48 

Переменная для каждого находится рядом с верхней частью трех отдельных стеков.

Все это в области высокого адреса, называемой «стеком пространства», но в многопоточной программе, которая разделена на несколько стеков ; он не рассматривается как одна большая структура LIFO.