linux - Raspberry Pi GPIO/值文件暂时出现错误权限

标签 linux raspberry-pi raspbian gpio

我儿子正在 Raspberry Pi 上实现一个服务器,允许控制 GPIO通过网络连接的引脚。他发现了一些奇怪的行为,起初看起来像是一个错误(但请参阅下面的答案)。

首先,正在使用的操作系统是 Raspbian , Debian Linux 的一个版本.他正在使用标准系统文件来控制 GPIO 端口。

我们从一个 GPIO 引脚开始,例如引脚 17,处于非导出状态。例如,

echo "17" > /sys/class/gpio/unexport

现在,如果要求服务器打开引脚 17,它会执行以下操作:

  1. 打开/sys/class/gpio/export,向其中写入“17”,然后关闭导出文件
  2. 打开/sys/class/gpio/gpio17/direction文件进行读取,检查它是设置为输入还是输出。关闭文件。然后,如有必要,重新打开文件进行写入并将“out”写入文件,将引脚设置为输出引脚,并关闭方向文件。

此时,我们应该可以打开/sys/class/gpio/gpio17/value进行写入,并向其中写入“1”。

但是,/sys/class/gpio/gpio17/value文件的权限存在,但是组权限是只读的。如果我们为了等待几分之一秒而进入“ sleep ”状态,则权限会发生变化,因此组权限具有写入权限。

我原以为操作系统在正确设置值文件的权限之前,不应从写入 direction 文件返回。

为什么会这样? 这似乎是一个错误。我应该在哪里报告(更详细...)? 请参阅下面的答案。

以下是相关的代码。代码已经过一些编辑和释义,但它本质上就是正在使用的代码。 (请记住,这是一个试图学习 C++ 和 Unix 概念的 12 年级学生的代码):

class GpioFileOut
{
    private:
        const string m_fName;
        fstream m_fs;

    public:
        GpioFileOut(const string& sName)
        : m_fName(("/sys/class/gpio/" + sName).c_str())
        {
            m_fs.open(m_fName.c_str());
            if (m_fs.fail())
            {
                cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl << endl;
            }
            else
            {
                cout << m_fName << " opened" << endl;
            }
        }

        ~GpioFileOut()
        {
            m_fs.close();
            cout << m_fName << " closed" << endl << endl;
        }

        void reOpen()
        {
            m_fs.close();
            m_fs.open(m_fName);
            if (m_fs.fail())
            {
                cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl << endl;
            }
            else
            {
                cout << m_fName << " re-opened" << endl;
            }
        }

        GpioFileOut& operator<<(const string &s)
        {
            m_fs << s << endl;
            cout << s << " sent to " << m_fName << endl;
            return *this;
        }

        GpioFileOut& operator<<(int n)
        {
            return *this << to_string(n); //ostringstream
        }

        bool fail()
        {
            return m_fs.fail();
        }
};

class GpioFileIn
{
    private:
        ifstream m_fs;
        string m_fName;

    public:
        GpioFileIn(const string& sName)
        : m_fs( ("/sys/class/gpio/" + sName).c_str())
        , m_fName(("/sys/class/gpio/" + sName).c_str())
        {
            if (m_fs <= 0 || m_fs.fail())
            {
                cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl;
            }
            else
            {
                cout << m_fName << " opened" << endl;
            }
        }

        ~GpioFileIn()
        {
            m_fs.close();
            cout << m_fName << " closed" << endl << endl;
        }

        void reOpen()
        {
            m_fs.close();
            m_fs.open(m_fName);
            if (m_fs <= 0 || m_fs.fail())
            {
                cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl;
            }
            else
            {
                cout << m_fName << " re-opened" << endl;
            }
        }

        GpioFileIn& operator>>(string &s)
        {
            m_fs >> s;
            cout << s << " read from " << m_fName << endl;
            return *this;
        }
        bool fail()
        {
            return m_fs.fail();
        }
};

class Gpio
{
    public:
        static const bool OUT = true;
        static const bool IN = false;
        static const bool ON = true;
        static const bool OFF = false;

        static bool setPinDirection(const int pinId, const bool direction)
        {
            GpioFileOut dirFOut(("gpio" + to_string(pinId) + "/direction").c_str());
            if (dirFOut.fail())
            {
                if (!openPin(pinId))
                {
                    cout << "ERROR! Pin direction not set: Failed to export pin" << endl;
                    return false;
                }
                dirFOut.reOpen();
            }
            dirFOut << (direction == OUT ? "out" : "in");
        }


