Cannot send data larger than 64 KB with pipes in C - c

I am trying to send usr/share/dict/words file (which is around 971 KB) from parent to two child processes via named pipes. However, output is empty and I did not get any errors.I waited for a minute but nothing changed. Program works fine with 10KB of data that I tried with. I know that pipe buffer size is 64KB but can someone explain what is wrong with my approach:
Here in the code:
- ptr_child_1 refers to shared memory pointer, 1 MB of shared segment mapped.
- fd and sd are pipes, file is the file pointer
Code is long that is why I did not paste all of them. I just wonder how I should send that large file
Parent write:
mknod(FIFO_1, S_IFIFO | 0666, 0);
mknod(FIFO_2, S_IFIFO | 0666, 0);
// open pipes
int fd = open(FIFO_1, O_WRONLY);
int sd = open(FIFO_2, O_WRONLY);
char str[3000];
while(fgets(str, sizeof(str), file) > 0)
{
// write all lines of file
write(fd, str, strlen(str));
write(sd, str, strlen(str));
}
// send EOF explicitly
write(fd, "\0", 1);
write(sd, "\0", 1);
// close file and pipes
close(fd);
close(sd);
fclose(file);
Child read:
int num;
char s[3000];
while((num = read(fd, s, sizeof(s))) > 0)
{
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += num;
}
// close pipe
close(fd);

Related

Fail to read/write struct to fifo recursively in C

In an exercise problem, I am required to build a client program (write first) that opens a .txt file, put each line and the total bytes of each line into a struct variable and then send it out to the server program (read first). Right after this is done, the client program will also receive a struct file (similarly only has char * and int attributes) from the server program.
// Below are global variables in both programs
#define BUFSIZE 1024
struct info_pack
{
char line[BUFSIZE]; // the line to receive messages
int bytes; // the bytes of data transferred
};
char fifo_path[] = "./my_fifo";
struct info_pack info_w; // the info_pack for writing each line in text.txt
struct info_pack info_r; // the info_pack for reading feedback info_pack sent from the server program
First is the client program:
// the main() in the client program
int main()
{
int fd;
int i = 0, index = 1, bytes = 0, line_length, fifo_read;
char *file_path = "/home/text.txt";
FILE *fd2;
mkfifo(fifo_path, 0666);
if ((fd2 = fopen(file_path, "r")) < 0)
{
perror("Opening file");
return -1;
}
else
{
printf("Successfully open the target file\n");
while (fgets(info.line, BUFSIZE, fd2) != NULL)
// the "segmentation fault" error appears right after this line
{
info_w.bytes = strlen(line);
fd = open(fifo_path, O_WRONLY);
printf("The %d th line sent out is: %s\n%d bytes are sent\n\n",
index, info_w.line, info_w.bytes);
write(fd, &info_w, sizeof(info_w) + 1);
close(fd);
fd = open(fifo_path, O_RDONLY);
fifo_read = read(fd, &info_r, sizeof(info_r));
close(fd);
if (fifo_read > 0)
{
printf("Feedback: %s\nand %d bytes are returned\n", info_r.line, info_r.bytes);
}
}
printf("All data is successfully transfered\n");
}
return 0;
}
Then is the server program
// the main() in the server program
int main()
{
int fd, fifo_read;
int line_length;
char *feedback = "SUCCESS";
strcpy(info_w.line, feedback);
info_w.bytes = strlen(feedback);
// define a constant info_pack variable to send to the client program
if (mkfifo(fifo_path, 0666) < 0)
{
perror("client end: ");
exit(-1);
}
while (1)
// This server program will wait for any one single client's message
// This server program can only be terminated by manually input signals (like ^\)
{
fd = open(fifo_path, O_RDONLY);
printf("waiting for client's message\n");
fifo_read = read(fd, &info_r, sizeof(info_r));
close(fd);
if (fifo_read > 0)
// if receive the struct variable, print all of its attributes
{
if (info_r == NULL)
printf("Found no lines sent from the client\n");
else
printf("Read from fifo:\n %s\n(in info)%d bytes read (actually)%d bytes read\n", info_r.line, info_r.bytes, fifo_read);
}
else
{
sleep(1);
printf("Fail to read data from the client\n");
}
// Because of the error in client program this server program
// always pause here
fd = open(fifo_path, O_WRONLY);
printf("Now writing feedback to the client\n");
write(fd, info_w, sizeof(info_w));
close(fd);
}
}
Could anyone explain why the segmentation fault error appears in the client program? Then I can test if the both the client and the server can co-op properly.By the way, I read this post already but, in this post, it is a one-time data stream and I cannot find any hints in it.

