【问题标题】:What am I doing wrong with read() and write()?我在 read() 和 write() 上做错了什么?
【发布时间】:2016-03-18 21:27:18
【问题描述】:

我正在尝试进行一些初学者内核模块/用户级程序通信。我之前得到了使用fdopen() 的建议,效果很好,但我发现我需要使用open()read()write()。我阅读了这些手册页,并认为我将 fopen、fgets、fputs 正确转换为这些,并且我的程序可以编译,但我没有得到所需的输出。

我有一个计时器,如果我输入./userprogram -s (int) (name),例如./userprogram -s 5 hello,5 秒后它将通过与我的内核模块通信将hello 打印到控制台。在我切换到这些新功能后,它会打印 /lib/ld-uClibc.so.0 (并且现在似乎要等待大约 5 秒)。我是否还需要更改内核级代码的工作方式?我以为我只是能够更改用户级程序,内核模块将继续像以前一样工作。这是我尝试使用注释掉的原始代码:

// open file
int pFile;
pFile = open("/dev/mytimer", O_RDWR);
if (pFile < 0) {
    fprintf (stderr, "mytimer module isn't loaded\n");
    return 1;
}

// Check if timer set
if (argc >= 4 && strcmp(argv[1], "-s") == 0) {
    lenNum = strlen(argv[2]);
    lenName = strlen(argv[3]);
    char *ptr = malloc(lenNum+lenName+4);
    strncat(ptr, argv[1], 2);//flag
    strncat(ptr," ", 1);
    strncat(ptr, argv[2], lenNum);//timer length
    strncat(ptr," ", 1);
    strncat(ptr, argv[3], lenName);//message

    /* fputs(ptr, pFile); */
    write(pFile, ptr, sizeof(ptr));
    /*
    while (fgets(line, 256, pFile) != NULL) {
        printf("%s", line);
    } */
    while (read(pFile, ptr, sizeof(ptr)) != 0) {
        printf("%s", line);
    }   

欢迎提出任何建议。

【问题讨论】:

  • 你已经在使用open(),至少,如果你是通过fdopen()获取流的话。
  • 我想我正在做类似int file = open() 然后FILE *pFile = fdopen(file, "r+"); 然后在filefgets() 上使用open()fgets()fputs()pFile 上。
  • fdopen() 将流与打开的文件描述符相关联。您必须以某种方式获取文件描述符,通常是通过open()。你当然可以fdopen() 标准文件号之一,但这毫无意义,因为你已经免费获得了stdinstdoutstderr
  • 您在分配内存以将其复制到时没有考虑argv[1] 的长度。但更糟糕的是strncat(ptr, argv[1], 2);,因为*ptr 不是一个初始字符串。 malloc 不初始化内存它只分配它。
  • 您需要检查来自 write()read()(以及所有系统调用)的返回码是否有错误(并报告它们),特别是因为你有问题。顺便说一句,很高兴看到有人使用 K&R 风格进行改变。

标签: c linux file-io unistd.h


【解决方案1】:

[我]认为我正确地将 fopen、fgets、fputs 转换为这些

...但你错了。没有基于write() 的单行等效于fputs(),也没有基于read() 的单行等效于fgets()。当您使用低级 read()s 和 write()s 时,基于流的 I/O 函数会完成很多您必须自己完成的工作。

其中一些区别是:

  • read() 不提供字符串终止。你必须自己做。
  • write() 不注意字符串终止。如果您希望它在字符串终止符处停止,那么您必须通过您要求它传输的字节数来控制它。
  • read()write() 都不能保证在一次调用中传输请求的全部字节数。如果要传输特定数量的字节,则必须准备循环。
  • read() 不会自动停在任何特定字符处,包括换行符
  • 大多数流都提供缓冲,对您来说是不可见的。如果你想缓冲你的read()s 和write()s,那么你必须自己处理它(但这些函数非常适合它)。

但是,在您的特定情况下,您还犯了语义错误。 write()read() 的第三个参数是要传输的最大字节数——通常是缓冲区的大小——但 sizeof(ptr) 是指针本身的大小,而不是要传输的空间的大小它指向的。

【讨论】:

  • 我以为我读到fputs() 不会终止但puts() 会?我想我也可能读错了。我对你的第三个要点感到困惑,你能再解释一下吗?我按照@Jens 的建议将sizeof() 更改为strlen(),它似乎解决了部分问题,但我仍然添加了一些奇怪的字符。
  • @Austin puts() 在字符串内容后输出换行符,fputs() 没有,但都注意字符串终止。否则,您首先不能谈论“字符串内容”。字符串终止符告诉他们两个内容的结束位置。另一方面,write() 函数不会根据您提供给它的字节值进行区分。
【解决方案2】:

sizeof(ptr) 可能是 4 或 8(即sizeof(char *)),而不是您要写入的数据的长度。

而且构建数据的方式非常繁琐。为什么不使用单个sprintf(),或者,如果有的话,asprintf(),它甚至可以分配内存:

char *ptr;
int i;

i = asprintf (&ptr, "%s %s %s", argv[1], argv[2], argv[3]);
write (pFile, ptr, i);
free(ptr);

是不是又甜又简洁?

【讨论】:

  • @Austin 这是对许多 Unix 系统上的 ISO C 和 POSIX 的扩展。
  • @Jens,它简洁明了,是的,但并不完全正确。它不考虑短 write() 的可能性。它也不做任何错误检查,但在这方面它与 OP 的尝试是平行的。
  • write()短了有什么问题?
  • @Austin 短写入意味着写入的字节数少于 i。一个健壮的程序会使用 write() 的返回值并检查写入是否很短,然后尝试写入剩余的字节,直到所有字节都被写入。或者它可能只是发出关于短写的警告。通常短写很少见,但在某些情况下,例如文件系统已满,消息对于套接字来说太长,它们就会发生。
猜你喜欢
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2021-03-24
  • 2019-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多