Sunteți pe pagina 1din 17

Lecture 6

Chapter 6 of Robbins Book

UNIX Special Files

BIL 244 – System Programming


Pipes

• The capacity to communicate is essential for processes that


cooperate to solve a problem.
• The simplest UNIX interprocess communication mechanism is
the pipe, which is represented by a special file.
• The pipe function creates a communication buffer that the caller
can access through the file descriptors fildes[0] and
fildes[1].
• The data written to fildes[1] can be read from fildes[0] on a
first-in-first-out basis.
#include <unistd.h>
int pipe(int fildes[2]);

• If successful, pipe returns 0. If unsuccessful, pipe returns –1 and


sets errno
BIL 244 – System Programming
Pipes

• From the command line:


ls -l | sort -nr | head
produces a list of files sorted by size with only the largest ones
displayed.
• The system call pipe has no external or permanent name and can
only be accessed through its two file descriptors.
• The pipe can only be used by the process that created it and its
descendents.
• When a process calls read on a pipe, the read returns
immediately if the pipe is not empty. If the pipe is empty, the
read blocks until something is written to the pipe, as long as
some process has the pipe open for writing.
• On the other hand, if no process has the pipe open for writing, a
read from an empty pipe returns 0, indicating an end-of-file
condition
BIL 244 – System Programming
A program in which a parent writes a string to a pipe and the child reads the string.
(The program does not check for I/O errors)

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 10

int main(void) {
char bufin[BUFSIZE] = "empty";
char bufout[] = "hello";
int bytesin;
pid_t childpid;
int fd[2];

if (pipe(fd) == -1) {
perror("Failed to create the pipe");
return 1;
}
bytesin = strlen(bufin);
childpid = fork();
if (childpid == -1) {
perror("Failed to fork");
return 1;
}
if (childpid) /* parent code */
write(fd[1], bufout, strlen(bufout)+1);
else /* child code */
bytesin = read(fd[0], bufin, BUFSIZE);
fprintf(stderr, "[%ld]:my bufin is {%.*s}, my bufout is {%s}\n",
(long)getpid(), bytesin, bufin, bufout);
return 0;
}
Pipelines

• The following command line inputs have the same effect

#> ls –l > my.file #> ls –l | sort –n


#> sort –n < my.file

The output of ln –l is written to myfile


sort command uses myfile as an input

BIL 244 – System Programming


Pipelines

• The vertical bar (|) of the code in the previous page


represents a pipe.
• A programmer can build complicated transformations
from simple filters by feeding the standard output of
one filter into the standard input of the other filter
through an intermediate pipe.
• The pipe acts as a buffer between the processes,
allowing them to read and write at different speeds.
The blocking nature of read and write effectively
synchronize the processes.

BIL 244 – System Programming


A program to execute the equivalent of ls -l | sort -n

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
pid_t childpid;
int fd[2];

if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {


perror("Failed to setup pipeline");
return 1;
}
if (childpid == 0) { /* ls is the child */
if (dup2(fd[1], STDOUT_FILENO) == -1)
perror("Failed to redirect stdout of ls");
else if ((close(fd[0]) == -1) || (close(fd[1]) == -1))
perror("Failed to close extra pipe descriptors on ls");
else { execl("/bin/ls", "ls", "-l", NULL);
perror("Failed to exec ls");
}
return 1;
}

if (dup2(fd[0], STDIN_FILENO) == -1) /* sort is the parent */


perror("Failed to redirect stdin of sort");
else if ((close(fd[0]) == -1) || (close(fd[1]) == -1))
perror("Failed to close extra pipe file descriptors on sort");
else {
execl("/bin/sort", "sort", "-n", NULL);
perror("Failed to exec sort");
}
return 1;
}
FIFOs

• FIFOs, or named pipes allow you to use pipes between processes


that are not related.

• You can create a named pipe with the mkfifo command or the
mkfifo system call
#include <sys/stat.h>

int mkfifo(const char *path, mode_t mode);

• If successful, mkfifo returns 0. If unsuccessful, mkfifo returns –


1 and sets errno. A return value of –1 means that the FIFO was
not created
• Important property of pipes and FIFOs:
A write of at most PIPE_BUF bytes to a pipe or FIFO is
atomic.
Note that reads are not necessarily atomic.

BIL 244 – System Programming


The following code segment creates a FIFO, myfifo, in the current working directory
This FIFO can be read by everybody but is writable only by the owner

#define FIFO_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

if (mkfifo("myfifo", FIFO_PERMS) == -1)


perror("Failed to create myfifo");

Remove a FIFO the same way you remove a file.


Either execute the rm command from a shell or call unlink from a program

The following code segment removes myfifo from the current working directory

if (unlink("myfifo") == -1)

perror("Failed to remove myfifo");


The parent reads what its child has written to a named pipe

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define BUFSIZE 256
#define FIFO_PERM (S_IRUSR | S_IWUSR)
/* function predefinitions */
int dofifochild(const char *fifoname, const char *idstring);
int dofifoparent(const char *fifoname);

