在 write() 写入的字节数少于请求的字节数后,如何让线程继续?

Posted

技术标签:

【中文标题】在 write() 写入的字节数少于请求的字节数后,如何让线程继续?【英文标题】:How to get a thread to continue after write() has written less bytes than requested? 【发布时间】:2009-05-24 18:51:24 【问题描述】:

我正在使用以下代码通过命名管道将数据从一个应用程序写入另一个应用程序。永远不要退出写入发生的线程。但是,如果 r_write() 返回的值少于应有的值,则线程/程序会因某种原因停止。一旦 write 返回的返回值少于应有的值,如何使线程继续?

ssize_t r_write(int fd, char *buf, size_t size) 

   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;

   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) 
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten) == -1 && (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   
   return totalbytes;





void* sendData(void *thread_arg)

    int fd, ret_val, count, numread;
    string word;
    char bufpipe[5];

    ret_val = mkfifo(pipe, 0777); //make the sprout pipe

    if (( ret_val == -1) && (errno != EEXIST)) 
    
        perror("Error creating named pipe");
        exit(1);
       
    while(1)
    
        if(!sproutFeed.empty())
               
            string s;
            s.clear();
            s = sproutFeed.front();

            int sizeOfData = s.length();
            snprintf(bufpipe, 5, "%04d", sizeOfData);

            char stringToSend[strlen(bufpipe) + sizeOfData +1];
            bzero(stringToSend, sizeof(stringToSend));                  
            strncpy(stringToSend,bufpipe, strlen(bufpipe));         
            strncat(stringToSend,s.c_str(),strlen(s.c_str()));
            strncat(stringToSend, "\0", strlen("\0"));                  
            int fullSize = strlen(stringToSend);
            cout << "sending string" << stringToSend << endl;
            fd = open(pipe,O_WRONLY);
            int numWrite = r_write(fd, stringToSend, strlen(stringToSend) );
            if(numWrite != fullSize)
            
                bzero(bufpipe, strlen(bufpipe));
                bzero(stringToSend, strlen(stringToSend));
                cout << "NOT FULL SIZE WRITE " << endl; //program ends here??
            
            else
            
                    sproutFeed.pop();
                    bzero(bufpipe, strlen(bufpipe));
                    bzero(stringToSend, strlen(stringToSend));
                               
        
        else
        
            sleep(1);
        
    


【问题讨论】:

您的代码片段没有显示管道名称是如何确定的(显然,名称是堆栈中的随机垃圾)。此外,我认为没有任何理由使 FIFO 可执行 - 如果必须,请使用 0666 权限,但在上下文中,0600 可能是明智的。由于您也没有展示管道阅读器是如何创建的,因此我们无法轻易判断这是否存在问题;可能不是,但是.. 此外,您在循环的每次迭代中打开 FIFO,并且永远不会关闭文件描述符。这会让读者感到困惑,并尽早而不是稍后在文件描述符之外运行您的程序。由于选择的选项,第一次打开会阻塞,直到有进程读取 FIFO;但是,有可能该进程将继续等待 EOF,因为 FIFO 保持打开状态,所以该 EOF 永远不会到来。 writer 中的后续 open 调用都将立即返回,因为 reader 已经存在。 【参考方案1】:

如果write() 为写入的字节数返回正值(非零、非负),则表示成功,但没有空间容纳所有数据。再试一次,从缓冲区写入剩余的数据(并根据需要重复)。不要忘记,FIFO 的容量是有限的 - 如果需要,写入器会被阻止。

如果write() 返回负值,则写入失败。您可能无法恢复,但请查看errno 了解原因。

我认为write() 可以返回零的唯一情况是,如果您使用O_NONBLOCK 打开文件描述符并且尝试写入会阻塞。您可能需要仔细查看 write() 的手册页以检查是否有其他可能性。

然后,您的线程会做什么取决于它经历短写的原因,以及您想对此做什么。

【讨论】:

【参考方案2】:

写入 FIFO 失败。调查errno 的值以找出原因。查看系统上的errno.h 以破译errno 的值。如果程序在尝试写入控制台时结束,原因可能是相关的。

此外,您的循环似乎并未关闭 FIFO (close(fd)) 的文件描述符。

最后,您提到了多线程。您系统上的标准库流cout 可能不是(也可能不是)线程安全的。在这种情况下,从多个线程同时写入控制台会导致不可预知的错误。

【讨论】:

我在 write 的调用下添加了这一行。 if(errno == EPIPE) 信号(SIGPIPE,SIG_IGN);现在它将传输更多数据,但最终它仍会在写入时收到损坏的管道信号。还有另一种方法可以完全忽略所有损坏的管道错误吗?我找到了一种通过套接字而不是文件的方法。【参考方案3】:

您需要使文件描述符非阻塞。你可以这样做:

fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

说明

这就是 fcntl 的工作原理(不是完整的描述 - 请查看 man fcntl )。首先,包括:

#include <unistd.h>
#include <fcntl.h>

读取文件描述符的标志

使用F_GETFL 获取文件描述符的标志。来自man fcntl

 F_GETFL
              读取文件描述符的标志。

返回值
       对于成功的调用,返回值取决于操作:

       F_GETFL 标志值。

这就是它的使用方式:

int fd_flags = fcntl(fd, F_GETFL);

写入文件描述符的标志

使用F_SETFL 设置O_NONBLOCK 标志。再次引用man fcntl

 F_SETFL
              将描述符标志的文件状态标志部分设置为
              由 arg 指定的值。剩余位(访问模式、文件 cre?
              arg 中的标志)被忽略。在 Linux 上,这个命令可以
              仅更改 O_APPEND、O_NONBLOCK、O_ASYNC 和 O_DIRECT
              标志。

这就是它的使用方式:

fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);

【讨论】:

以上是关于在 write() 写入的字节数少于请求的字节数后,如何让线程继续?的主要内容,如果未能解决你的问题,请参考以下文章

C: read/write

文件IO详解---write函数详解

QTcpSocket::write - 如何写入大文件?

read()和write()

read和write函数

SylixOS write 0字节问题