Я как раз частично поддерживаю, считая, что read() для SOCK_STREAM, SOCK_DGRAM и SOCK_SEQPACKET объединён зря. Там всё равно надо обрабатывать результаты совсем по-разному. Для потока - понимать, что данные поступили по порядку, буферизация в read() может быть как угодно, хоть по байту, хоть по миллиону байт, а возврат 0 обозначает закрытие входа. Для SOCK_DGRAM надо самому контролировать повторы и потери, буфер должен позволять полную датаграмму, а возврат 0 может быть нормальной ситуацией пустой датаграммы (UDP позволяет!) Для SOCK_SEQPACKET промежуточное между ними. Ещё и ancillary data у сокетов усложняет картину. То есть это действительно разные операции, объединение их под одной крышей достаточно насильственно.
А вот различать в этом случае IPv4, IPv6, IPX, локальные сокеты, пайпы - это уже точно перебор.
Во всяких утилитах вроде sort и grep вход это stdin а выход - stdout, которые ни разу не файлы, а каналы (pipe), при чём ещё односторонние.
Нет. Это может быть, повторяю, что угодно. Их сама утилита использует как односторонние каналы, но им ничто не мешает быть произвольными ядерными сущностями, которые просто умеют в read() или write().
Сам по себе stdin может быть терминалом - для тех программ, которым это нужно. Тот же bash, если интерактивный режим, использует свои stdin и stdout именно как терминал: настраивает политику raw режима, уточняет его тип, принимает/передаёт нужные esc-последовательности. (Если неинтерактивный, скрипт - ему пофиг, работает точно так же как grep, со входом и выходом.) И тут тоже, терминал может быть настоящий железный, может быть виртуальный от юниксовой консоли, может быть псевдотерминал (сейчас в 99+% случаев), может быть что-то другое, прикидывающееся терминалом - программе пофиг, пока сущность, спрятанная за дескриптором, поддерживает tcgetattr(), tcsetattr(), tcflush() и тому подобные вызовы.
По-идее, sh/bash должен перенаправлять эти каналы куда надо - хоть в терминал, хоть в файл, хоть куда-то ещё.
А может и не перенаправлять. При вызове без "|" он оставляет то, что ему самому поступило как дескрипторы 0 или 1. Или команду может запускать вообще не bash, а отдельная программа, давая на вход файлы или сама порождая пайпы. И снова для самой утилиты просто есть нечто на дескрипторе 0, которое как положено отрабатывает read(), и на 1 - write(). Такое себе ООП.
Кажется, вы никогда не интересовались тем, что там происходит внутри.
Сама же утилита grep должна работать с stdin/stdout только как с каналом - через соответствующий интерфейс
Да. Но это уже её выбор, а не то, что ей назначил шелл.
Условно говоря, если я читаю файлы, мне API сокетов не интересны.
Совсем не условно говоря, если у вас код какого-нибудь grep, sort и ещё десятки всяких, ему нужен API ввода или вывода потока байтов. При этом ему пофиг, как выглядит источник или приёмник потока байтов, пока он поток: это файл, терминал, безымянный пайп, именованный пайп, сокет пространства файлов, сокет TCP, псевдофайл /proc или /sys - работа с ними абсолютно одинакова. И это хорошо тем, что для простых задач применяются простые обобщённые решения.
Вот когда переходим к сложным задачам - программа сама открывает объекты, поднимает соединения, делает сама восстановление после ошибок при этом - и средства усложняются.
То есть в Unix при этом оптимальна, говоря терминами лингвистов, learning curve. Простые запросы - простые методы, сложные запросы - возможность применить сложные методы.
А вот так как было в OS/360 или есть местами в Windows, что даже для простой задачи надо навернуть что-то на порядок более замороченное - приводит к опусканию рук и хлопанью дверьми, или к нелепейшим решениям для простых задач только потому, что голова пухнет от нафиг не нужных подробностей.
даже несколько проще будет, ибо не нужно будет реализовывать диспатчинг в зависимости от фактического типа объекта.
Эта разница настолько мала, что не заслуживает внимания.
Не лучше ли иметь несколько отдельных системных вызовов для чтения из различных типов источников?
Это проходили. Например, в OS/360. Для каждого типа операции своё API. Посмотрите, например, макры для БТМД и ОТМД (не знаю названий на английском), они несовместимы по стилю и надо под каждый проектировать по-своему. И именно по опыту этих систем перешли к варианту, когда операция универсальна для разных типов внешних объектов.
read(), например, именно что читает порцию данных. Ей одинаково, это был файл, псевдофайл в соответствующей FS, пайп безымянный, пайп именованный, сокет... Ей надо прочитать порцию байт, она её читает.
Конечно, надо заметить, что Unix тут переабстрагировался в другую сторону. Например, если read() для файла получил 0, это точно конец файла, а для терминала это может быть Ctrl+D (сброс буфера) при пустом буфере (начало строки), а для UDP датаграмма размера 0 (законный вариант!) Семантика такого read() для TCP совпадает с файловой, а для UDP - нет, есть границы датаграмм, ещё и источник не однозначен. Я бы не делал read() для UDP, разрешив тут только явный recvfrom(). Но в целом вреда от переобобщения образца Unix сильно меньше, чем от необходимости делать всегда разные вызовы, как было в OS/360 (и как частично воспроизвели в Windows с сокетами).
Сейчас консенсус в том, что есть вызовы для самого простого банального типа, как read/write, а есть усложнения для тех, кому надо (например, recvmsg для желающих получить ещё и доп. опции от передатчика).
UPD: А ещё это как "трейты" ("типажи") в языках как Rust. Вот есть типаж "нечто, из чего можно read". Вот есть "нечто, поддерживающее recvmsg". Вот есть "нечто с прямым доступом", соответственно, умеет lseek(). Просто в рантайме на уровне собственно границы юзерленд - ядро тип стёрт до одного int, и поэтому вы можете попытаться вызвать lseek для сокета и recvmsg для файла, и получите ошибку в рантайме, а не при компиляции. Ещё и read потоковый, read датаграммный и read для SOCK_SEQPACKET, по факту, объединены в одном интерфейсе, хоть и они и разные по сути. Вот это уже та часть легаси, которую неплохо бы исправить.
Ещё в фидошные времена в ru.os.cmp пытались получить определение файла и нашли, что в разных ОС настолько отличаются критерии определения "файл", и разные псевдофайловые системы типа /proc настолько усложняют картину, что общего определения просто нет! Есть несколько вариантов, подходящих для конкретных ОС, но не для всех одновременно. Это несмотря на то, что интуитивно мы (ну кто прошёл начальный этап) всё понимаем.
Отсюда и пункт в позднее сформированном "постмодернистском" FAQ:
Процесс таки не файл. Вы не можете "открыть" процесс и иметь дескриптор на него. То, что в /proc, может исчезнуть на ходу, дав вам обгон (race) при попытке что-то сделать с этим процессом. А вот в Windows это решили: их аналог kill() это таки открыть процесс и уже по хэндлу что-то послать ему.
Интернет это не файл. Это в Plan9 таки довели идею "всё есть файл" почти во всём, дав возможность, например, open("/net/tcp/1.2.3.4:443") (или похоже), там Интернет, по факту, файловый каталог:)
Увы. То, что через дескрипторы просто передаются почти произвольные ресурсы, это ещё не всё, что нужно.
Формально - да, можно. На практике - вы при восстановлении будете должны очень много вещей вычислить и подставить самому: например, что функция должна зваться CreatePlan(), а не sub_4D6B034F, как именно формировались данные (.ascii "Hello", а не .db 0x48, 0x65, 0x6c, 0x6c, 0x6f), и так далее. Это называется реверс-инжинирингом и это отдельный серьёзный навык.
У меня на одной из прошлых работ был любитель MS Natural. Как-то он привёз новую и распаковал, я опробовал. Я не понял, как вообще на этом можно работать. Клавиши из такого свежего скрипят и заедают. У предыдущей было то же самое, я бы там понял на б/у, но не на новой же...
Я начинал ещё на терминалах ЕС7066, там клавиатура была толстенная и с заметным подъёмом от пользователя. Видимо, кто-то так раньше рассчитывал. Поэтому привык поднимать задние подставки на PCшных клавах. Только прочитав про туннельный синдром перестал поднимать эти подставки (хоть мне, скорее всего, этот синдром и не грозит).
Справедливости ради я бы заметил, что часть описанного вами это полезная вариативность. Кому-то нужен полноценный нумпад, а кому-то он, наоборот, мешает, или лаптоп маленький, или клавиатура нужна только 60%... Зато цифровой режим нумпада, мне кажется, очень хорошая идея именно если требуется "бухгалтерский" режим.
А вот форма Enter (ВК) это следствие различия стандартов ANSI и ISO, и метания правой "|" вокруг неё - по той же причине. Клавиша в позиции B00 (между LShift и Z в нашем варианте) с обозначениями (для России, Украины и т.д.) "|" это настолько неудобно, что авторов ISO9995 хочется слегка пожечь синим пламенем и заставить их официально перенести её в позицию B11 (откусив кусок от RShift и тем самым сделав шифты примерно равными по ширине).
Курсор... вот новый игровой Asus меня таки бесит. PgUp, PgDn, Home, End только как Fn+↑↓←→ соответственно, а там, где их привычно искать - мультимедийный набор. Не знаю, сколько ещё привыкать.
Сейчас на Win11 24H2 мне эта комбинация четырёх модификаторов без целевой клавиши вызвала окно "Welcome to Microsoft 365 Copilot" (после чего начало делать вид, что работает). Видимо, эффект от неё меняется в зависимости от последнего постановления ВЦСПС службы продажников? Мне не нравится, что это только модификаторы без единой целевой клавиши. Но после сочетаний Ctrl+Shift и Alt+Shift удивляться не приходится.
Так вы попробуйте не просто нажимать, а нажать, спросить, поработать в другом окне, опять нажать.
Да, почему-то после этого он открылся только со второго нажатия. Но открылся, и после этого снова - закрывается и открывается по кругу. Какой-то эффект там есть, но совсем критичного нет.
Это не та же PrintScreen, что традиционно известна со времён клавиатуры XT (отдельной кнопкой - AT-101), та тоже есть (на нём как Fn+Q). Это отдельная кнопка, которая именно что посылает последовательность Shift+Win+S, в виде соответствующих сканкодов нажатия и отжатия. Вы это, очевидно, пропустили в моём сообщении, как и то, что эта комбинация делает не то же, что и классический PrintScreen.
Про "шутку" я не говорил, не надо приписывать мне не мои слова. А вот что это эксперимент, который могут не продолжить - да, именно так.
Я, как юниксоед всю свою карьеру, приветствовал этот шаг по направлению к Space-Cadet keyboard, даже ещё когда не знал про неё:) Super есть. Ещё три модификатора бы, только размещение придумать.
Я как раз частично поддерживаю, считая, что read() для SOCK_STREAM, SOCK_DGRAM и SOCK_SEQPACKET объединён зря. Там всё равно надо обрабатывать результаты совсем по-разному. Для потока - понимать, что данные поступили по порядку, буферизация в read() может быть как угодно, хоть по байту, хоть по миллиону байт, а возврат 0 обозначает закрытие входа. Для SOCK_DGRAM надо самому контролировать повторы и потери, буфер должен позволять полную датаграмму, а возврат 0 может быть нормальной ситуацией пустой датаграммы (UDP позволяет!) Для SOCK_SEQPACKET промежуточное между ними. Ещё и ancillary data у сокетов усложняет картину. То есть это действительно разные операции, объединение их под одной крышей достаточно насильственно.
А вот различать в этом случае IPv4, IPv6, IPX, локальные сокеты, пайпы - это уже точно перебор.
Нет. Это может быть, повторяю, что угодно. Их сама утилита использует как односторонние каналы, но им ничто не мешает быть произвольными ядерными сущностями, которые просто умеют в read() или write().
Сам по себе stdin может быть терминалом - для тех программ, которым это нужно. Тот же bash, если интерактивный режим, использует свои stdin и stdout именно как терминал: настраивает политику raw режима, уточняет его тип, принимает/передаёт нужные esc-последовательности. (Если неинтерактивный, скрипт - ему пофиг, работает точно так же как grep, со входом и выходом.) И тут тоже, терминал может быть настоящий железный, может быть виртуальный от юниксовой консоли, может быть псевдотерминал (сейчас в 99+% случаев), может быть что-то другое, прикидывающееся терминалом - программе пофиг, пока сущность, спрятанная за дескриптором, поддерживает tcgetattr(), tcsetattr(), tcflush() и тому подобные вызовы.
А может и не перенаправлять. При вызове без "|" он оставляет то, что ему самому поступило как дескрипторы 0 или 1. Или команду может запускать вообще не bash, а отдельная программа, давая на вход файлы или сама порождая пайпы. И снова для самой утилиты просто есть нечто на дескрипторе 0, которое как положено отрабатывает read(), и на 1 - write(). Такое себе ООП.
Кажется, вы никогда не интересовались тем, что там происходит внутри.
Да. Но это уже её выбор, а не то, что ей назначил шелл.
Совсем не условно говоря, если у вас код какого-нибудь grep, sort и ещё десятки всяких, ему нужен API ввода или вывода потока байтов. При этом ему пофиг, как выглядит источник или приёмник потока байтов, пока он поток: это файл, терминал, безымянный пайп, именованный пайп, сокет пространства файлов, сокет TCP, псевдофайл /proc или /sys - работа с ними абсолютно одинакова. И это хорошо тем, что для простых задач применяются простые обобщённые решения.
Вот когда переходим к сложным задачам - программа сама открывает объекты, поднимает соединения, делает сама восстановление после ошибок при этом - и средства усложняются.
То есть в Unix при этом оптимальна, говоря терминами лингвистов, learning curve. Простые запросы - простые методы, сложные запросы - возможность применить сложные методы.
А вот так как было в OS/360 или есть местами в Windows, что даже для простой задачи надо навернуть что-то на порядок более замороченное - приводит к опусканию рук и хлопанью дверьми, или к нелепейшим решениям для простых задач только потому, что голова пухнет от нафиг не нужных подробностей.
Эта разница настолько мала, что не заслуживает внимания.
Это проходили. Например, в OS/360. Для каждого типа операции своё API. Посмотрите, например, макры для БТМД и ОТМД (не знаю названий на английском), они несовместимы по стилю и надо под каждый проектировать по-своему. И именно по опыту этих систем перешли к варианту, когда операция универсальна для разных типов внешних объектов.
read(), например, именно что читает порцию данных. Ей одинаково, это был файл, псевдофайл в соответствующей FS, пайп безымянный, пайп именованный, сокет... Ей надо прочитать порцию байт, она её читает.
Конечно, надо заметить, что Unix тут переабстрагировался в другую сторону. Например, если read() для файла получил 0, это точно конец файла, а для терминала это может быть Ctrl+D (сброс буфера) при пустом буфере (начало строки), а для UDP датаграмма размера 0 (законный вариант!) Семантика такого read() для TCP совпадает с файловой, а для UDP - нет, есть границы датаграмм, ещё и источник не однозначен. Я бы не делал read() для UDP, разрешив тут только явный recvfrom(). Но в целом вреда от переобобщения образца Unix сильно меньше, чем от необходимости делать всегда разные вызовы, как было в OS/360 (и как частично воспроизвели в Windows с сокетами).
Сейчас консенсус в том, что есть вызовы для самого простого банального типа, как read/write, а есть усложнения для тех, кому надо (например, recvmsg для желающих получить ещё и доп. опции от передатчика).
UPD: А ещё это как "трейты" ("типажи") в языках как Rust. Вот есть типаж "нечто, из чего можно read". Вот есть "нечто, поддерживающее recvmsg". Вот есть "нечто с прямым доступом", соответственно, умеет lseek(). Просто в рантайме на уровне собственно границы юзерленд - ядро тип стёрт до одного int, и поэтому вы можете попытаться вызвать lseek для сокета и recvmsg для файла, и получите ошибку в рантайме, а не при компиляции. Ещё и read потоковый, read датаграммный и read для SOCK_SEQPACKET, по факту, объединены в одном интерфейсе, хоть и они и разные по сути. Вот это уже та часть легаси, которую неплохо бы исправить.
Ещё в фидошные времена в ru.os.cmp пытались получить определение файла и нашли, что в разных ОС настолько отличаются критерии определения "файл", и разные псевдофайловые системы типа /proc настолько усложняют картину, что общего определения просто нет! Есть несколько вариантов, подходящих для конкретных ОС, но не для всех одновременно. Это несмотря на то, что интуитивно мы (ну кто прошёл начальный этап) всё понимаем.
Отсюда и пункт в позднее сформированном "постмодернистском" FAQ:
Q48: что такое файл?
A48: ОПЯТЬ?
Процесс таки не файл. Вы не можете "открыть" процесс и иметь дескриптор на него. То, что в /proc, может исчезнуть на ходу, дав вам обгон (race) при попытке что-то сделать с этим процессом.
А вот в Windows это решили: их аналог kill() это таки открыть процесс и уже по хэндлу что-то послать ему.
Интернет это не файл. Это в Plan9 таки довели идею "всё есть файл" почти во всём, дав возможность, например, open("/net/tcp/1.2.3.4:443") (или похоже), там Интернет, по факту, файловый каталог:)
Увы. То, что через дескрипторы просто передаются почти произвольные ресурсы, это ещё не всё, что нужно.
Текст и данные - об этом постоянно забывают.
Формально - да, можно. На практике - вы при восстановлении будете должны очень много вещей вычислить и подставить самому: например, что функция должна зваться CreatePlan(), а не sub_4D6B034F, как именно формировались данные (.ascii "Hello", а не .db 0x48, 0x65, 0x6c, 0x6c, 0x6f), и так далее. Это называется реверс-инжинирингом и это отдельный серьёзный навык.
Подтверждаю для Asus ROG Strix G16.
У меня на одной из прошлых работ был любитель MS Natural. Как-то он привёз новую и распаковал, я опробовал. Я не понял, как вообще на этом можно работать. Клавиши из такого свежего скрипят и заедают. У предыдущей было то же самое, я бы там понял на б/у, но не на новой же...
Я начинал ещё на терминалах ЕС7066, там клавиатура была толстенная и с заметным подъёмом от пользователя. Видимо, кто-то так раньше рассчитывал. Поэтому привык поднимать задние подставки на PCшных клавах.
Только прочитав про туннельный синдром перестал поднимать эти подставки (хоть мне, скорее всего, этот синдром и не грозит).
Кажется, это осталось у них только во ThinkPad линии? У остальных линий такого нет.
Справедливости ради я бы заметил, что часть описанного вами это полезная вариативность. Кому-то нужен полноценный нумпад, а кому-то он, наоборот, мешает, или лаптоп маленький, или клавиатура нужна только 60%... Зато цифровой режим нумпада, мне кажется, очень хорошая идея именно если требуется "бухгалтерский" режим.
А вот форма Enter (ВК) это следствие различия стандартов ANSI и ISO, и метания правой "|" вокруг неё - по той же причине. Клавиша в позиции B00 (между LShift и Z в нашем варианте) с обозначениями (для России, Украины и т.д.) "|" это настолько неудобно, что авторов ISO9995 хочется слегка пожечь синим пламенем и заставить их официально перенести её в позицию B11 (откусив кусок от RShift и тем самым сделав шифты примерно равными по ширине).
Курсор... вот новый игровой Asus меня таки бесит. PgUp, PgDn, Home, End только как Fn+↑↓←→ соответственно, а там, где их привычно искать - мультимедийный набор. Не знаю, сколько ещё привыкать.
Сейчас на Win11 24H2 мне эта комбинация четырёх модификаторов без целевой клавиши вызвала окно "Welcome to Microsoft 365 Copilot" (после чего начало делать вид, что работает).
Видимо, эффект от неё меняется в зависимости от последнего постановления
ВЦСПСслужбы продажников?Мне не нравится, что это только модификаторы без единой целевой клавиши. Но после сочетаний Ctrl+Shift и Alt+Shift удивляться не приходится.
Всегда назначаю CapsLock на эту функцию. Индикатор - ScrollLock, если есть на клавиатуре, ну и в трее DM.
Дайте, пожалуйста, точную ссылку на фото той модели, которую имеете в виду. А то их много, и ни одна, что нашлась поиском, не отвечает моему запросу.
Да, почему-то после этого он открылся только со второго нажатия. Но открылся, и после этого снова - закрывается и открывается по кругу.
Какой-то эффект там есть, но совсем критичного нет.
Это не та же PrintScreen, что традиционно известна со времён клавиатуры XT (отдельной кнопкой - AT-101), та тоже есть (на нём как Fn+Q). Это отдельная кнопка, которая именно что посылает последовательность Shift+Win+S, в виде соответствующих сканкодов нажатия и отжатия. Вы это, очевидно, пропустили в моём сообщении, как и то, что эта комбинация делает не то же, что и классический PrintScreen.
Про "шутку" я не говорил, не надо приписывать мне не мои слова. А вот что это эксперимент, который могут не продолжить - да, именно так.
Прошу таки быть внимательнее к деталям.
11ка 24H2 26100.4946: нажал - всплыло окно Copilot поверх всех, нажал снова - исчезло, и так по кругу.
Я, как юниксоед всю свою карьеру, приветствовал этот шаг по направлению к Space-Cadet keyboard, даже ещё когда не знал про неё:) Super есть. Ещё три модификатора бы, только размещение придумать.