首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >basic_filebuf::underflow在/proc/pid/stat上读取ifstream文件时出错

basic_filebuf::underflow在/proc/pid/stat上读取ifstream文件时出错
EN

Stack Overflow用户
提问于 2013-08-06 19:26:08
回答 2查看 12K关注 0票数 5

为什么下面的代码会抛出异常?请注意,该文件是一个/proc/pid/stat文件,因此它可能受到内核的干扰。

代码语言:javascript
复制
// Checked that file does exist
try {
  std::ifstream file(path.c_str());
  // Shouldn't even be necessary because it's the default but it doesn't 
  // make any difference.
  file.exceptions(std::ifstream::goodbit);
  // Read the stream into many fields
  // !!!! The exception was thrown here.
  file >> _ >> comm >> state >> ppid >> pgrp >> session >> tty_nr
       /* >> ... omitted */;
  file.close();
} catch (const std::ifstream::failure& e) {
  std::cout << "Exception!!!! " << e.what();
}

例外情况是"basic_filebuf::underflow读取文件时出错“。

当我们没有要求异常(通过设置file.exceptions())时,流不应该抛出异常吗?

更多信息:

  • 它运行于gcc版4.1.2 20080704 (红帽4.1.2-54)
  • 导致问题的完整代码(没有try/catch部分):proc.hpp
EN

回答 2

Stack Overflow用户

发布于 2013-08-06 19:44:27

更新2

我甚至试图通过手动设置微小或巨大的缓冲区大小来强制错误:

代码语言:javascript
复制
    std::filebuf fb;
    // set tiny input buffer
    char buf[8]; // or huge: 64*1024
    fb.pubsetbuf(buf, sizeof(buf));
    fb.open(path.c_str(), std::ios::in);

    std::istream file(&fb);

我已经证实使用strace读取的大小确实很小

代码语言:javascript
复制
sudo strace ./test $(sudo ps h -ae -o pid) |& 
      egrep -w 'read|open' | grep -v '= 7' | less -SR

有趣的是,所有这些都没有失败。

已更新

作为对这些评论的回应,我设计了一个独立的程序来完成OP所描述的,但我无法重现这个问题:

  • http://coliru.stacked-crooked.com/view?id=81960c...
代码语言:javascript
复制
#include <sys/types.h> // For pid_t.
#include <fstream>
#include <string>

// mock up
#include <boost/variant.hpp>
namespace {
    struct None {};
    struct Error { std::string s; Error(std::string s): s(s){} };

    std::ostream& operator<<(std::ostream& os, None const&) {
        return os << "None";
    }

    std::ostream& operator<<(std::ostream& os, Error const& e) {
        return os << "Error {" << e.s << "}";
    }

    template <typename T>
        using Result = boost::variant<None, Error, T>;
}
// end mockup

namespace proc {

    // Snapshot of a process (modeled after /proc/[pid]/stat).
    // For more information, see:
    // http://www.kernel.org/doc/Documentation/filesystems/proc.txt
    struct ProcessStatus
    {
        pid_t pid;
        std::string comm;
        char state;
        pid_t ppid, pgrp, session;
        int tty_nr;
        pid_t tpgid;
        unsigned int flags;
        unsigned long minflt, cminflt, majflt, cmajflt;
        unsigned long utime, stime;
        long cutime, cstime, priority, nice, num_threads, itrealvalue;
        unsigned long long starttime;
        unsigned long vsize;
        long rss;
        unsigned long rsslim, startcode, endcode, startstack, kstkeip, signal, blocked, sigcatch, wchan, nswap, cnswap;

