是否可以在 C 中使用相同的文件描述符进行读写
is it possible to read and write with the same file descriptor in C
我正在尝试写入文件并显示我用另一个进程写入的内容的输出。我想出的代码:
void readLine (int fd, char *str) {
int n;
do {
n = read (fd, str, 1);
} while (*str++ != '[=10=]');
}
int main(int argc,char ** argv){
int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
if(fork()==0){
char buf[1000];
while(1) {
readLine(fd,buf);
printf("%s\n",buf);
}
}else{
while(1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
}
}
我想要的输出(每个结果彼此间隔一秒):
abcd
abcd
abcd
....
不幸的是,这段代码不起作用,似乎子进程(文件“sharedFile”的reader)从文件中读取垃圾,因为即使文件为空,它也会以某种方式读取值。
尝试调试代码时,readLine
函数从未正确读取写入的文件,它始终读取 0 字节。
有人可以帮忙吗?
首先,当文件描述符在分叉后共享时,parent 和 child 都指向相同的 打开文件描述符 ,这特别意味着它们共享相同的文件位置。 fork()
手册页对此进行了解释。
所以每当 parent 写入时,位置都会更新到文件末尾,因此 child 总是尝试在文件末尾读取,那里没有数据.这就是为什么 read()
returns 0,就像你到达文件末尾时一样正常。
(发生这种情况时,您不应尝试对缓冲区中的数据执行任何操作。这并不是说您在“阅读垃圾”,而是您没有阅读任何内容 但是然后假装缓冲区中的任何垃圾都是你刚刚读到的。特别是你的代码完全无视 read()
中的 return 值,这就是你应该告诉什么的方式你真的读过。)
如果你想让child有一个独立的文件位置,那么child需要为自己单独open()
文件并得到一个指向新文件的新fd说明。
但是,当 child 读取了当前文件中的所有数据时,read()
将再次 return 0;它不会等待 parent 写更多。一些其他进程打开文件进行写入这一事实不会影响 read()
在常规文件上的语义。
所以你需要做的是,当read()
returns 0时,你手动休眠一段时间,然后再试一次。当文件中有更多数据时,read()
将 return 为正数,然后您可以处理读取的数据。或者,有更优雅但更复杂的方法使用 system-specific API,如 Linux 的 inotify
,它可以休眠直到文件内容更改。您可能熟悉 tail -f
,它在不同的系统上结合使用了这些方法。
另一个危险的错误是,如果其他人将文本写入不包含预期的空字节的文件,您的 child 将读取比缓冲区所能容纳的更多数据,从而使缓冲区溢出。这可能是一个可利用的安全漏洞。
这是修复这些错误并适用于我的代码版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void readLine (int fd, char *str, size_t max) {
size_t pos = 0;
while (pos < max) {
ssize_t n = read(fd, str + pos, 1);
if (n == 0) {
sleep(1);
} else if (n == 1) {
if (str[pos] == '[=10=]') {
return;
}
pos++;
} else {
perror("read() failure");
exit(2);
}
}
fprintf(stderr, "Didn't receive null terminator in time\n");
exit(2);
}
int main(int argc, char ** argv){
int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
if (fd < 0) {
perror("parent opening sharedFile");
exit(2);
}
pid_t pid = fork();
if (pid == 0){
int newfd = open("sharedFile", O_RDONLY);
if (newfd < 0) {
perror("child opening sharedFile");
exit(2);
}
char buf[1000];
while (1) {
readLine(newfd, buf, 1000);
printf("%s\n",buf);
}
} else if (pid > 0) {
while (1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
} else {
perror("fork");
exit(2);
}
return 0;
}
我正在尝试写入文件并显示我用另一个进程写入的内容的输出。我想出的代码:
void readLine (int fd, char *str) {
int n;
do {
n = read (fd, str, 1);
} while (*str++ != '[=10=]');
}
int main(int argc,char ** argv){
int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
if(fork()==0){
char buf[1000];
while(1) {
readLine(fd,buf);
printf("%s\n",buf);
}
}else{
while(1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
}
}
我想要的输出(每个结果彼此间隔一秒):
abcd
abcd
abcd
....
不幸的是,这段代码不起作用,似乎子进程(文件“sharedFile”的reader)从文件中读取垃圾,因为即使文件为空,它也会以某种方式读取值。
尝试调试代码时,readLine
函数从未正确读取写入的文件,它始终读取 0 字节。
有人可以帮忙吗?
首先,当文件描述符在分叉后共享时,parent 和 child 都指向相同的 打开文件描述符 ,这特别意味着它们共享相同的文件位置。 fork()
手册页对此进行了解释。
所以每当 parent 写入时,位置都会更新到文件末尾,因此 child 总是尝试在文件末尾读取,那里没有数据.这就是为什么 read()
returns 0,就像你到达文件末尾时一样正常。
(发生这种情况时,您不应尝试对缓冲区中的数据执行任何操作。这并不是说您在“阅读垃圾”,而是您没有阅读任何内容 但是然后假装缓冲区中的任何垃圾都是你刚刚读到的。特别是你的代码完全无视 read()
中的 return 值,这就是你应该告诉什么的方式你真的读过。)
如果你想让child有一个独立的文件位置,那么child需要为自己单独open()
文件并得到一个指向新文件的新fd说明。
但是,当 child 读取了当前文件中的所有数据时,read()
将再次 return 0;它不会等待 parent 写更多。一些其他进程打开文件进行写入这一事实不会影响 read()
在常规文件上的语义。
所以你需要做的是,当read()
returns 0时,你手动休眠一段时间,然后再试一次。当文件中有更多数据时,read()
将 return 为正数,然后您可以处理读取的数据。或者,有更优雅但更复杂的方法使用 system-specific API,如 Linux 的 inotify
,它可以休眠直到文件内容更改。您可能熟悉 tail -f
,它在不同的系统上结合使用了这些方法。
另一个危险的错误是,如果其他人将文本写入不包含预期的空字节的文件,您的 child 将读取比缓冲区所能容纳的更多数据,从而使缓冲区溢出。这可能是一个可利用的安全漏洞。
这是修复这些错误并适用于我的代码版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void readLine (int fd, char *str, size_t max) {
size_t pos = 0;
while (pos < max) {
ssize_t n = read(fd, str + pos, 1);
if (n == 0) {
sleep(1);
} else if (n == 1) {
if (str[pos] == '[=10=]') {
return;
}
pos++;
} else {
perror("read() failure");
exit(2);
}
}
fprintf(stderr, "Didn't receive null terminator in time\n");
exit(2);
}
int main(int argc, char ** argv){
int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
if (fd < 0) {
perror("parent opening sharedFile");
exit(2);
}
pid_t pid = fork();
if (pid == 0){
int newfd = open("sharedFile", O_RDONLY);
if (newfd < 0) {
perror("child opening sharedFile");
exit(2);
}
char buf[1000];
while (1) {
readLine(newfd, buf, 1000);
printf("%s\n",buf);
}
} else if (pid > 0) {
while (1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
} else {
perror("fork");
exit(2);
}
return 0;
}