int main (int argc, char *argv[]) {


pid_t childpid;

if (argc != 2) { /* command line has pipe name */


fprintf(stderr, "Usage: %s pipename\n", argv[0]);
return 1;
}
if (mkfifo(argv[1], FIFO_PERM) == -1) { /* create a named pipe */
if (errno != EEXIST) {
fprintf(stderr, "[%ld]:failed to create named pipe %s: %s\n",
(long)getpid(), argv[1], strerror(errno));
return 1;
}
}
if ((childpid = fork()) == -1){
perror("Failed to fork");
return 1;
}
if (childpid == 0) /* The child writes */
return dofifochild(argv[1], "this was written by the child");
else
return dofifoparent(argv[1]);
}
The child writes to the pipe and returns

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "restart.h"
#define BUFSIZE 256

int dofifochild(const char *fifoname, const char *idstring) {


char buf[BUFSIZE];
int fd;
int rval;
ssize_t strsize;

fprintf(stderr, "[%ld]:(child) about to open FIFO %s...\n",


(long)getpid(), fifoname);
while (((fd = open(fifoname, O_WRONLY)) == -1) && (errno == EINTR)) ;
if (fd == -1) {
fprintf(stderr, "[%ld]:failed to open named pipe %s for write: %s\n",
(long)getpid(), fifoname, strerror(errno));
return 1;
}
rval = snprintf(buf, BUFSIZE, "[%ld]:%s\n", (long)getpid(), idstring);
if (rval < 0) {
fprintf(stderr, "[%ld]:failed to make the string:\n", (long)getpid());
return 1;
}
strsize = strlen(buf) + 1;
fprintf(stderr, "[%ld]:about to write...\n", (long)getpid());
rval = r_write(fd, buf, strsize);
if (rval != strsize) {
fprintf(stderr, "[%ld]:failed to write to pipe: %s\n",
(long)getpid(), strerror(errno));
return 1;
}
fprintf(stderr, "[%ld]:finishing...\n", (long)getpid());
return 0;
}
The parent reads what was written to a named pipe.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "restart.h"
#define BUFSIZE 256
#define FIFO_MODES O_RDONLY

int dofifoparent(const char *fifoname) {


char buf[BUFSIZE];
int fd;
int rval;

fprintf(stderr, "[%ld]:(parent) about to open FIFO %s...\n",


(long)getpid(), fifoname);
while (((fd = open(fifoname, FIFO_MODES)) == -1) && (errno == EINTR)) ;
if (fd == -1) {
fprintf(stderr, "[%ld]:failed to open named pipe %s for read: %s\n",
(long)getpid(), fifoname, strerror(errno));
return 1;
}
fprintf(stderr, "[%ld]:about to read...\n", (long)getpid());
rval = r_read(fd, buf, BUFSIZE);
if (rval == -1) {
fprintf(stderr, "[%ld]:failed to read from pipe: %s\n",
(long)getpid(), strerror(errno));
return 1;
}
fprintf(stderr, "[%ld]:read %.*s\n", (long)getpid(), rval, buf);
return 0;
}
Pipes and the Client-Server Model

• The client-server model is a standard pattern for process


interaction.
• One process, designated the client, requests a service
from another process, called the server
• This section introduces client-server applications that use
named pipes as the communication vehicle.
• We look at two types of client-server communication
• simple-request and
• request-reply.
• In simple-request, the client sends information to the server
in a one-way transmission; in request-reply the client sends
a request and the server sends a reply.

BIL 244 – System Programming


Terminal Control

• Terminals are represented by special files that with


additional control features that determine such things
as when input is delivered to the terminal and how
input and output are related.
• Terminals are controlled from the command line with
the stty command
stty [-a | -g]
stty operands

• The first form reports information about the terminal.


The -a form produces all information available.
The -g form produces a single line of numbers.
BIL 244 – System Programming
Terminals

• Programs access terminal characteristics through a structure of


type struct termios.
This structure contains at least the following members:
tcflag_t c_iflag; /* input modes */
tcflag_t c_oflag; /* output modes */
tcflag_t c_cflag; /* control modes */
tcflag_t c_lflag; /* local modes */
cc_t c_cc[NCCS]; /* control characters */
• The c_cc array holds the values of characters that have special
meaning to the terminal device drivers, such as the end-of-file
character (usually CTRL-D) and the interrupt character (usually
CTRL-C).
• How these characters behave is determined by the mode of the
terminal, either canonical or noncanonical.

BIL 244 – System Programming


Terminals

• Two functions are used to get and set the termios


structure:
#include <termios.h>

int tcgetattr(int fildes, struct termios *termios_p);


int tcsetattr(int fildes, int optional_actions,
const struct termios *termios_p);

BIL 244 – System Programming


A function that sets a particular terminal control character to be a particular value.

#include <errno.h>
#include <termios.h>
#include <unistd.h>

int ttysetchar(int fd, int flagname, char c) {


int error;
struct termios term;

if (tcgetattr(fd, &term) == -1)


return -1;
term.c_cc[flagname] = (cc_t)c;
while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) &&
(errno == EINTR)) ;
return error;
}

S-ar putea să vă placă și