Linux pipe(): Reading from a pipe doesn't always unblock writers - c

I have a problem using pipe under Linux. I would like to fill a pipe to make further write's call blocking. An other process should be able to read some characters from the pipe that should allow the other process to write.
The example code:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int size = 65535;
int total = 0;
// Create the pipe
if(pipe(pipefd) == -1)
{
perror("pipe()");
exit(EXIT_FAILURE);
}
// Fill in (almost full = 65535 (full - 1 byte))
while(total < size)
{
write(pipefd[1], &total, 1);
total++;
}
// Fork
switch(fork())
{
case -1:
perror("fork()");
exit(EXIT_FAILURE);
case 0:
// Close unused read side
close(pipefd[0]);
while(1)
{
// Write only one byte, value not important (here -> total)
int ret = write(pipefd[1], &total, 1);
printf("Write %d bytes\n", ret);
}
default:
// Close unused write side
close(pipefd[1]);
while(1)
{
int nbread;
scanf("%4i", &nbread);
char buf[65535];
// Read number byte asked
int ret = read(pipefd[0], buf, nbread);
printf("Read %d bytes\n", nbread);
}
}
return 0;
}
I don't understand the behavior below. The process write one last time because I didn't fill the pipe completely, normal. But afterwards, the write is blocking (pipe full) and any read should unblock the waiting write call.
test#pc:~$./pipe
Write 1 bytes
4095
Read 4095 bytes
1
Read 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
...
Instead, the write call is unblocked only after having read 4096 bytes... WHY????
Normally, after a read success of X bytes, there should be X bytes of space available in the pipe and so the write should be able to write up to X bytes, no?
How can I have the behavior "read 1 byte, write 1 byte, etc" instead of "read 1 byte, read 1, read 10, read 2000, ...(until 4096 byte read), write 4096" ?

Why it doesn't work the way you think
So basically what I understand is that your pipe is associated with some kind of linked list of kernel buffers. Processes waiting to write to your pipe are waken up only when one of these buffer is emptied. It happens that in your case these buffers are 4K in size.
See: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?id=HEAD
Specifically line: 281 Where the test on the buffer size is done and line: 287 where the decision to wake up other processes is done.
The size of the pipe buffer is indeed dependent on the memory page size, see man fcntl
F_SETPIPE_SZ (int; since Linux 2.6.35)
Change the capacity of the pipe referred to by fd to be at least arg bytes.
An unprivileged process can adjust the pipe capacity to any value between
the system page size and the limit defined in /proc/sys/fs/pipe-max-size
(see proc(5)). Attempts to set the pipe capacity below the page size are
silently rounded up to the page size. Attempts by an unprivileged process to
set the pipe capacity above the limit in /proc/sys/fs/pipe-max-size yield
the error EPERM; a privileged process (CAP_SYS_RESOURCE) can override the
limit. When allocating the buffer for the pipe, the kernel may use a
capacity larger than arg, if that is convenient for the implementation. The
F_GETPIPE_SZ operation returns the actual size used. Attempting to set the
pipe capacity smaller than the amount of buffer space currently used to
store data produces the error EBUSY.
How to make it work
The pattern you try to achieve is classical. But it is used the way around. People starts with an empty pipe. Process waiting for an event, does read the empty pipe. Process wanting to signal an event, write a single byte to the pipe.
I think I seen that in Boost.Asio but I'm too lazy to find the correct reference.

Pipe uses 4kB pages for buffer and write is blocked until there is an empty page for write and then do not block until it is full again. It is well described in fjardon's answer. If you would like to use the pipe for signalling you are looking for opposite scenario.
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
// Create the pipe
if(pipe(pipefd) == -1)
{
perror("pipe()");
exit(EXIT_FAILURE);
}
// Fork
switch(fork())
{
case -1:
perror("fork()");
exit(EXIT_FAILURE);
case 0:
// Close unused write side
close(pipefd[1]);
while(1)
{
char c;
// Read only one byte
int ret = read(pipefd[0], &c, 1);
printf("Woke up\n", ret);
fflush(stdout);
}
default:
// Close unused read side
close(pipefd[0]);
size_t len = 0;;
char *str = NULL;
while(1)
{
int nbread;
char buf[65535];
while (getline(&str, &len, stdin)) {
if (sscanf(str, "%i", &nbread)) break;
};
// Write number byte asked
int ret = write(pipefd[1], buf, nbread);
printf("Written %d bytes\n", ret);
fflush(stdout);
}
}
return 0;
}

Related

Need Suggestion while handle huge pipe data

I'm practicing C code with pipe system call, it works well with small chunks of data. but as the data goes beyond the pipe capacity, dead lock occurs.
My test system is Debian Sid, but i believe it share the common ground with other Linux distributions. This piece of code works well while the input file '/tmp/a.out' is small enough to fit within the pipe, but blocked as the file is up to 1M.
#include <sys/errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#define CHUNK 2048
int main() {
int fd=open("/tmp/a.out",O_RDONLY);
int pin[2];
int pout[2];
int nread;
char buff[CHUNK];
pipe(pin);
pipe(pout);
int rc;
pid_t pid=fork();
if (pid == 0) {
close(pin[1]);
dup2(pin[0],STDIN_FILENO);
close(pout[0]);
dup2(pout[1],STDOUT_FILENO);
execlp("cat","cat",(char *)0);
} else if (pid > 0) {
close(pin[0]);
close(pout[1]);
/* I think dead lock occurs here, but i can't figure out a way to avoid it */
while ( (nread=read(fd,buff,CHUNK)) > 0) write(pin[1],buff,nread);
close(pin[1]);
while ( (nread=read(pout[0],buff,CHUNK)) >0) write(STDOUT_FILENO,buff,nread);
waitpid(pid,&rc,0);
exit(rc);
} else {
perror("fork");
exit(errno);
}
}
Any suggestions? I know Python's subprocess class have something like subprocess.communicate() to avoid this kind of dead lock, but i don't know how to deal with it in C.
Many thanks.
The first process pipes into cat and cat pipes back into the first process. Hence, for cat to not block on piping back, the first process must also drain that pipe. E.g.:
fcntl(pout[0], F_SETFL, fcntl(pout[0], F_GETFL) | O_NONBLOCK);
while((nread=read(fd, buff, CHUNK)) > 0) {
write(pin[1], buff, nread); // TODO: check errors and partial writes here.
while((nread=read(pout[0],buff,CHUNK)) > 0) // pout[0] must be set into non-blocking mode.
write(STDOUT_FILENO, buff, nread);
}
A more robust way is to set both pin[1] and pout[0] into non-blocking mode, use select to determine whether pin[1] is ready for write and pout[0] for read and then do write/read correspondingly and handle partial reads and writes.
From your suggestions at least I have 2 ways to solve this problem
1. Setting 'NON-BLOCK' mode by 'fcntl' or 'select/poll/epoll'
Use concurrency such as 'pthread' for stdin pipe
piece of code attached.
struct data {
int from_fd;
int to_fd;
};
and code for pipes should look like
pthread_t t;
struct data d;
d.from_fd=fd;
d.to_fd=pin[1];
pthread_create(&t,NULL,&fd_to_pipe,(void*) &d);
while ( (nread=read(pout[0],buff,CHUNK)) >0) write(STDOUT_FILENO,buff,nread);
waitpid(pid,&rc,0);
pthread_join(t,NULL);
Thank you !

FIFO pipe returns 0 instead of correct integer?