        friend std::ostream& operator<<(std::ostream& os, proc::ProcessStatus const& ps) {
            return os << 
                "pid: "         << ps.pid         << "\n" << 
                "comm: "        << ps.comm        << "\n" << 
                "state: "       << ps.state       << "\n" << 
                "ppid: "        << ps.ppid        << "\n" << 
                "pgrp: "        << ps.pgrp        << "\n" << 
                "session: "     << ps.session     << "\n" << 
                "tty_nr: "      << ps.tty_nr      << "\n" << 
                "tpgid: "       << ps.tpgid       << "\n" << 
                "flags: "       << ps.flags       << "\n" << 
                "minflt: "      << ps.minflt      << "\n" << 
                "cminflt: "     << ps.cminflt     << "\n" << 
                "majflt: "      << ps.majflt      << "\n" << 
                "cmajflt: "     << ps.cmajflt     << "\n" << 
                "utime: "       << ps.utime       << "\n" << 
                "stime: "       << ps.stime       << "\n" << 
                "cutime: "      << ps.cutime      << "\n" << 
                "cstime: "      << ps.cstime      << "\n" << 
                "priority: "    << ps.priority    << "\n" << 
                "nice: "        << ps.nice        << "\n" << 
                "num_threads: " << ps.num_threads << "\n" << 
                "itrealvalue: " << ps.itrealvalue << "\n" << 
                "starttime: "   << ps.starttime   << "\n" << 
                "vsize: "       << ps.vsize       << "\n" << 
                "rss: "         << ps.rss         << "\n" << 
                "rsslim: "      << ps.rsslim      << "\n" << 
                "startcode: "   << ps.startcode   << "\n" << 
                "endcode: "     << ps.endcode     << "\n" << 
                "startstack: "  << ps.startstack  << "\n" << 
                "kstkeip: "     << ps.kstkeip     << "\n" << 
                "signal: "      << ps.signal      << "\n" << 
                "blocked: "     << ps.blocked     << "\n" << 
                "sigcatch: "    << ps.sigcatch    << "\n" << 
                "wchan: "       << ps.wchan       << "\n" << 
                "nswap: "       << ps.nswap       << "\n" << 
                "cnswap: "      << ps.cnswap      << "\n";
        }
    };

    // Returns the process statistics from /proc/[pid]/stat.
    // The return value is None if the process does not exist.
    inline Result<ProcessStatus> status(pid_t pid)
    {
        std::string path = "/proc/" + std::to_string(pid) + "/stat";

        std::ifstream file(path.c_str());

        if (!file.is_open()) {
#if 1
            return Error("Failed to open '" + path + "'");
#else // FIXME reenable
            // Need to check if file exists AFTER we open it to guarantee
            // process hasn't terminated (or if it has, we at least have a
            // file which the kernel _should_ respect until a close).
            if (!os::exists(path)) {
                return None();
            }
            return Error("Failed to open '" + path + "'");
#endif
        }

        std::string _; // For ignoring fields.

        // Parse all fields from stat.
        ProcessStatus ps;
        if (file >> _ >> ps.comm >> ps.state >> ps.ppid >> ps.pgrp >> ps.session >> ps.tty_nr
            >> ps.tpgid >> ps.flags >> ps.minflt >> ps.cminflt >> ps.majflt >> ps.cmajflt
            >> ps.utime >> ps.stime >> ps.cutime >> ps.cstime >> ps.priority >> ps.nice
            >> ps.num_threads >> ps.itrealvalue >> ps.starttime >> ps.vsize >> ps.rss
            >> ps.rsslim >> ps.startcode >> ps.endcode >> ps.startstack >> ps.kstkeip
            >> ps.signal >> ps.blocked >> ps.sigcatch >> ps.wchan >> ps.nswap >> ps.cnswap)
        {
            return ps;
        } else
        {
            return Error("Failed to read/parse '" + path + "'");
        }
    }


} // namespace proc {

int main(int argc, const char *argv[])
{
    for (auto i=1; i<argc; ++i)
        std::cout << proc::status(std::stoul(argv[i])) << "\n";
}

它在我的机器上快乐地运行,打印类似的东西

代码语言:javascript
复制
pid: 594590200
comm: (test)
state: R
ppid: 8123
pgrp: 8123
session: 8123
...

即使/当我折磨它

代码语言:javascript
复制
sudo ./test $(sudo ps h -ae -o pid) | grep -v : | sort -u
./test $(sudo ps h -ae -o pid) | grep -v : | sort -u