named pipe blocks on writing before its maximum size

I try to fill a named pipe (created by mkfifo /tmp/pipe) by writing to it 3 bytes at a time until the write() function blocks.
On my system, a pipe seems to be limited to 16 pages of 4096 bytes. Thus the pipe can contain 65536 bytes.
I do that with the following C code:
int main ()
{
pid_t child;
child = fork ();
if (child == 0)
{
ssize_t ret;
ssize_t total = 0;
unsigned char *datat = malloc (65536);
assert (datat != NULL);
int fd = open ("/tmp/pipe", O_WRONLY);
assert (fd != -1);
while (1)
{
printf ("Trying writting\n");
ret = write (fd, datat, 3);
assert (ret != -1);
total += ret;
printf ("write : %ld.\n", total);
}
}
else
{
int fd = open ("/tmp/pipe", O_RDONLY);
assert (fd != -1);
while (1); //prevent closing the pipe.
}
return 0;
}
By this way, I succeed to fill the pipe until 65520 bytes. I don't understand why 65520 and not 65536 (or 65535 if we consider that 65536 is not a multiple of 3).
Then I tried to write 65520 bytes and, after, write 3 bytes:
int
main (int argc, char *argv[])
{
pid_t child;
child = fork ();
if (child == 0)
{
ssize_t ret;
ssize_t total = 0;
unsigned char *datat = malloc (65536);
assert (datat != NULL);
int fd = open ("/tmp/pipe", O_WRONLY);
assert (fd != -1);
while(1)
{
printf ("Trying writting\n");
ret = write (fd, datat, 65520);
assert (ret != -1);
total += ret;
printf ("Trying writting\n");
ret = write (fd, datat, 3);
assert (ret != -1);
total += ret;
printf ("write : %ld.\n", total);
}
}
else
{
int fd = open ("/tmp/pipe", O_RDONLY);
assert (fd != -1);
while (1); //prevent closing the pipe.
}
return 0;
}
I expected the second write to block, however it was not the case and I wrote 65523 bytes.
The question is: why can't I write more than 65520 bytes on the first case whereas I can in the second?
EDIT:
More information :
My Operating system is Linux archlinux 4.16.5-1-ARCH
man 7 pipe give information about the size (which is equal to 65536 bytes) of the pipe and is confirmed by fcntl:
int
main (int argc, char *argv[])
{
int fd = open ("/tmp/pipe", O_WRONLY);
printf ("MAX : %d\n", fcntl (fd, F_GETPIPE_SZ));
return 0;
}
It's because of the way 4KB pages are filled with written data in the pipe implementation in the Linux kernel. More specifically, the kernel appends written data to a page only if the data fits entirely in the page, otherwise puts the data into another page with enough free bytes.
If you write 3 bytes at a time, the pipe pages won't be filled at their full capacity, because the page size (4096) is not a multiple of 3: the nearest multiple is 4095, so each page will end up with 1 "wasted" byte. Multiplying 4095 by 16, which is the total number of pages, you get 65520.
In your second use case, when you write 65520 bytes all at once, you are filling 15 pages entirely (61440 bytes), plus you are putting the remaining 4080 bytes in the last page, which will have 16 bytes still available for subsequent writes: that's why your second write() call with 3 bytes succeeds without blocking.
For full details on the Linux pipe implementation, see https://elixir.bootlin.com/linux/latest/source/fs/pipe.c.

Infinite Loop While Reading and Writing from Files - C

I'm trying to read from an input file and ultimately reverse the buffer it reads from and write it to an output file. For now though, I'm testing to see if a buffer I read from would even make it to the output file, and so far it isn't and I'm getting an infinite loop. The buffer should read in PAGESIZE bytes (from a call to sysconf()) and if the file output is larger than the buffer, then the buffer should be written to the output file first then be flushed and reused again to get the rest of the input until the file descriptor returns 0 for no data left. This is what I have so far:
int fdRead = open(inputFile, O_RDONLY);
if (fdRead == -1)
err_sys("Error reading input file '%s', check spelling?\n", inputFile);
int fdWrite = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); //overwrites file if it exists
if (fdWrite == -1)
err_sys("Error creating output file '%s'\n", outputFile);
while (1) {
read(fdRead, buf, size);
if (fdRead == 0)
break;
if (fdRead == -1)
err_sys("Error reading from input file '%s'\n", inputFile);
lseek(fdRead, size, SEEK_CUR);
if (fdRead == -1)
err_sys("Error reading from input file '%s'\n", inputFile);
write(fdWrite, buf, size);
if (fdWrite == -1)
err_sys("Error writing to output file '%s'\n", outputFile);
lseek(fdWrite, size, SEEK_CUR);
if (fdWrite == -1)
err_sys("Error writing to output file '%s'\n", outputFile);
memset(buf, '\0', size);
}
close(fdRead);
close(fdWrite);
I suppose that fdRead is never returning 0, and thus not exiting the loop. My question is how do I fix that?
p.s: size is the call from sysconf() that gets the PAGESIZE, e.g
size = sysconf(_SC_PAGESIZE);
And inputFile and outputFile are both char * and I've tested that they return and store good strings.
Transcribing comments into an answer.
You need to capture the return value from read() — you're ignoring it and testing whether the file descriptor is 0 or negative after the read().
So if I did something like int bytesRead then tested for if (bytesRead == 0) instead of if (fdRead == 0), then that should solve my problem?
Yes, you need something like:
int nbytes = read(fdRead, buf, size);
if (nbytes <= 0) break;
You should use the positive nbytes in the write() call; you might not get all size bytes filled by the read().
Testing the file descriptor after the read is wrong (it won't have changed under normal circumstances), and ignoring the value returned by read() is wrong, and not using the value returned by read() in the call to write() is wrong.
OK so it should be write(fdWrite, buf, nbytes)?
Yes, it should be
int obytes;
if ((obytes = write(fdWrite, buf, nbytes)) != nbytes)
{
…oops — short write …
}
You get to decide what's the appropriate response to a short write (a positive value, but not the number of bytes you expected to write). If you're writing to a socket, it might be appropriate to try writing the unwritten section of the data again (that's why obytes is used to capture the number of bytes successfully written). If you're writing to a disk file, it probably means there's no space left, so there's no point (little point) in trying again. If obytes is negative, you've had a write error; there is usually little point in trying to continue.
This all has helped out a lot and it seems to be working okay. I've only run into one other problem. I tested this on a large file (Alice in Wonderland text file) and the output file is almost the whole thing, but cuts off the last two paragraphs or so. …
You need to review why you have the lseek() operations in the code. Neither of them should be necessary, and both are dubious. I think the lseek() on fdRead() means you miss chunks of text of size bytes each; I think the lseek() on fdWrite() means you insert size null bytes into the output file.
/*Here's the code which may help for that.i havemodified in it for your need.*/
int fdRead = open(inputFile, O_RDONLY);
int Ret_Val;
if (fdRead == -1)
err_sys("Error reading input file'%s',check spelling?\n",
inputFile );
int fdWrite = open(outputFile, O_WRONLY | O_CREAT
| O_TRUNC, 0644); //overwrites file if it exists
if (fdWrite == -1)
err_sys("Error creating output file '%s'\n", outputFile);
while (1) {
Ret_Val=read(fdRead, buf, size);
if (Ret_Val == 0)
break;
if (Ret_Val == -1)
err_sys("Error reading from input file'%s'\n",inputFile);
lseek(fdRead, size, SEEK_CUR);
if (Ret_Val == -1)
err_sys("Error reading from input file'%s'\n",
inputFile );
Ret_Val=write(fdWrite, buf, size);
if (Ret_Val == -1)
err_sys("Error writing to output file'%s'\n",outputFile);
lseek(fdWrite, size, SEEK_CUR);
if (Ret_Val == -1)
err_sys("Error writing to output file'%s'\n",outputFile);
memset(buf, '\0', size);
}
close(fdRead);
close(fdWrite);

Read all characters written in FIFO using open() system call

I have a FIFO pipe, which is opened at both ends using open() in O_RDWR mode. At the reading end, read() is not reading all the characters, but lesser than that specified in the call. Is there a way to ensure that all characters are read using open()?
Thanks in advance
if (p != NULL){
printf("Inside p not null!\n");
if((fd = open(p, O_RDWR)) < 0){
perror("File could not be opened!");
exit(EXIT_FAILURE);
}
//FILE *rdptr = fopen(p,"r");
memset(buf,0,file_len);
rc = read(fd, buf, file_len);
printf("Number of bytes read: %d\n", rc);
printf("Data detected on FIFO\n");
buf[rc] = '\0';
char base[20] = "output.txt";
char name[20];
sprintf(name, "%d%s", suffix, base);
FILE *fptr = fopen(name,"ab+");
fd_wr = open(name,O_WRONLY);
charnum = write(fd_wr,buf,rc);
kill(id_A, SIGKILL);
//printf("No. of characters written: %d\n",charnum);
//FD_CLR(fd, &rdfs);
}
First minor comment: you should use O_RDONLY to open the file: don't use more permissions than necessary.
Second issue: if file_len is very large, it's possible that the writer has blocked trying to write the entire chunk of data (since a FIFO can only hold a certain amount of unread data). If that's the case, then read will only read the data that has been stored in the FIFO, and will immediately return with whatever it could read. This will allow the writer to write more bytes, which will then be read in the next read.
You should loop reads, adjusting an offset into the buffer, until the entire file_len bytes are read. Something like this:
size_t offset = 0;
while(offset < file_len) {
rc = read(fd, buf+offset, file_len-offset);
if(rc < 0) {
/* handle I/O error or something... */
} else {
offset += rc;
}
}

Linux C write buffer to file from a while loop issue

I have the following function (that dumps a process memory region). If I write to stdout write(STDOUT_FILENO, buf, rd); it outputs the buffer correctly, the problem rises when I want to write the buffer to a file, the file gets written but with the same date over and over:
void dump_region(int fd, off64_t start, off64_t end)
{
char buf[4096];
int fdo;
fdo = open("memdump_log", O_WRONLY | O_CREAT, 0644);
if (fdo == -1) {
fprintf(stderr, "open failed: %m\n");
close(fd);
exit(1);
}
lseek64(fd, start, SEEK_SET);
while(start < end) {
int rd;
rd = read(fd, buf, 4096);
write(fdo, buf, rd);
//write(STDOUT_FILENO, buf, rd);
start += 4096;
}
close(fdo);
}
The function is accessed from main() like this:
if(maps && mem != -1) {
char buf[BUFSIZ + 1];
while(fgets(buf, BUFSIZ, maps)) {
off64_t start, end;
sscanf(buf, "%llx-%llx", &start, &end);
dump_region(mem, start, end);
}
}
Any idea where am I wrong?
Modify
fdo = open("memdump_log", O_WRONLY | O_CREAT, 0644);
into
fdo = open("memdump_log", O_WRONLY | O_CREAT | O_APPEND, 0644);
You need to seek to the end of your output file, or passing the O_APPEND to open
You keep reopening the output file on every call to dump_region. When opening a file it will always start writing at the start. Either keep the file open all the time, seek to the end, or try the O_APPEND flag.

Resources