I am attempting to implement a basic communication between two processes. I intend for each process to receive a piece of information then transmit one back. I am new to pipes so have attempted this using this code example:
How to send a simple string between two programs using pipes?
I set up the code and it works fine, I then duplicated the code for a second pipe in order to receive another integer. However my second pipe does not transmit the integer, the program receives a 0 instead.
program1.c:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_BUF 1024
int main()
{
int fd; // file descriptor
int fd_b;
int data = 5;
int buf; // buffer from fifo
char * fifo_one = "/tmp/fifo_one";
char * fifo_two = "/tmp/fifo_two";
// create fifo
mkfifo(fifo_one, 0666);
// write to FIFO
fd = open(fifo_one, O_WRONLY);
write(fd, &data, sizeof(&data));
close(fd);
// remove FIFO
unlink(fifo_one);
// receive from FIFO
fd_b = open(fifo_two, O_RDONLY);
read(fd_b, &buf, MAX_BUF);
printf("Received: %d\n", buf);
close(fd_b);
return 0;
}
program2.c:
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main()
{
int fd; // file descriptor
int fd_b;
char * fifo_one = "/tmp/fifo_one";
char * fifo_two = "/tmp/fifo_two";
int buf; // buffer from fifo
int ret_dat; // return data
// receive data from fifo
fd = open(fifo_one, O_RDONLY);
read(fd, &buf, MAX_BUF);
printf("Received: %d\n", buf);
close(fd);
// decide return
if (buf == 5) {
ret_dat = 10;
}
// send data back
// create fifo
mkfifo(fifo_two, 0666);
// write to FIFO
fd_b = open(fifo_two, O_WRONLY);
write(fd_b, &ret_dat, sizeof(&ret_dat));
close(fd_b);
// remove FIFO
unlink(fifo_sendBal);
return 0;
}
The second program receives the 5, but does not send back 10 successfully,
I understand that timings effect IPC so I have tried using sleep after certain events but I cannot get it to work.
The second program receives the 5, but does not send back 10 successfully ? The property of FIFO inter process communication created by calling mkfifo() is that both process should alive to communicate each other. From the manual page of mkfifo
Once you have created a FIFO special file in this way, any process
can open it for reading or writing, in the same way as an ordinary
file. However, it has to be open at both ends simultaneously before
you can proceed to do any input or output operations on it. Opening
a FIFO for reading normally blocks until some other process opens the
same FIFO for writing, and vice versa. See fifo(7) for nonblocking
handling of FIFO special files.

why non-blocking write to disk doesn't return EAGAIN or EWOULDBLOCK?

I modified a program from APUE, the program first open a file, then mark the fd as non-blocking, then continue write to the fd until write return -1.
I think since disk I/O is slow, when write buffers in OS is nearly full, the write system call will return -1, and the errno should be EAGAIN or EWOULDBLOCK.
But I ran the program for about several minutes and I repeated running the program serveral times, the write system call didn't returned -1 even once! Why?
Here's the code:
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
char buf[4096];
int
main(void)
{
int nwrite;
int fd = open("a.txt", O_RDWR);
if(fd<0){
printf("fd<0\n");
return 0;
}
int i;
for(i = 0; i<sizeof(buf); i++)
buf[i] = i*2;
set_fl(fd, O_NONBLOCK); /* set nonblocking */
while (1) {
nwrite = write(fd, buf, sizeof(buf));
if (nwrite < 0) {
printf("write returned:%d, errno=%d\n", nwrite, errno);
return 0;
}
}
clr_fl(STDOUT_FILENO, O_NONBLOCK); /* clear nonblocking */
exit(0);
}
The O_NONBLOCK flag is primarily meaningful for file descriptors representing streams (e.g, pipes, sockets, and character devices), where it prevents read and write operations from blocking when there is no data waiting to read, or buffers are too full to write anything more at the moment. It has no effect on file descriptors opened to regular files; disk I/O delays are essentially ignored by the system.
If you want to do asynchronous I/O to files, you may want to take a look at the POSIX AIO interface. Be warned that it's rather hairy and infrequently used, though.

Fork Process / Read Write through pipe SLOW

