Как все начиналось
В рамках лабораторной работы в университете надо было написать небольшую программу для работы с системными вызовами. Язык программирования можно было выбрать любой, а поскольку я учу Golang, мой выбор пал на него.
Погуглив, я нашел специальный пакет syscall, но понятность документации оставляет желать лучшего. Я стал копать дальше, но материала по данной теме оказалось очень мало, а на русском языке почти нет, поэтому я и решил написать данную статью.
Быстро про системные вызовы
Системный вызов - это обращение к ядру операционной системы. С помощью них наши программы могут планировать процессы, создавать каналы взаимодействия друг с другом и много чего ещё интересного.
В данной статье я рассмотрю следующие системные вызовы:
fork() - создание нового процесса с тем же исполняемым кодом, что и родительском процессе. Причем продолжение обоих процессов продолжится с того места, где был вызван syscall
exec() - меняет исполняемый код процесса на другой
Обёртки над системными вызовами
В пакете syscall есть разные реализации приведенных выше системных вызовов.
ForkExec() объединила в себе fork и exec, что немного упростило написание кода. Посмотрим на сигнатуру этого метода:
ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
argv0 - файл с программой, которую мы хотим запустить в новом процессе.
argv - остальные аргументы командной строки, включая argv0.
attr - специальная структура
ProcAttr
, в которой описаны параметры для запуска нового процесса.Возвращает функция pid нового процесса или 0, если не получилось его создать, и ошибку, если таковая имеется.
Давайте рассмотрим ProcAttr
подробнее:
type ProcAttr struct {
Dir string // Current working directory.
Env []string // Environment.
Files []uintptr // File descriptors.
Sys *SysProcAttr
}
dir - путь до рабочей директории.
env - переменные окружающей среды.
files - файловые дескрипторы. Можно удобно их задать для текущего процесса без использования дополнительных системных вызовов.
sys - остальные параметры процесса типа SysProcAttr.
Я не использовал всех полей, пример кода выглядит так
args := &syscall.ProcAttr{
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
}
Если вам нужно перенаправить stdin, stdout или stderr в другие файлы, то файловые дескрипторы следует поменять.
Классическое представление
Если у вас возникла потребность вызвать эти системные вызовы отдельно, то можно использовать следующие функции:
syscall.Syscall(syscall.SYS_FORK, 0)
Первым аргументом идёт системный вызов, далее аргументы. Возвращается pid дочернего процесса в родительском, а в дочернем 0.
Exec(argv0 string, argv []string, envv []string) (err error)
argv0 - файл с программным кодом, который будет исполняться в новом процессе.
argv - аргументы командной строки
envv - переменные окружения
Не вижу особого смысла использовать такой вариант для создания новых процессов, потому что приходится писать больше кода и для изменения параметров нового процесса придется использовать дополнительные системные вызовы.
Вывод
Golang предоставляет удобный интерфейс для взаимодействия с системными вызовами, что может быть очень удобно для написания низкоуровневых приложений приложений. Надеюсь статья помогла вам разобраться с пакетом syscall.