#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
// Older form:
// res = mknod("/tmp/myfifoxx", S_IFIFO| 0777, 0));
int res = mkfifo("/tmp/my_fifoxx", 0777);
if (res == 0)
printf("FIFO created\n");
exit(EXIT_SUCCESS);
}
Go and do an ls -lF /tmp to see your named pipe.
Once you have run fifo1.c to create the pipe, try the following experiments:
Note that if you want your Named Pipe to be available to others, you need to
call:
umask(0);
Experiment 1:
cat < /tmp/myfifoxx
Experiment 2
echo "Some stuff" > /tmp/myfifoxx
Experiment 3
cat < /tmp/myfifoxx&
echo "Some stuff" > /tmp/myfifoxx
Note that in the first 2 experiments we see a blocking effect. In the third experiment, both jobs finish.
// Let's start with the header files, a #define and the check that the correct number
// of command-line arguments have been supplied.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifoxx"
int main(int argc, char *argv[])
{
int res;
int open_mode = 0;
int i;
if (argc < 2) {
fprintf(stderr, "Usage: %s <some combination of\
O_RDONLY O_WRONLY O_NONBLOCK>\n", *argv);
exit(EXIT_FAILURE);
}
// Assuming that the program passed the test, we now set the value of open_mode
// from those arguments.
for(i = 1; i < argc; i++) {
if (strncmp(*++argv, "O_RDONLY", 8) == 0)
open_mode |= O_RDONLY;
if (strncmp(*argv, "O_WRONLY", 8) == 0)
open_mode |= O_WRONLY;
if (strncmp(*argv, "O_NONBLOCK", 10) == 0)
open_mode |= O_NONBLOCK;
}
// We now check whether the FIFO exists and create it if necessary.
// Then the FIFO is opened and output given to that effect while the program
// catches forty winks. Last of all, the FIFO is closed.
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO\n", getpid());
res = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid(), res);
sleep(5);
if (res != -1) (void)close(res);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
Try some of the open combinations:
Experiment1:
execute ./fifo2 O_RDONLY
Experiment 2
execute ./fifo2 O_WRONLY
Experiment 3
In window 1, execute ./fifo2 O_RDONLY
In window 2, execute ./fifo2 O_WRONLY
Experiment 4
execute ./fifo2 O_RDONLY O_NONBLOCK
Experiment 5
execute ./fifo2 O_WRONLY O_NONBLOCK
Note that experiment 1 and 2 block. Experiments 3, 4, and 5 terminate. Experiment 5 errors out.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifoxx"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)
int main()
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[BUFFER_SIZE + 1];
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
while(bytes_sent < TEN_MEG) {
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1) {
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifoxx"
#define BUFFER_SIZE PIPE_BUF
int main()
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes_read = 0;
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
do {
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
Execute ./fifo3 in one window and ./fifo4
in another window. Note that very quickly we transfer 10 megabytes.
This is meant to be an in-class exercise. It is motivated from a real situation I encountered in the web world. Named pipes provides a solution to this problem.
Problem: You are using an apache web server and you have some code which does a minimal amount of parsing of your user request. In your product, a user has a session and is associated with some cached information that is being managed by a process that is running in another program (on the same machine).
Keep in mind that each of the user requests may be serviced by different Apache children due to the Apache architecture. So its tough to keep a point to point socket connection.
From Apache, you would like to send your request to some central place (a named pipe), and you would like to magically have the response come back to you.
You have a task which reads this named pipe, and then forwards onto the proper process to deal with this user. This process has cached information for this user.
You have decided to have children processes to deal with each of the user sessions and the user's associated cache.
So.... it looks like the process that reads the central message queue needs to forward the request off to the appropriate child. And the child then responds back to the original Apache client.
-------------------------------------------------------
It's good to draw a picture of where we want to go.
How do we go about solving this????
Create a Server proto1 that reads from a named pipe. The server should create a named pipe if it doesn't exist. When the server goes down, the server should delete the named pipe. In the first step, lets just have the server print out what it sees. The matching client will open the named pipe and in a loop allow the user to enter keyboard input that then gets sent to the named pipe. Have the name of the receiving pipe contain your student number (i.e. /tmp/c27500). You can get your student userid by calling getenv("USER");
In proto2, the client also creates a named
pipe that contains its Process id (getpid returns
your process ID). In the string sent to the server named pipe, we will send
in the name of the receiving pipe. The server will parse the received string
and send back some response to the client response named pipe. The client should
also delete its pipe when it dies. The command can look like:
Resp_pipe_name: Rest of the string.
In proto 3, the server will spawn children
for each user session (0,1,2,3, etc. ). The server will use the pipe command
to create a connection between itself and each child. A command line argument
will indicate which user child to send to. The new command will look like:
Resp_pipe_name: User: Rest of the string
If we get to proto4, the child process can
now add some kind of command structure. For example, the child process could
keep a cache of information for each user. The cache could be stored in a file.
Each entry in the file could be referenced by line number. The new command for
the client could allow you to read or write a specific line number in the cache
file. The format, might look like:
Resp_pipe_name: User: Read: item_number
or
Resp_pipe_name: User: Write: item_number: replacement line
Here is a partially working solution.
The np_serve program reads on its named pipe and expects to see contents that looks like:
name: Rest of input
np_serve will parse the name and write to the associated file.
np_client2 writes to the np_serve named pipe and has its own unique named pipe that reads back a response. This part works. One tricky part is that the response pipe was opened in non-bocking mode to keep us from hanging on the Open call. This meant that a read immediated after a sending to the server was likely to fail because the read is not blocking and the read returns immediately before the server had a chance to send the response. This was fixed by using a select statement to wait for I/O activity on the response pipe.
Things left undone: We can't seem to get contol-C to work. Also we haven't taken the next stop of spawning children processes to receive their data through local pipes.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
int getOut = 0;
void mysignal(int num)
{
getOut = 1;
printf("Caught signal %d\n", num);
}
char * get_name()
{
static char cbuff[200];
sprintf(cbuff, "/tmp/srv_%s", getenv("USER"));
return cbuff;
}
void remove_pipe()
{
unlink(get_name());
}
int getFid()
{
int rc;
int fid;
char * name = get_name();
rc = mkfifo(get_name(), 0777);
if (rc == 0)
printf("FIFO(%s) created\n", name );
else
{
printf("mkfifo err: %s\n", strerror(errno));
exit(-1);
}
fid = open(name, O_RDWR);
if ( fid < 0)
{
printf("open err: %s\n", strerror(errno));
exit(-1);
}
/*
if (open (name, O_WRONLY))
{
printf(" open(%s) for write failed\n%s",
name, strerror(errno));
}
*/
return fid;
}
void guts(int fid)
{
char cbuff[5000];
char *str, *str2;
int nread;
int respFid;
for (;;)
{
nread = read(fid, cbuff, sizeof(cbuff));
if (nread <= 0) break;
if (getOut) break;
cbuff[nread] =0;
printf("Buff = \n%s\n", cbuff);
str = strchr(cbuff, ':');
if (str != NULL)
{
*str = 0;
respFid = open(cbuff, O_WRONLY);
if (respFid < 0)
{
printf("open err(%s): %s\n",
cbuff, strerror(errno));
}
else
{
str2 = &str[1];
write(respFid, str2, strlen(str2));
close(respFid);
}
}
}
}
int main()
{
int fid;
// signal(SIGINT, mysignal);
remove_pipe();
fid = getFid();
guts(fid);
remove_pipe();
}
// This is the solution I came up with after class, but then
// I received an email from a student with a better solution.
// It turns out that we can remove the O_NONBLOCK now that we
// opening the pipe in READ/WRITE mode. This means that that
// you don't have to use my select trick to find out when
// you have read activity. The net result is that there is a simpler
// solution which consists of just eliminating all of the code
// that is highlighted
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
int getOut = 0;
char * getRespName()
{
static char cbuff[200];
sprintf(cbuff,"/tmp/np_client%d", getpid());
return cbuff;
}
int getRespFid()
{
int rc;
int fid;
char * name = getRespName();
rc = mkfifo(name, 0777);
if (rc == 0)
printf("FIFO(%s) created\n", name );
else
{
printf("mkfifo err: %s\n", strerror(errno));
exit(-1);
}
fid = open(name, O_RDWR| O_NONBLOCK);
if ( fid < 0)
{
printf("open err: %s\n", strerror(errno));
exit(-1);
}
/*
if (open (name, O_WRONLY))
{
printf(" open(%s) for write failed\n%s",
name, strerror(errno));
}
*/
return fid;
}
void mysignal(int num)
{
getOut = 1;
printf("Caught signal %d\n", num);
}
char * get_name()
{
static char cbuff[200];
sprintf(cbuff, "/tmp/srv_%s", getenv("USER"));
return cbuff;
}
int getFid()
{
int rc;
int fid;
char * name = get_name();
printf("About to open %s\n", name);
fid = open(name, O_WRONLY);
printf("Returned from open %s\n", name);
if ( fid < 0)
{
printf("open err: %s\n", strerror(errno));
exit(-1);
}
return fid;
}
// This routine was added to wait for I/O ---- remember
// we were forced to make the open on the response pipe non-blocking
// Calling select_read allows us to wait until some input is available
// to read.
int select_read(int fid)
{
fd_set fdCheck;
int numInputs;
FD_ZERO(&fdCheck);
FD_SET(fid, &fdCheck);
numInputs = select(fid+1, &fdCheck, 0,0,0);
if (numInputs <= 0)
{
printf("select_read error: %s\n", strerror(errno));
return -1;
}
if (FD_ISSET(fid, &fdCheck))
{
printf("Good select_read call\n");
return 0;
}
else
printf("FD_ISSET not set\n");
return -1;
}
void guts(int fid, int respFid)
{
char cbuff[5000];
char outbuff[2000];
char *str;
int nread, nLen;
nLen = sprintf(cbuff, "%s:", getRespName());
for (;;)
{
str = fgets(&cbuff[nLen], sizeof(cbuff), stdin);
if (getOut) break;
write(fid, cbuff, strlen(cbuff));
if (getOut) break;
// Check to see if input is in the buffer before
// calling read.
if (select_read(respFid) == 0)
{
nread = read(respFid, outbuff, sizeof(outbuff));
if (nread <= 0)
{
printf("read err: nread(%d): %s\n",
nread, strerror(errno));
}
else
{
outbuff[nread] = 0;
printf("My Response was = \n%s\n", outbuff);
}
}
if (getOut) break;
}
close (respFid);
}
int main()
{
int fid, respFid;
signal(SIGINT, mysignal);
fid = getFid();
respFid = getRespFid();
guts(fid, respFid);
}