        static bool setPinValue(const int pinId, const bool pinValue)
        {
            string s;
            {
                GpioFileIn dirFIn(("gpio" + to_string(pinId) + "/direction").c_str());
                if (dirFIn.fail())
                {
                    if (!openPin(pinId))
                    {
                        cout << "ERROR! Pin not set: Failed to export pin"<<endl;
                        return false;
                    }
                    dirFIn.reOpen();
                }
                dirFIn >> s;
            }

            if (strncmp(s.c_str(), "out", 3) == 0)
            {
                struct stat _stat;
                int nTries = 0;
                string fname("/sys/class/gpio/gpio"+to_string(pinId)+"/value");

                for(;;)
                {
                    if (stat(fname.c_str(), &_stat) == 0)
                    {
                        cout << _stat.st_mode << endl;
                        if (_stat.st_mode & 020 )
                            break;
                    }
                    else
                    {
                        cout << "stat failed. (Did the pin get exported successfully?)" << endl;
                    }

                    cout << "sleeping until value file appears with correct permissions." << endl;
                    if (++nTries > 10)
                    {
                        cout << "giving up!";
                        return false;
                    }
                    usleep(100*1000);
                };
                GpioFileOut(("gpio" + to_string(pinId) + "/value").c_str()) << pinValue;
                return true;
            }
            return false;
        }

        static bool openPin(const int pinId)
        {
            GpioFileOut fOut("export");
            if (fOut.fail())
                return false;
            fOut << to_string(pinId);
            return true;
        }
}

int main()
{
    Gpio::openPin(17);
    Gpio::setPinDirection(17, Gpio::OUT)
    Gpio::setPinValue(17, Gpio::ON);
}

关键点是:没有 for(;;) 循环stat 的文件,执行失败,我们可以看到权限更改100 毫秒内的文件。

最佳答案

从内核的角度来看,已导出的每个 GPIO 引脚的“值”文件是使用模式 0644 和所有权 root:root 创建的。当您写入“方向”文件时,内核不会做任何更改。

您所描述的行为是由于 systemd udev 服务的运行造成的。此服务监听来自内核的有关设备状态更改的事件,并相应地应用规则。

当我在我自己的 Pi 上测试时,我没有遇到你描述的行为 -/sys 中的 gpio 文件全部归 root:root 所有,模式为 0644,并且无论方向如何都没有改变。然而,我正在运行 Pidora,我在我的系统中找不到与此相关的任何 udev 规则。我假设 Raspbian(或者可能是您添加到系统中的某些软件包)添加了此类规则。

我确实找到了 this thread其中提到了一些建议的规则。特别是这条规则会产生你描述的效果:

SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/class/gpio; chmod -R 770 /sys/class/gpio; chown -R root:gpio /sys/devices/virtual/gpio; chmod -R 770 /sys/devices/virtual/gpio'"

您可以在/lib/udev/rules.d、/usr/lib/udev/rules.d 和/etc/udev/rules.d 中搜索任何包含文本“gpio”的文件,以确认您是否有这样的文件规则。顺便说一句,如果更改是通过改变引脚上的方向触发的,我会感到惊讶,更可能是通过将引脚导出到用户空间的操作。

导出设备后需要休眠一段时间的原因是,在您的进程休眠之前,systemd 服务可能没有机会运行并执行规则。

这样做的原因,而不是仅仅让内核来处理它,是为了将策略实现推送到用户空间,以便在不使内核本身过于复杂的情况下提供最大的灵 active 。

参见:systemd-udevd.service手册页和 udev手册页。

关于linux - Raspberry Pi GPIO/值文件暂时出现错误权限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25695973/

相关文章:

qt 配置交叉编译问题 : device and xplatform

c++ - 交叉编译Qt to Raspberry Pi 3B+ addressbook (Protocol Buffer) - error symbol lookup error

javascript - 在树莓派上设置 DynamoDB 表

linux - 在 Raspberry Pi 2 中远程连接到 PostgreSQL

c - 在 Raspberry Pi 上,backtrace() 返回 0 帧

c - 什么时候应该使用 select 与多线程进行比较?

c - 什么是二进制文件,我该如何创建?

linux - 是否有适用于 Mac OS 和 Linux 的 MSDN 等效项?

Java RXTXcomm Lib 未加载或找不到

linux - 如何更改我的 OpenSUSE Linux 计算机的名称?