gpt4 book ai didi

c - 为什么从伪终端读取失败?

转载 作者:IT王子 更新时间:2023-10-29 01:05:47 25 4
gpt4 key购买 nike

我已经从进程A创建了一个伪终端(/dev/pts/N),并且正在以一定的间隔向其中写入随机整数。我可以从screen打开该点,并检查其输出。
但是cat /dev/pts/N 失败:它无限地阻塞并且不返回。

我正在尝试使用open()/read()函数从另一个进程中读取它,而且read()从未返回。

int main(){
int source_fd = open("/dev/pts/4", O_RDONLY);

while(1){
char buffer[READ_BUFFER_SIZE] = {0};
char* buff_ptr = buffer;
int r = read(source_fd, (void*)buff_ptr, 1);
// !!!! never comes here
while(r > 0){
++buff_ptr;
r = read(source_fd, (void*)buff_ptr, 1);
}
}
}

最佳答案

简短的答案:您没有正确处理伪终端。通过从伪终端读取外部过程来观察奇怪甚至随机的结果是正常的;你不应该那样做。就像让两个人同时在同一个键盘上书写一样。 (仅因为您可以在某些电视节目中看到它,但这并不意味着它有意义。)

长答案:改变您的方法,您将获得更好的结果。

考虑您可以执行以下任务来使自己的伪终端行为更加生动:

  • 创建一个伪终端主机,并允许从机访问它

    (使用 posix_openpt() grantpt() unlockpt() 创建伪终端。使用 ptsname() 找出从属端的设备名称。)
  • fork 子进程。

    (使用 fork() 派生子进程,然后使用 setsid() 从控制终端分离。它还会创建一个新的进程组,因此您的主进程可以通过将信号发送到整个组来向该子进程启动的所有进程发送信号。 )
  • 在子进程中,打开标准输入(STDIN_FILENO)以从从属伪终端读取,并打开标准输出(STDOUT_FILENO)和标准错误(STDERR_FILENO)以写入伪终端的从属端。执行nano

    (使用 dup2() 将描述符复制到正确的位置,使用 close() 关闭多余的描述符,例如,使用 execlp("nano", "nano", NULL) 执行nano。请注意,第一个"nano"是nano命令的文件名,第二个是该命令看到的argv[0]参数。它不提供任何实际的命令行参数;就像您在自己喜欢的shell中运行nano一样。)
  • 现在,在父进程中,您可以读写伪终端的主端。

    请注意,您可能必须同时进行。没有办法知道什么时候可以/需要/必须阅读(更多),什么时候写可能会阻塞。

    我不能在这里过分强调全双工或不阻塞的重要性。如果您从未从伪终端读取数据,也不要指望它能工作。
  • 在父进程中,删除文件foobar.txt

    (使用 remove() unlink() 。)

    这样做是为了避免nano不会 pop “文件已存在”对话框。
  • 在父进程中,当读取任何输出时,从属进程可能会写入伪终端,
  • 等待一秒钟(当nano绘制编辑器屏幕时)
  • Some text和回车\r
  • 等待一刻,
  • 编写Ctrl + O(\017,通常可视为^O)
  • foobar.txt和回车\r
  • 等待一刻,
  • 编写Ctrl + X(\030,通常可视为^X),
  • 等待

  • nano应该退出。
  • 在父进程中,等待子进程(nano)退出。

    (为此使用循环和 waitpid() 。)

  • 如果完成上述操作,您的主终端控制程序将模拟一个运行很短的 nano session 的本地或远程“人”,只编写 Some text和换行符,将其保存到 foobar.txt并退出。 (该文件应包含 "Some text\n\n",因为 nano就是这样工作的。)

    如果您创建一个仅执行从主伪终端文件描述符读取的操作的辅助线程,则最容易实现步骤6。从非常清楚的意义上讲,它的作用就像是自动排水。毕竟,我们对这里向终端输出的 nano并不真正感兴趣。在第7步之后,您只需关闭该描述符,从而使帮助程序线程出错( read()返回-1, errno == EBADF返回),然后返回,因此主线程可以使用 pthread_join()来获取它。

    当然,您可以使用非阻塞I/O来实现步骤6。无论采取哪种方式,都必须始终从主伪终端获取 read(),并且在从属进程也向终端写入信息时,也不要因 write()陷入僵局。我敢打赌,这就是OP所处的困境。

    在上述情况下,流经伪终端的典型通信顺序为:
    Slave -> Master: "\e[?1049h\e[1;24r\e(B\e[m\e[4l\e[?7h\e[?12l\e[?25h"
    Slave -> Master: "\e[?1h\e=\e[?1h\e=\e[?1h\e="
    Slave -> Master: "\e[39;49m\e[39;49m\e(B\e[m\e[H\e[2J\e(B\e[0;7m"
    " GNU nano 2.2.6 "
    " New Buffer "
    "\e[23;1H^G\e(B\e[m Get Help "
    "\e(B\e[0;7m^O\e(B\e[m WriteOut "
    "\e(B\e[0;7m^R\e(B\e[m Read File "
    "\e(B\e[0;7m^Y\e(B\e[m Prev Page "
    "\e(B\e[0;7m^K\e(B\e[m Cut Text "
    "\e(B\e[0;7m^C\e(B\e[m Cur Pos"
    "\015\e[24d\e(B\e[0;7m^X\e(B\e[m Exit"
    "\e[14G\e(B\e[0;7m^J\e(B\e[m Justify "
    "\e(B\e[0;7m^W\e(B\e[m Where Is "
    "\e(B\e[0;7m^V\e(B\e[m Next Page "
    "\e(B\e[0;7m^U\e(B\e[m UnCut Text"
    "\e(B\e[0;7m^T\e(B\e[m To Spell\015\e[3d"
    Master -> Slave: "Some text\015"
    Slave -> Master: "\e[1;71H\e(B\e[0;7mModified\015\e[3d\e(B\e[mSome text\015\e[4d"
    Master -> Slave: "\017"
    Slave -> Master: "\e[22d\e(B\e[0;7mFile Name to Write: "
    " "
    " "
    "\e[23;14H\e(B\e[m "
    "\e(B\e[0;7mM-D\e(B\e[m DOS Format "
    "\e(B\e[0;7mM-A\e(B\e[m Append "
    "\e(B\e[0;7mM-B\e(B\e[m Backup File"
    "\e[24;2H\e(B\e[0;7mC\e(B\e[m Cancel "
    "\e(B\e[0;7mM-M\e(B\e[m Mac Format "
    "\e(B\e[0;7mM-P\e(B\e[m Prefix\e[K\e[22;21H"
    Master -> Slave: "foobar.txt\015"
    Slave -> Master: "\e[1;31H\e[39;49m\e(B\e[0;7mFile: foobar.txt"
    "\e[1;71H \e[22;31H\e(B\e[m\e[1K "
    "\e(B\e[0;7m[ Wrote 2 lines ]"
    "\e(B\e[m\e[K\e[23;14H\e(B\e[0;7m^O\e(B\e[m WriteOut "
    "\e(B\e[0;7m^R\e(B\e[m Read File "
    "\e(B\e[0;7m^Y\e(B\e[m Prev Page "
    "\e(B\e[0;7m^K\e(B\e[m Cut Text "
    "\e(B\e[0;7m^C\e(B\e[m Cur Pos"
    "\e[24;2H\e(B\e[0;7mX\e(B\e[m Exit "
    "\e(B\e[0;7m^J\e(B\e[m Justify "
    "\e(B\e[0;7m^W\e(B\e[m Where Is "
    "\e(B\e[0;7m^V\e(B\e[m Next Page "
    "\e(B\e[0;7m^U\e(B\e[m UnCut Text"
    "\e(B\e[0;7m^T\e(B\e[m To Spell\015\e[4d"
    Master -> Slave: "\030"
    Slave -> Master: "\e[23d\e[J\e[24;80H"
    Slave -> Master: "\e[24;1H\e[?1049l\015\e[?1l\e>"

    其中 \e\033\x1B的简写,即。 ASCII ESC字符。

    尤其要注意,从属 nano进程如何生成各种输出,只是为了绘制精美的编辑器屏幕。如果某个时钟或某个时钟定期更改,它将基本上每秒产生一次这些更新。

    Master-> Slave使用 \r而不是 \n作为换行符的原因是默认的 termios设置。

    关于c - 为什么从伪终端读取失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29485718/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com