Все запутанно) Но главное, чтобы все понимали о чем идет речь. Например, многие говорят "функционал" вместо "функциональность" (приложения), но все равно всем понятно о чем идет речь из контекста.
Нет, я говорю именно про ситуацию, когда читаем 0. Давайте посмотрим еще раз на пример:
private int x;
private int y;
public void T1() {
x = 1; // W1
int r1 = y; // R1
}
public void T2() {
y = 1; // W2
int r2 = x; // R2
}
Если мы прочитали на R1 любое значение (хоть 0, хоть 1, но нас интересует именно 0), то запись W1 точно произошла, а значит на чтении R2 мы должны будем увидеть 1. Аналогично рассуждаем и о втором треде. Таким образом, мы можем увидеть 0 только на одном из чтений, но никак не на обоих, так как это просто невозможно если смотреть с точки зрения порядка в программе, ведь хотя бы одна запись должна была произойти. Другими словами, выполнение программы начинается или с записи x, или с записи y. Если мы прочитали (0, 0), то это означало бы, что выполнение программы началось с чтений, но ведь они идут после записей? Однако это происходит по причине memory reordering, так как Java не дает sequential consistency по умолчанию.
Не LoadStore, а StoreLoad.
Нет, это называется именно LoadStore (читать так: loads can be reordered after stores).
Не StoreLoad, а LoadStore.
И здесь тоже именно StoreLoad (читать так: stores can be reordered after loads). Кстати, причина наличия такого переупорядочивания в x86 - это Store Buffer.
Да, это про implementation и consistency. Лично я придерживаюсь мнения, что говорить "имплементация" - наоборот корректнее, чем "реализация". Та же вики под Реализацией имеет в виду что-то другому, чем Имплементация. Не знаю, насколько валидно говорить "консистентность" (наверное правильнее "согласованность"?), но это понятие используется повсеместно и если всем все понятно, то проблемы нет :)
Насчет перевода в Exclusive состояние еще уточню, но кажется вы правы, спасибо!
Но ведь данные чтения никак не аффектят друг друга, а поэтому их можно переупорядочить :) Перечитайте раздел [Happens-Before] Same thread actions - там написано о том, как переупорядочивание действий в треде не нарушает happens-before. Только в том случае, если связать с помощью happens-before действия в разных тредах, happens-before будет гарантировать, что инструкция чтения R2 произойдет только после R1 (запретит LoadLoad memory reordering), а инструкция записи W1 произойдет до W2 (запретит StoreStore memory reordering), иначе happens-before в цепочке действий (W1,W2,R1,R2) было бы нарушено. Другими словами, пока мы не свяжем W2 и R1 с помощью happens-before (что дает нам volatile), то не будет happens-before между наборами действий (W1,W2) и (R1,R2).
Но здесь есть один тонкий момент, если мы говорим о графах с циклами. Насколько помню, в DFS мы помечаем вершину как пройденную после посещения очередной вершины, а в BFS - перед посещением (добавлением в очередь).
Писал статью сам. В статье хотел поделиться опытом работы и показать внутреннюю кухню, что было бы полезно тем кто еще на старте карьеры и рассматривает в качестве работы крупные компании или хотел бы работать в нашей компании, но не знает, как тут у нас все устроено. Наверное статья кажется не такой живой, потому что в основном я рассказываю об устройстве компании и выстроенных процессах, чем конкретно о себе)
Хотя насчет простоты xterm я все таки погорячился — его соурс код сложно понятен, да и вообще сам он раздут сильно по возможностям и наполнен многими ненужными в актуальное время вещами. Но в качестве понимания того, как примерно эмулятор терминала работает, вполне пойдет
/dev/ptmx — это все таки устройство. Фраза — "это не устройство, а интерфейс" странная, так как данное и является интерфейсом для создания pty. Как это работает? Мы вызываем open(/dev/ptmx), и вызов open обслуживает драйвер данного устройства, конкретнее мы видели, что за это отвечает функция ptmx_open.
"PTM и PTS — это виртуальные устройства...". Да, это верно, но не по той причине что вы написали, а потому, что они не существуют физически на диске, а живут в виртуальной файловой системе /dev, т.е. живут в памяти, в ядре. Вообще, то, как я определил виртуальные устройства — расходится с тем, что говорит википедия. Там виртуальные устройства — это устройства, не взаимодействующие напрямую с аппаратными устройствами. Так что лучше избегать понятия "виртуальное устройство" по отношению к ptm/pts и говорить, что PTM и PTS являются файлами устройств, а конкретнее, терминальными устройствами. Не думаю, что их можно назвать виртуальными устройствами, так как формально они взаимодействуют с терминалом. Вот /dev/null уже является виртуальным устройством.
Ну и последнее я считаю своей самой большой ошибкой в статье. Зря я вообще заикнулся о gnome терминале. Дело в том, что его работа намного сложнее, чем какой-нибудь xterm. Он не создает новые процессы для терминалов, а запускает их внутри тредов. Программы внутри терминала конечно же запускаются в отдельных процессах (а конкретнее этим занимается vte widget и его функция vte_terminal_spawn_async). Мой вам совет — поизучайте xterm — крайне простой и понятный эмулятор терминала, который, более того, работает в обход gtk, gnome и всяких других графических тулкитов.
Если вам интересно, убедиться в том, что gnome терминал — не отдельный процесс, можно достаточно просто. Введите:
$ ps --pid $$ --format ppid=
И вы увидите, что родителем баша является сам Gnome Terminal Server. Да, это кажется странным со стороны, но наверняка на то были причины, что gnome терминалы работают внутри тредов. Первую причину, которую я вижу, это функциональность вкладок (tabs).
Пишите в лс, можем еще что-то обсудить, если понятнее не стало :)
Хотел поставить плюс за ваш комментарий, приятно видеть разбирающихся людей в такой узкой сфере. Но я случайно промахнулся по кнопке голоса, так что апну этот комментарий)
Да, конечно же спавнится новый процесс и уже в нем запускается Shell. Смотрите эту функцию: https://developer.gnome.org/vte/unstable/VteTerminal.html#vte-terminal-spawn-async. Это — VTE widget, созданный и поддерживаемый GNOME. В нем и происходит почти вся работа по созданию терминалов. Конкретно в данной функции 4 аргументом принимается нужная команда, создается PTY и в новом процессе запускается данная команда на созданном PTY.
Вы сказали, что связь между pty master и pty slave это аналог линии связи. Однако, ведь это не правда. Линия связи лежала перед UART устройством. Байты с UART устройства принимал UART драйвер и передавал их после обработки драйверу консоли. Сейчас заместо uart драйвера и драйвера консоли выступают PTM и PTS устройства. Дисциплина линии же все также лежит между этими двумя устройствами, но здесь нет никакой линии связи больше. Конечно, это не отдельная дисциплина линии, а экспортируется дисциплина одного из устройств. Линия связи ушла, но дисциплина линии осталась. Точно также, как и название TTY — очень много концепций сохранилось с тех времен, поэтому не забывайте, что, может быть, хоть и сейчас "дисциплина линии" это что-то непонятное, но если мы окунемся в прошлое, то название будет иметь смысл.
Все запутанно)
Но главное, чтобы все понимали о чем идет речь. Например, многие говорят "функционал" вместо "функциональность" (приложения), но все равно всем понятно о чем идет речь из контекста.
Нет, я говорю именно про ситуацию, когда читаем 0. Давайте посмотрим еще раз на пример:
Если мы прочитали на R1 любое значение (хоть 0, хоть 1, но нас интересует именно 0), то запись W1 точно произошла, а значит на чтении R2 мы должны будем увидеть
1
. Аналогично рассуждаем и о втором треде. Таким образом, мы можем увидеть0
только на одном из чтений, но никак не на обоих, так как это просто невозможно если смотреть с точки зрения порядка в программе, ведь хотя бы одна запись должна была произойти.Другими словами, выполнение программы начинается или с записи
x
, или с записиy
. Если мы прочитали (0, 0), то это означало бы, что выполнение программы началось с чтений, но ведь они идут после записей? Однако это происходит по причине memory reordering, так как Java не дает sequential consistency по умолчанию.Нет, это называется именно
LoadStore
(читать так: loads can be reordered after stores).И здесь тоже именно
StoreLoad
(читать так: stores can be reordered after loads). Кстати, причина наличия такого переупорядочивания в x86 - это Store Buffer.Да, это про implementation и consistency. Лично я придерживаюсь мнения, что говорить "имплементация" - наоборот корректнее, чем "реализация". Та же вики под Реализацией имеет в виду что-то другому, чем Имплементация. Не знаю, насколько валидно говорить "консистентность" (наверное правильнее "согласованность"?), но это понятие используется повсеместно и если всем все понятно, то проблемы нет :)
Насчет перевода в Exclusive состояние еще уточню, но кажется вы правы, спасибо!
Но ведь данные чтения никак не аффектят друг друга, а поэтому их можно переупорядочить :) Перечитайте раздел [Happens-Before] Same thread actions - там написано о том, как переупорядочивание действий в треде не нарушает happens-before.
Только в том случае, если связать с помощью happens-before действия в разных тредах, happens-before будет гарантировать, что инструкция чтения
R2
произойдет только послеR1
(запретит LoadLoad memory reordering), а инструкция записиW1
произойдет доW2
(запретит StoreStore memory reordering), иначе happens-before в цепочке действий(W1,W2,R1,R2)
было бы нарушено.Другими словами, пока мы не свяжем
W2
иR1
с помощью happens-before (что дает нам volatile), то не будет happens-before между наборами действий(W1,W2)
и(R1,R2)
.Кстати, вот яркий пример. Взгляните на данный тест - https://github.com/blinky-z/JmmArticleHabr/blob/main/jcstress/tests/object/JmmReorderingObjectTest.java. Суть его в том, что мы можем прочитать неконсистентное состояние объекта, даже если увидели non-
null
ссылку. Он воспроизводится на ARM, но совсем не воспроизводится на x86, т.к. последний запрещаетStoreStore
/LoadLoad
reordering.Кстати, вот яркий пример. Взгляните на данный тест - https://github.com/blinky-z/JmmArticleHabr/blob/main/jcstress/tests/object/JmmReorderingObjectTest.java. Суть его в том, что мы можем прочитать неконсистентное состояние объекта, даже если увидели non-
null
ссылку. Он воспроизводится на ARM, но совсем не воспроизводится на x86, т.к. последний запрещаетStoreStore
/LoadLoad
reordering.Укажите, пожалуйста, на ошибки. Буду только рад поправить и сделать материал лучше
Вы абсолютно правы, x86 обладает намного более строгими гарантиями по сравнению с ARM/Power. Об этом я также упоминал несколько раз в статье
Рад стараться!
Asciidoc, к сожалению, не так лаконичен, прост и читабелен как Markdown. Например, об этом сказано даже в спеке CommonMark
Вы полностью правы.
Но здесь есть один тонкий момент, если мы говорим о графах с циклами. Насколько помню, в DFS мы помечаем вершину как пройденную после посещения очередной вершины, а в BFS - перед посещением (добавлением в очередь).
Писал статью сам. В статье хотел поделиться опытом работы и показать внутреннюю кухню, что было бы полезно тем кто еще на старте карьеры и рассматривает в качестве работы крупные компании или хотел бы работать в нашей компании, но не знает, как тут у нас все устроено. Наверное статья кажется не такой живой, потому что в основном я рассказываю об устройстве компании и выстроенных процессах, чем конкретно о себе)
Классная статья, спасибо
Рекомендую st для более глубого изучения устройства терминалов — это точно самый простой и понятной терминал на сегодня. https://st.suckless.org/
Хотя насчет простоты xterm я все таки погорячился — его соурс код сложно понятен, да и вообще сам он раздут сильно по возможностям и наполнен многими ненужными в актуальное время вещами. Но в качестве понимания того, как примерно эмулятор терминала работает, вполне пойдет
Посмотрел.
ptmx_open
.Если вам интересно, убедиться в том, что gnome терминал — не отдельный процесс, можно достаточно просто. Введите:
И вы увидите, что родителем баша является сам Gnome Terminal Server. Да, это кажется странным со стороны, но наверняка на то были причины, что gnome терминалы работают внутри тредов. Первую причину, которую я вижу, это функциональность вкладок (tabs).
Пишите в лс, можем еще что-то обсудить, если понятнее не стало :)
Хотел поставить плюс за ваш комментарий, приятно видеть разбирающихся людей в такой узкой сфере. Но я случайно промахнулся по кнопке голоса, так что апну этот комментарий)
Разве gnome не запускает терминалы в тредах внутри сервера?
Да, конечно же спавнится новый процесс и уже в нем запускается Shell. Смотрите эту функцию: https://developer.gnome.org/vte/unstable/VteTerminal.html#vte-terminal-spawn-async. Это — VTE widget, созданный и поддерживаемый GNOME. В нем и происходит почти вся работа по созданию терминалов. Конкретно в данной функции 4 аргументом принимается нужная команда, создается PTY и в новом процессе запускается данная команда на созданном PTY.
Замечу одну неточность в вашем ответе:
Вы сказали, что связь между pty master и pty slave это аналог линии связи. Однако, ведь это не правда. Линия связи лежала перед UART устройством. Байты с UART устройства принимал UART драйвер и передавал их после обработки драйверу консоли. Сейчас заместо uart драйвера и драйвера консоли выступают PTM и PTS устройства. Дисциплина линии же все также лежит между этими двумя устройствами, но здесь нет никакой линии связи больше. Конечно, это не отдельная дисциплина линии, а экспортируется дисциплина одного из устройств. Линия связи ушла, но дисциплина линии осталась. Точно также, как и название TTY — очень много концепций сохранилось с тех времен, поэтому не забывайте, что, может быть, хоть и сейчас "дисциплина линии" это что-то непонятное, но если мы окунемся в прошлое, то название будет иметь смысл.