У меня самая странная ошибка, с которой я когда-либо сталкивался с системой linux, и, кажется, есть только два возможных объяснения -
- Либо добавление sudo делает запись файлов мгновенно
- Или добавление sudo вызывает небольшую задержку при выполнении операторов
- Или я не знаю, что происходит с моей программой.
Хорошо, позвольте мне рассказать вам немного. В настоящее время я пишу программу С++ для малины pi gpio. Насколько я знаю, в программе нет видимой ошибки, так как она работает с sudo успешно и с задержками тоже. Итак, вот как работает rpi gpio -
Сначала вы должны экспортировать его, чтобы зарезервировать его для манипуляции, он создаст новый каталог как
gpio+number
с несколькими файлами в нем.
echo 17 > /sys/class/gpio/export
Затем установите его направление (в средствах чтения и вывода означает запись)
echo "out" > /sys/class/gpio/gpio17/direction
Затем напишите значение (0 или 1 для выключения и включения)
echo 1 > /sys/class/gpio/gpio17/value
В конце, не экспортируйте его обратно, каталог будет удален.
echo 17 > /sys/class/gpio/unexport
Неважно, выполняете ли вы это с помощью команд bash или с помощью c/С++ или любого другого языка ввода-вывода, поскольку в unix это просто файлы, и вам просто нужно их прочитать/написать. До сих пор все работает нормально. Я проверил это вручную, и он работает, поэтому мой ручной тест проходит.
Теперь у меня есть простой тест, написанный для моей программы, который выглядит так:
TEST(LEDWrites, LedDevice)
{
Led led1(17, "MyLED");
// auto b = sleep(1);
EXPECT_EQ(true, led1.on());
}
Класс Led constructor
выполняет экспортную часть - echo 17 > /sys/class/gpio/export
, а вызов .on()
устанавливает направление - echo "write" > /sys/class/gpio/gpio17/direction
и также выводит значение - echo 1 > /sys/class/gpio/gpio17/value
. Забудьте об отсутствии здесь, поскольку он обрабатывается деструктором и не играет здесь никакой роли.
Если вам интересно, эти функции обрабатывают I/O, как это -
{
const std::string direction = _dir ? "out" : "in";
const std::string path = GPIO_PATH + "/gpio" + std::to_string(powerPin) + "/direction";
std::ofstream dirStream(path.c_str(), std::ofstream::trunc);
if (dirStream) {
dirStream << direction;
} else {
// LOG error here
return false;
}
return true;
}
означает базовый С++ файл /io. Теперь позвольте мне объяснить ошибку.
Во-первых, здесь три прогона одного теста -
Normal run
FAILS
[[email protected] build]$ ./test/testexe
Running main() from gtest_main.cc
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from LEDConstruction
[ RUN ] LEDConstruction.LedDevice
[ OK ] LEDConstruction.LedDevice (1 ms)
[----------] 1 test from LEDConstruction (1 ms total)
[----------] 1 test from LEDWrites
[ RUN ] LEDWrites.LedDevice
../test/test.cpp:20: Failure
Value of: led1.on()
Actual: false
Expected: true
[ FAILED ] LEDWrites.LedDevice (2 ms)
[----------] 1 test from LEDWrites (3 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 2 test cases ran. (6 ms total)
[ PASSED ] 1 test.
[ FAILED ] 1 test, listed below:
[ FAILED ] LEDWrites.LedDevice
1 FAILED TEST
run with sudo
ПРОСМОТРЫ
[[email protected] build]$ sudo ./test/testexe
[sudo] password for isaac:
Running main() from gtest_main.cc
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from LEDConstruction
[ RUN ] LEDConstruction.LedDevice
[ OK ] LEDConstruction.LedDevice (1 ms)
[----------] 1 test from LEDConstruction (2 ms total)
[----------] 1 test from LEDWrites
[ RUN ] LEDWrites.LedDevice
[ OK ] LEDWrites.LedDevice (2 ms)
[----------] 1 test from LEDWrites (2 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 2 test cases ran. (5 ms total)
[ PASSED ] 2 tests.
wtf delay run
PASSES имеет раскомментированный // auto b = sleep(1);
[[email protected] build]$ ./test/testexe
Running main() from gtest_main.cc
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from LEDConstruction
[ RUN ] LEDConstruction.LedDevice
[ OK ] LEDConstruction.LedDevice (1 ms)
[----------] 1 test from LEDConstruction (2 ms total)
[----------] 1 test from LEDWrites
[ RUN ] LEDWrites.LedDevice
[ OK ] LEDWrites.LedDevice (1001 ms)
[----------] 1 test from LEDWrites (1003 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 2 test cases ran. (1005 ms total)
[ PASSED ] 2 tests.
Единственное различие между задержкой и стандартным запуском - единственная строка без комментирования - // auto b = sleep(1);
Все одинаково, включая устройство, структуру каталогов, сборку conf и все. Единственное, что объясняет это: linux может иногда создавать этот файл и его друзей позже или требуется некоторое время? и я называю .on()
до этого. Ну, это может объяснить это...
Но тогда почему происходит вызов sudo без задержки? Делает ли это запись быстрее/мгновенно или делает сам оператор задержки? Является ли это причиной какой-то буферизации? Пожалуйста, скажите нет:/
Если это имеет значение, я использую следующее правило dev для получения доступа не к sudo к каталогу gpio -
SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660"
SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"
SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"
EDIT. Как упоминалось в @charles, я использовал std::flush
после каждой записи, сделанной мной в операциях ввода-вывода. Все еще не работает.
Strace на помощь
Посмотрите на выполнение команды сбоя -
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/gpio17/value", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = -1 EACCES (Permission denied)
open("/sys/class/gpio/gpio17/direction", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = -1 EACCES (Permission denied)
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
..., 0666) = -1 EACCES (Permission denied)
Окаай, вот что-то, что объясняет, почему он проходит с судо. Но почему это происходит с задержкой? Пусть также проверьте,
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/gpio17/value", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/gpio17/direction", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
Нет, подождите, wtf? Это означает, что разрешенное разрешение должно быть указано, если файлы не были созданы в это время. Но как использование sudo
решает это?
Здесь соответствующий вывод для sudo -
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/export", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/gpio17/value", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
open("/sys/class/gpio/gpio17/direction", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
open("/sys/class/gpio/unexport", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3