... Я просто хочу узнать о принципе, лежащем в основе небезопасного пакета.
Это аварийный люк .
Все решительно типизированные, но скомпилированные языки имеют основную проблему c: реальные машины, на которых будут работать скомпилированные программы, не имеют той же системы типизации, что и компилятор. 1 То есть сама машина, вероятно, имеет линейное адресное пространство, в котором байты собраны в машинные слова , сгруппированные в страницы и т. д. Операционная система может также обеспечить доступ, скажем, к степени детализации страницы: если вам нужно больше памяти, ОС предоставит вам одну страницу - 4096 байт, или 8192 байт, или 65536 байт, или любого размера страницы - дополнительной памяти на время.
Есть много способов решить эту проблему. Например, можно написать код непосредственно на машинном (или ассемблерном) языке, используя набор инструкций оборудования, чтобы общаться с ОС для достижения вещей на уровне ОС. Затем этот код может взаимодействовать с скомпилированной программой, действуя как go -вместо. Если скомпилированная программа должна выделить 40-байтовую структуру данных, этот код машинного уровня может выяснить, как это сделать в рамках ограничений по размеру страницы ОС.
Но написание машинного кода затруднительно и кропотливый. Именно поэтому у нас есть языки высокого уровня и компиляторы. Что если бы у нас был способ, на языке высокого уровня, нарушить нормальные правила, навязанные языком? Нарушая определенные c требования определенными c способами, тщательно координируя эти способы со всем другим кодом, который также нарушает эти требования, мы можем, в коде, который мы отдаляем от обычного программирования приложений, писать большую часть нашего управления памятью, управление процессами и т. д. на нашем языке высокого уровня.
Другими словами, мы можем использовать unsafe
(или что-то подобное в других языках) для намеренно break type- безопасность обеспечивается нашим языком высокого уровня. Когда мы делаем это - когда мы нарушаем правила - мы должны знать, каковы все правила, и что наши указанные здесь c нарушения будут работать правильно, в сочетании со всем нормальным кодом, который выполняет , подчиняется нормальному rules и в сочетании со всем специальным, небезопасным кодом, который нарушает правила.
Для этого часто требуется помощь самого компилятора. Если вы осмотрите источник runtime
, поставляемый с Go, вы найдете подпрограммы с аннотациями, такими как go:noescape
, go:noinline
, go:nosplit
и go:nowritebarrier
. Вам нужно знать, когда и почему они требуются, если вы собираетесь широко использовать некоторые из программ аварийной штриховки.
Некоторые из более простых применений, такие как приемы, чтобы получить доступ к строке или фрагменту Заголовки ... ну, они все еще небезопасны , но они небезопасны в более предсказуемых условиях и не требуют такого рода тесной координации с самим компилятором.
Чтобы понять как, когда и почему они работают, вам нужно понять, как компилятор и среда выполнения выделяют и работают со строками и слайсами, а в некоторых случаях, как распределяется память на оборудовании, и некоторые правила о Go ' Сборщик мусора. В частности, код G C знает о unsafe.Pointer
, но не uintptr
. Многое из этого довольно сложно: см., Например, https://utcc.utoronto.ca/~cks/space/blog/programming/GoUintptrVsUnsafePointer и ссылку на https://github.com/golang/go/issues/19135, в которой запись nil
в Go значение указателя вызывает * 1075 сборщик мусора * жаловался, потому что запись заставила G C проверить ранее сохраненное значение , которое было недопустимым.
1 См. эту статью в Википедии о Intel 432 , чтобы узнать о заметной попытке разработки аппаратного обеспечения для работы скомпилированных языков высокого уровня. В прошлом были и другие, часто с той же судьбой, хотя некоторые проекты IBM были более успешными.