Как получить доступ к значению errno из предыдущего вызова библиотеки libc.so.6?

Если то, что я пытаюсь сделать, не является хорошей идеей или не поддерживается, дайте мне знать. Я пытаюсь использовать различные системные библиотеки (libc.so.6, libpthread.so.0 и т. Д.) Для API. API будет вызывать эти библиотеки для доступа к open(), `close () и т. Д.

Я не уверен, что буду обрабатывать errno при сбоях, возвращая -1, вместо того, чтобы возвращать errno напрямую. Когда я вызываю close() в libc.so.6, он возвращает -1 в случае сбоя, поэтому я могу обнаружить произошедшую ошибку. Однако я не могу найти в libc какой-либо механизм, который позволяет мне запрашивать текущее значение errno, которое могло быть установлено предыдущим вызовом функции в том же потоке. Это не проблема при вызове функций pthread, потому что они напрямую возвращают значение errno, и я могу с этим работать.

Есть ли способ определить текущее значение errno для данного потока после выполнения функции, которая установила бы errno (например, close()), используя какой-либо вызов библиотеки?

Возможные решения:

Я мог бы написать библиотеку-оболочку, которая вызывает close() и т. Д. Запрашивает errno в состоянии ошибки и напрямую возвращает errno, но я стараюсь избегать включения пользовательских библиотек, которые должны быть включены в API.

Если есть способ вызвать стандартную библиотеку, это было бы идеально.


person Ryan    schedule 18.01.2017    source источник
comment
«Переменная» errno является локальной для потока, а не глобальной.   -  person Jonathan Leffler    schedule 19.01.2017
comment
Непонятно, о чем вы спрашиваете. errno доступен глобально для каждого потока. Таким образом, вам не нужна никакая библиотечная функция, чтобы получить ее значение. Просто: r = some_libc_function(); if (r==-1) last_errno = errno;   -  person kaylum    schedule 19.01.2017
comment
Спасибо за ответы. Я, вероятно, должен был дополнительно пояснить, что я получаю доступ к этим библиотекам из среды, которая предоставляет интерфейс для вызова библиотек, но у меня нет способа скомпилировать код вместе с этими вызовами библиотек, например включить ‹errno.h› и получить доступ к errno напрямую как Переменная. Вот почему я пытаюсь посмотреть, есть ли способ получить доступ к errno, если я не написал специально для этого код. Не уверен, что это имеет смысл.   -  person Ryan    schedule 19.01.2017
comment
@Ryan: В этом случае вам, вероятно, потребуется создать дополнительную библиотеку-заглушку, содержащую функцию get_errno или аналогичную, которая просто возвращает значение (или адрес) errno. В качестве альтернативы вы можете использовать функцию __errno_location, которая является серверной частью errno в glibc, но это не будет переносимым дизайном.   -  person R.. GitHub STOP HELPING ICE    schedule 19.01.2017
comment
Мне нравится идея заглушки @_R .. Итак, Райан, почему ты не можешь получить errno по ее адресу? А что насчет dlsym()? Или я предполагаю, что проблема в том, что вам нужна уже открытая библиотека в списке времени выполнения изображения ...   -  person clearlight    schedule 19.01.2017
comment
@clearlight: errno - это локальный символ потока, а не глобальный символ, поэтому у него нет адреса как такового - только смещение в локальной секции потока. По историческим причинам он часто не реализуется просто как локальный для потока символ с именем errno, поэтому вы не можете просто объявить это, даже если ваш компилятор поддерживает локальные для потока символы.   -  person Chris Dodd    schedule 19.01.2017
comment
@ChrisDodd, так что, если он в TLS, можно ли сделать вывод, что есть какой-то некрасивый способ получить к нему доступ без функции заглушки?   -  person clearlight    schedule 19.01.2017
comment
@clearlight: зависит от библиотеки C. Например, Solaris libc делает это: #define errno (*(___errno()))   -  person myaut    schedule 19.01.2017
comment
@R .. Спасибо за идею-заглушку. Я попытался это сделать, заставив close () генерировать ошибку (присвоив ему fd -1), а затем использовал __errno_location для получения указателя errno. Затем разыменование этого адреса вернуло значение 9 (EBADFD), чего я и ожидал. Я понимаю, что это не переносимо, поэтому мне нужно будет посмотреть дальше, чтобы увидеть, будет ли это проблемой для моего API, но на данный момент он делает то, что мне нужно.   -  person Ryan    schedule 19.01.2017
comment
@ChrisDodd: Формально errno - это макрос, а не какой-либо внешний объект, поэтому вы не можете ожидать, что для него будет символ. Есть ли у errno адрес - открытый вопрос: см. stackoverflow.com/questions/12945486/is-errno-legal -c   -  person R.. GitHub STOP HELPING ICE    schedule 19.01.2017
comment
@R: errno может быть макросом, а может и не быть - разные реализации различаются. Вы можете полагаться на то, что он является единственным lvalue для любого конкретного потока, поэтому вы можете получить его адрес для потока, этот адрес будет стабильным для этого потока.   -  person Chris Dodd    schedule 19.01.2017


Ответы (1)


errno не является глобальной переменной для каждого процесса (как раньше) в многопоточной среде, такой как Linux, существует другая errno псевдопеременная для каждого потока (на самом деле errno - это макрос, определенный в #include <errno.h>). Отвечая на ваш вопрос, лучший и самый простой способ сохранить errno из предыдущего системного вызова - это сделать копию errno сразу после завершения системного вызова. Вы даже можете сделать оболочку вокруг ваших системных вызовов для хранения значений errno в кольцевом буфере, чтобы вы могли получить последнее, от предыдущего до последнего и т. Д. До некоторого фиксированного значения. Поскольку для хранения всех этих значений требуется некоторый объем памяти, в системе не было вызвано никаких средств для этого. Кроме того, значение errno связано с последним системным вызовом, выполненным одним потоком, поэтому, поскольку вы должны решить, как и когда выполнять системный вызов, у вас также есть возможность решить, хотите ли вы сохранить, как и когда значение ошибки.

person Luis Colorado    schedule 19.01.2017