From: Alejandro Colomar <alx.manpages@gmail.com>
To: "J.H. vd Water" <henri.van.de.water@xs4all.nl>,
linux-man <linux-man@vger.kernel.org>
Subject: Fwd: Simple changes to select(2) and pipe(7) - example program
Date: Wed, 9 Nov 2022 14:57:31 +0100 [thread overview]
Message-ID: <9f23de64-4748-5176-1caa-f05b9f4d5f0f@gmail.com> (raw)
In-Reply-To: <30847211-efc7-12be-6ce9-c5c4ada16805@xs4all.nl>
[-- Attachment #1.1.1: Type: text/plain, Size: 1697 bytes --]
-------- Forwarded Message --------
Subject: Re: Simple changes to select(2) and pipe(7) - example program
Date: Wed, 9 Nov 2022 09:42:43 +0100
From: J.H. vd Water <henri.van.de.water@xs4all.nl>
To: Alejandro Colomar <alx@kernel.org>
CC: Michael Kerrisk <mtk.manpages@gmail.com>
On 11/8/22 13:20, Alejandro Colomar wrote:
> On 11/6/22 19:53, J.H. vd Water wrote:
[snip]
>> man 2 select ... DESCRIPTION reads:
>>
>> "select() allows a program to monitor multiple file descriptors, waiting
>> until one or more of the file descriptors become "ready" for some class of
>> I/O operation (e.g., input possible). A file descriptor is considered
>> ready if it is possible to perform a corresponding I/O operation
>> (e.g., read(2), or a sufficiently small write(2)) without blocking."
>>
>> I suggest to add the following line:
>>
>> "However, note that select(2) will block on the read end of a pipe/fifo, if
>> the write end of the pipe/fifo has never been opened before, unlike read(2)
>> (read(2) will always return with zero if the write end of the pipe/fifo is
>> closed - see pipe(7) where the text starts with I/O on pipes and fifos).
>
> It is interesting. Could you please share a small example program
> that demonstrates this behavior? That would certainly help a lot
> reviewing the change.
Hi Alex,
Yesterday, I replied to Alejandro Colomar <alx.manpages@gmail.com>; I also sent
you a copy of the message I sent to M.K. on 29th of September (clarification).
This time I will attach 2 files (i.e. server.c and client.c), the small example
program that you asked for (using alx@kernel.org as address).
Regards,
Henri
[-- Attachment #1.1.2: client.c --]
[-- Type: text/x-csrc, Size: 919 bytes --]
// gcc -Wall -Wextra -o client client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define FIFO_PATH "/tmp/myfifo"
int
main(int argc, char *argv[])
{
int fd = -1;
fd = open(FIFO_PATH, O_WRONLY);
printf("fd = %d\n", fd);
if (fd < 0) {
fprintf(stderr, "Could not open fifo %s: %s\n", FIFO_PATH, strerror(errno));
return -1;
}
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
printf("flags = 0%o\n", flags);
if (argc > 1) {
write(fd, argv[1], strlen(argv[1]) );
} else {
write(fd, &"What ho!", 9);
}
// note: printf "What ho" > /tmp/myfifo from bash, would close the file descriptor.
printf("Closing ... fd = %d\n", fd);
close(fd); // EOF
return 0;
}
//=====
[-- Attachment #1.1.3: server.c --]
[-- Type: text/x-csrc, Size: 4452 bytes --]
// gcc -Wall -Wextra -o server server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#define FIFO_PATH "/tmp/myfifo"
int
main()
{
int fd = -1;
fd_set readfds;
int nsel;
char buf[80] = { 0 };
if (unlink (FIFO_PATH) < 0 && errno != ENOENT) {
perror ("unlink");
exit (1);
}
if (mkfifo(FIFO_PATH, 0600) < 0) {
fprintf(stderr, "Could not create fifo %s: %s\n", FIFO_PATH, strerror(errno));
return -1;
}
fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK); // open() does not block
printf("fd = %d\n", fd);
if (fd < 0) {
fprintf(stderr, "Could not open fifo %s: %s\n", FIFO_PATH, strerror(errno));
return -1;
}
if (fcntl(fd, F_SETFL, O_RDONLY) == -1) // pragmatic
fprintf(stderr, "fcntl F_SETFL: %s\n", strerror(errno));
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
printf("flags = 0%o\n", flags);
//
while(1) {
printf("Calling select() ... ");
fflush(stdout);
FD_ZERO (&readfds);
FD_SET (fd, &readfds);
/*
Different from read(2), select(2) will *block* on the read end of a fifo (or
pipe) if the write end has never been opened before (and only in that case),
while read(2) will always return with zero (in case of an empty pipe) if the
write end is closed/ has been closed.
Therefore, calling select(2) before calling read(2), will enable us to block
if the pipe has never been openend before (again, and only in that case).
*/
nsel = select (fd + 1, &readfds, NULL, NULL, NULL);
// select() blocks until the write end of the pipe has been opened (while the
// write end has never been opened before) and data has been injected.
printf ("returned %d\n", nsel);
printf("Reading ...\n");
ssize_t status = 0;
while (1) { // allow me to reset errno and inspect status ...
errno = 0;
/*
read(2) will block on the read end of a fifo (or pipe) in case the write end
of the fifo (or pipe) has been opened and the pipe is empty. [1]
[1] only if the O_NONBLOCK open file status flag has been disabled.
*/
status = read(fd, buf, sizeof(buf));
printf("status = %ld, errno = %d\n", status, errno);
// will arrive here only if the write end of fifo has been opened (before it
// was closed again)
if (status == 0) { // the write end of the fifo has been closed (EOF)
close(fd);
int fd2;
if ((fd2 = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0) {
perror ("open");
exit (1);
}
if (fcntl(fd, F_SETFL, O_RDONLY) == -1) // pragmatic
fprintf(stderr, "fcntl F_SETFL: %s\n", strerror(errno));
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
printf("flags = 0%o\n", flags);
/*
assign the new file descriptor to variable fd; otherwise select(2) will
not block on the read end in case the write end of the fifo (or pipe) is
closed.
*/
fd = fd2;
printf("fd = %d - refreshed\n", fd);
break;
}
if (status > 0) {
if (write(1, buf, status) < 0) {
fprintf(stderr, "Error sending message: '%s': %s\n", buf, strerror(errno));
}
if (buf[status - 1] != '\n') {
write(1, &"\n", 1);
}
if (strncasecmp(buf, "quit", 4) == 0) {
close(fd);
remove(FIFO_PATH);
exit (0);
}
}
if (status < 0) {
/* An error occurred, bail out */
close(fd);
perror("read");
exit (1);
}
} // end while(1) read
} // end while(!done) select
close(fd);
remove(FIFO_PATH);
return 0;
}
//=====
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next parent reply other threads:[~2022-11-09 13:58 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <30847211-efc7-12be-6ce9-c5c4ada16805@xs4all.nl>
2022-11-09 13:57 ` Alejandro Colomar [this message]
2022-11-09 14:02 ` Fwd: Simple changes to select(2) and pipe(7) - example program Alejandro Colomar
2022-11-09 14:04 ` Alejandro Colomar
2022-11-09 14:04 ` Alejandro Colomar
2022-11-09 14:15 ` Alejandro Colomar
2022-11-09 15:06 ` J.H. vd Water
2022-11-09 15:32 ` J.H. vd Water
2022-11-22 12:22 ` Simple changes to select(2) and pipe(7) J.H. vd Water
2022-11-22 12:25 ` J.H. vd Water
2022-12-01 14:39 ` J.H. vd Water
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=9f23de64-4748-5176-1caa-f05b9f4d5f0f@gmail.com \
--to=alx.manpages@gmail.com \
--cc=henri.van.de.water@xs4all.nl \
--cc=linux-man@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox