Abh*_*yal 10 c++ linux sudo file c++11
我现在有一个我曾经见过的最糟糕的bug系统,它似乎只有两种可能的解释 -
好吧,让我给你一些背景知识.我正在为raspberry pi gpio操作编写一个c ++程序.据我所知,程序中没有明显的错误,因为它成功地与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/c ++或任何其他语言IO执行此操作都无关紧要,因为在unix中这些只是文件,您只需要读取/写入它们即可.到目前为止一切正常.我已经手动测试了它并且它可以工作,所以我的手动测试通过.
现在我为我的程序编写了一个简单的测试,看起来像这样 -
TEST(LEDWrites, LedDevice)
{
Led led1(17, "MyLED");
// auto b = sleep(1);
EXPECT_EQ(true, led1.on());
}
Run Code Online (Sandbox Code Playgroud)
Led类constructor
执行导出部分 - echo 17 > /sys/class/gpio/export
当.on()
调用设置方向时 - echo "write" > /sys/class/gpio/gpio17/direction
并输出值 - echo 1 > /sys/class/gpio/gpio17/value
.忘掉这里的devport,因为它是由析构函数处理的,在这里不起作用.
如果你很好奇,这些函数会像这样处理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;
}
Run Code Online (Sandbox Code Playgroud)
表示基本的c ++文件/ io.现在让我解释一下这个bug.
首先,这里有3次相同的测试 -
Normal run
失败
[isaac@alarmpi 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 Code Online (Sandbox Code Playgroud)
run with sudo
通行证
[isaac@alarmpi 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.
Run Code Online (Sandbox Code Playgroud)
wtf delay run
PASSES 没有注释// auto b = sleep(1);
[isaac@alarmpi 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.
Run Code Online (Sandbox Code Playgroud)
b/w延迟和正常运行的唯一区别在于单个未注释的行 - // auto b = sleep(1);
一切都相同,包括设备,目录结构,构建conf和一切.解释这一点的唯一原因是linux有时可能会创建该文件及其朋友,或者需要一些时间?我.on()
之前打电话给他.那可以解释一下......
但是为什么sudo调用没有延迟通过?它是否使这些写入更快/更快或是否自己放置延迟语句?这是某种缓冲的原因吗?请说不:/
如果重要的话,我正在使用以下开发规则获取非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'"
Run Code Online (Sandbox Code Playgroud)
编辑 - 正如@charles所提到的,我std::flush
在I/O操作上每次写入后都使用过.仍然失败.
需要救援
让我们看看失败的构建命令的执行 -
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
Run Code Online (Sandbox Code Playgroud)
..., 0666) = -1 EACCES (Permission denied)
Okaaay,这是什么,这解释了为什么它与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
Run Code Online (Sandbox Code Playgroud)
没等了,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
Run Code Online (Sandbox Code Playgroud)
udev和你的程序之间有一场比赛.写入时/sys/class/gpio/export
,写入将不会返回,直到GPIO完全创建.但是,一旦创建,您就有两个进程同时对新设备执行操作:
/sys/class/gpio/gpio17/value
./sys/class/gpio/gpio17/value
.因此,在 udev更改其所有权和权限之前,您的程序可能会打开该value
文件.事实上这很可能,因为你的udev处理程序执行了一个shell的execve,然后执行chown和chmod.但即使没有这个,调度程序通常会优先考虑从系统调用返回时已经运行的任务,因此您的程序通常会在udev甚至已经唤醒之前打开该文件.value
通过插入睡眠,你允许udev做它的事情.因此,为了使其健壮,您可以在打开文件之前使用access()轮询该文件.
通过给予udev更高的优先级也会有所帮助.例如chrt -f -p $(pidof systemd-udevd) 3
.这为udev提供了实时优先级,这意味着它将始终在您的程序之前运行.它还可以使您的系统无响应,所以要小心.
归档时间: |
|
查看次数: |
1596 次 |
最近记录: |