它只是显示(大概是来自子subshell的sudo/ps )

代码语言:javascript
复制
Error {Failed to open '/proc/8652/stat'}
Error {Failed to open '/proc/8653/stat'}

我试着从输入流中读取两次信息(以强制读取超过结束类型的情况),但没有运气。

旧的答案:

您需要确定异常发生在什么样的条件下。以下演示代码在我的系统上按预期工作:

代码语言:javascript
复制
#include <fstream>
#include <iostream>

int main()
{
    system("ps -o pid,comm,state,ppid,pgrp,session,tty > input.txt");

    try {
        std::ifstream file("input.txt");

        file.exceptions(std::ifstream::goodbit);

        std::string _, comm, state, ppid, pgrp, session, tty_nr;
        while (file >> _ >> comm >> state >> ppid >> pgrp >> session >> tty_nr)
        {
            for (auto&& s : { _, comm, state, ppid, pgrp, session, tty_nr })
                std::cout << s << "\t";
            std::cout << "\n";
        }
        file.close();
    } catch (const std::ifstream::failure& e) {
        std::cout << "Exception!!!! " << e.what();
    }
}

印刷品,例如:

代码语言:javascript
复制
PID         COMMAND S       PPID    PGRP    SESS    TT      
20950       bash    S       20945   20950   20950   pts/1   
21275       vim     S       20950   21275   20950   pts/1   
21279       bash    S       21275   21275   20950   pts/1   
21280       test    S       21279   21275   20950   pts/1   
21281       sh      S       21280   21275   20950   pts/1   
21282       ps      R       21281   21275   20950   pts/1   

更新

我想,在许多系统上,默认的缓冲区大小可能是最大8192字节;让我们来创建一些愚蠢的长线吧!将system调用替换为

代码语言:javascript
复制
system("od /dev/urandom -t x8 -Anone | xargs -n256 | tr -d ' ' | xargs -n7 | head -n100 > input.txt");

结果7列,每行~29 in。输出是毫不犹豫的,相当于2.8mB的产出。

代码语言:javascript
复制
make -B  && ./test | wc 

在科里鲁现场看:

  • http://coliru.stacked-crooked.com/view?id=00160c8a2b5d4447cbda3866da3913aa-b8c04540b49ee47c13fd3df477ec213b

注意,在Coliru上,我们不能访问/dev/urandom__,这就是为什么我从二进制本身读取的原因。“随机”足以达到这一目的:)

票数 3
EN

Stack Overflow用户

发布于 2018-06-17 15:01:12

当我试图从与退出进程相对应的文件(因此该文件不再存在)读取时,g++ 4.5上始终会出现此错误。下面是一个测试用例:

代码语言:javascript
复制
#include <iostream>
#include <fstream>

int main(int argc, char** argv)
{
    if(argc!=2)
    {
        std::cerr << "Usage: " << argv[0] << " /proc/PID/filename\n";
        return 1;
    }
    std::ifstream file(argv[1]);
    if(!file)
    {
        std::cerr << "Failed to open the file\n";
        return 2;
    }
    std::cout << "Type anything and press <Return> to continue...\n";
    char c;
    std::cin >> c;
    int field;
    try
    {
        file >> field;
        if(file)
        {
            std::cout << "Successful read, field = " << field << "\n";
            return 0;
        }
        else
        {
            std::cout << "Non-exception read failure\n";
            return 3;
        }
    }
    catch(std::exception& ex)
    {
        std::cerr << "Exception: " << ex.what() << "\n";
        return 4;
    }
}

编译后,像这样进行测试:

代码语言:javascript
复制
$ sleep 0.5 & ./test /proc/$!/stat
[1] 8261
Type anything and press <Return> to continue...
d
Exception: basic_filebuf::underflow error reading the file: iostream error
[1]+  Done                    sleep 0.5

如果您将例如5传递给sleep,并在5秒的时间内键入恢复程序的内容,您将获得成功的阅读。

这是由于现在已经修复的libstdc++中的bug造成的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18088795

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档