ANSWER
https://stackoverflow.com/a/12507520/962890
it was so trivial.. args! but lots of good information received. thanks to everyone.
EDIT
link to github: https://github.com/MarkusPfundstein/stream_lame_testing
ORIGINAL POST
i have some questions regarding IPC through pipelines. My goal is to receive MP3 data per TCP/IP stream, pipe it through LAME to decode it to wav, do some math and store it on disk (as a wav). I am using non blocking IO for the whole thing.
What irritates me a bit is that the tcp/ip read is way more fast than the pipe line trough lame. When i send a ~3 MB mp3 the file gets read on the client side in a couple of seconds. In the beginning, i can also write to the stdin of the lame process, than it stops writing, it reads the rest of the mp3 and if its finished i can write to lame again. 4096 bytes take approx 1 second (to write and read from lame). This is pretty slow, because i want to decode my wav min 128kbs.
The OS Is a debian 2.6 kernel on a this micro computer:
https://www.olimex.com/dev/imx233-olinuxino-maxi.html
65 MB RAM
400 MhZ
ulimit -n | grep pipe returns 512 x 8 , means 4096 which is ok. Its a 32 bit system.
The weird thing is that
my_process | lame --decode --mp3input - output.wav
goes very fast.
Here is my fork_lame code (which shall essentialy connect stout of my process to stdin of lame and visa versa)
static char * const k_lame_args[] = {
"--decode",
"--mp3input",
"-",
"-",
NULL
};
static int
fork_lame()
{
int outfd[2];
int infd[2];
int npid;
pipe(outfd); /* Where the parent is going to write to */
pipe(infd); /* From where parent is going to read */
npid = fork();
if (npid == 0) {
close(STDOUT_FILENO);
close(STDIN_FILENO);
dup2(outfd[0], STDIN_FILENO);
dup2(infd[1], STDOUT_FILENO);
close(outfd[0]); /* Not required for the child */
close(outfd[1]);
close(infd[0]);
close(infd[1]);
if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
perror("execv");
return 1;
}
} else {
s_lame_pid = npid;
close(outfd[0]); /* These are being used by the child */
close(infd[1]);
s_lame_fds[WRITE] = outfd[1];
s_lame_fds[READ] = infd[0];
}
return 0;
}
This are the read and write functions. Please not that in write_lame_in. when i write to stderr instead of s_lame_fds[WRITE], the output is nearly immedieatly so its definitly the pipe through lame. But why ?
static int
read_lame_out()
{
char buffer[READ_SIZE];
memset(buffer, 0, sizeof(buffer));
int i;
int br = read(s_lame_fds[READ], buffer, sizeof(buffer) - 1);
fprintf(stderr, "read %d bytes from lame out\n", br);
return br;
}
static int
write_lame_in()
{
int bytes_written;
//bytes_written = write(2, s_data_buf, s_data_len);
bytes_written = write(s_lame_fds[WRITE], s_data_buf, s_data_len);
if (bytes_written > 0) {
//fprintf(stderr, "%d bytes written\n", bytes_written);
s_data_len -= bytes_written;
fprintf(stderr, "data_len write: %d\n", s_data_len);
memmove(s_data_buf, s_data_buf + bytes_written, s_data_len);
if (s_data_len == 0) {
fprintf(stderr, "finished\n");
}
}
return bytes_written;
}
static int
read_tcp_socket(struct connection_s *connection)
{
char buffer[READ_SIZE];
int bytes_read;
bytes_read = connection_read(connection, buffer, sizeof(buffer)-1);
if (bytes_read > 0) {
//fprintf(stderr, "read %d bytes\n", bytes_read);
if (s_data_len + bytes_read > sizeof(s_data_buf)) {
fprintf(stderr, "BUFFER OVERFLOW\n");
return -1;
} else {
memcpy(s_data_buf + s_data_len,
buffer,
bytes_read);
s_data_len += bytes_read;
}
fprintf(stderr, "data_len: %d\n", s_data_len);
}
return bytes_read;
}
The select stuff is pretty basic select logic. All blocks are non blocking of course.
Anyone any idea? I'd really appreciate any help ;-)
Oops! Did you check your LAME output?
Looking at your code, in particular
static char * const k_lame_args[] = {
"--decode",
"--mp3input",
"-",
"-",
NULL
};
and
if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
means you are accidentally omitting the --decode flag as it will be argv[0] for LAME, instead of the first argument (argv[1]). You should use
static char * const k_lame_args[] = {
/* argv[0] */ "lame",
/* argv[1] */ "--decode",
/* argv[2] */ "--mp3input",
/* argv[3] */ "-",
/* argv[4] */ "-",
NULL
};
instead.
I think you are seeing a slowdown because you're accidentally recompressing the MP3 audio. (I noticed this just a minute ago, so haven't checked if LAME does that if you omit the --decode flag, but I believe it does.)
It is possible there is some sort of a blocking issue wrt. nonblocking pipes (not really being nonblocking), causing your end to block until LAME consumes the data.
Could you try an alternative approach? Use normal, blocking pipes, and a separate thread (using pthreads), which has the singular purpose of writing data from a circular buffer to LAME. Your main thread then keeps filling the circular buffer from your TCP/IP connection, and can easily also track and report buffer levels -- very useful during development and debugging. I've had much better success with blocking pipes and threads than nonblocking pipes, in general.
In Linux, threads really do not have that much of an overhead, so you should be comfortable in using them even on embedded architectures. The only trick you must master is specifying a sensible stack size for the worker thread -- in this case 16384 bytes is quite likely enough -- because only the initial stack given to the process will automatically grow and threads stacks are fixed an by default quite large.
Do you need example code?
Edited to add:
Your program receives data from the TCP/IP connection probably at a steady rate. However, LAME consumes the data in largeish chunks. In other words, the situation is like a car being towed, with the tow car jerking and stopping, with the towee jerking into it every time: both your process and LAME are most of the time waiting the other to receive/send more data.
First, those two close are not required (actually, you shouldn't do that), because the two dup2 which follow will do it automatically :
close(STDOUT_FILENO);
close(STDIN_FILENO);

linux virtual file as device driver

I write a linux char device driver to simulate a file. The data is stored in an array and I want to implement a "read-file"-handler...
static ssize_t data_read(struct file *f, char __user *buf, size_t count, loff_t *f_pos){
char *msg_pointer;
int bytes_read = 0;
if(vault.storage==NULL)
return -EFAULT;
msg_pointer = vault.storage + *f_pos;
while (count && (*f_pos < vault.size) ) {
put_user(*(msg_pointer++), buf++);
count--;
bytes_read++;
++*f_pos;
}
return bytes_read;
}
vault.storage is a pointer to a kmalloc-creation. If I test the code by copying with dd it works as expected, but when I want to open the file with C
if((fp_data = open("/dev/vault0", O_RDWR)) < 0){
perror("could not open file.\n");
}
err = write(fp_data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 36);
if (err < 0){
perror("failed to write to sv \n");
}
read(fp_data, buffer, 36);
read(fp_data, buffer, 36);
the first read-command returns 4.. the second 0 - how is this possible?
write performed on a file is not guaranteed to write all the bytes requested atomically ... that is only reserved for a pipe or FIFO when the requested write-amount is less than PIPE_BUF in size. For instance, write can be interrupted by a signal after writing some bytes, and there will be other instances where write will not output the full number of requested bytes before returning. Therefore you should be testing the number of bytes written before reading back any information into a buffer to make sure you are attempting to read-back the same number of bytes written.
Put a printk in the data_read call and print the count and print what is returned to the user(check the value of bytes_read). The bytes_read is returned to the read() call in the use space. Make sure you are returning correct value. And you can also print the fpos and check what is happening.
Here I assume that your drivers read and write functions are called properly, I mean major and minor numbers of your device file belongs to your driver

Resources