0% found this document useful (0 votes)
10 views

lab04

The document provides an overview of inter-process communication (IPC) mechanisms in UNIX systems, focusing on named pipes (FIFOs) and pthreads. It explains the functionality of ordinary and named pipes, including their creation, usage, and limitations, along with examples of producer and consumer processes. Additionally, it covers the basics of pthreads, including thread creation, synchronization, and an example demonstrating summation using threads.

Uploaded by

alexiatsang03
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

lab04

The document provides an overview of inter-process communication (IPC) mechanisms in UNIX systems, focusing on named pipes (FIFOs) and pthreads. It explains the functionality of ordinary and named pipes, including their creation, usage, and limitations, along with examples of producer and consumer processes. Additionally, it covers the basics of pthreads, including thread creation, synchronization, and an example demonstrating summation using threads.

Uploaded by

alexiatsang03
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 33

Fall 2024 COMP 3511

Review #3

Yijun SUN
[email protected]
Coverages
 Named Pipe Examples
 Pthread Examples
Quick Review on Pipes
 Pipes - one of the first IPC mechanisms in early UNIX systems.
 Ordinary Pipes:
 Unidirectional;
 Cannot be accessed from outside the process that created it.
 Typically, a parent process creates a pipe and uses it to communicate
with a child process that it creates via fork().
 Cease to exist after the processes have finished communicating and
terminated.
 Named Pipes:
 Bidirectional;
 Can be accessed without a parent-child relationship. Several
processes can use it for communication, typically with several writers.
 Referred to as FIFOs and appear as typical files in UNIX systems,
created using mkfifo() system call.
 Continue to exist after the communicating processes have finished,
until they are explicitly deleted
A Named Pipe (FIFO) Example
 Description - In this example, we have a data producer running in a
process, and also a data consumer running in another process.
 They are not in parent-child relationship. They are created from Shell
independently. Either one can be started first.
 Expected behaviors:
 The producer creates a FIFO and then sends data to one end of it.
 The consumer receives the data from the other end of the FIFO.
 We will cover all fundamental operations on FIFOs: mkfifo (create),
open, read, write, and close.
A Named Pipe (FIFO) Example
 Common header for fifo_prod.c and fifo_con.c

#include <limits.h>
#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/testp"


#define BUFFER_SIZE 4096
A Named Pipe (FIFO) Example
 The producer - fifo_prod.c
#define TEN_MEG (1024 * 1024 * 10)

int main(void) {
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);
}
}
A Named Pipe (FIFO) Example
 The producer - fifo_prod.c
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d opened fd %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;
}
close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
A Named Pipe (FIFO) Example
 The consumer - fifo_con.c
int main(void) {
int pipe_fd;
int res;
int open_mode = O_RDONLY;
int bytes_read = 0;
char buffer[BUFFER_SIZE + 1];
memset(buffer, '\0', sizeof(buffer));

printf("Process %d opening FIFO O_RDONLY\n", getpid());


pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d opened fd %d\n", getpid(), pipe_fd);

if(pipe_fd != -1) {
do {
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
A Named Pipe (FIFO) Example
 Execution results
 In one terminal

 In another terminal
Blocking I/O
 Although FIFOs use standard file I/O functions (e.g., open(), read(),
and write()), it is important to note that they are not regular files. Specifically,
once data has been read from a FIFO, the data is discarded and cannot be
read again (just like an anonymous pipe). In contrast, with a regular file,
multiple processes can read the same data from the same file. That is,
regular files store data persistently, but FIFOs do not. Consequently, FIFOs
cannot be used for broadcasting a single message to multiple recipients;
only one process can read the data. Similarly, FIFOs (like pipes) are not
suitable for bi-directional communication; if a process writes into the FIFO
then immediately tries to read a response, it may read its own message!
 Also similar to anonymous pipes, FIFOs use blocking I/O until both ends
are opened by at least one process. As such, there is no concern about a
process writing into the FIFO too soon; if no process has opened the FIFO
for reading, the writing process will block until a reader becomes available.
This behavior may be problematic if the writing process needs to perform
some other task. To resolve this problem, pass the O_NONBLOCK option
during the call to open() to make the FIFO access non-blocking

Reference link
Recap the APIs
 FIFO-specific
 int mkfifo (const char *filename, mode_t mode)
 The mkfifo function is declared in the header file `sys/stat.h'.
 The mkfifo function makes a FIFO special file with name filename.
The mode argument is used to set the file's permissions.
 The normal, successful return value from mkfifo is 0. In the case of
an error, -1 is returned.
 Remarks
 Reading or writing pipe data is atomic if the size of data written is
not greater than PIPE_BUF.
 Reading or writing a larger amount of data may not be atomic.
– for example, output data from other processes sharing the
descriptor may be interspersed.
– Also, once PIPE_BUF characters have been written, further
writes will block until some characters are read.
Recap the APIs
 General IO
 int access (const char *filename, int how)
 The access function is declared in the header file `unistd.h'.
 The access function checks to see whether the file named by
filename can be accessed in the way specified by the how
argument. The how argument either can be the bitwise OR of the
flags R_OK, W_OK, X_OK, or the existence test F_OK.
 The return value is 0 if the access is permitted, and -1 otherwise.
 Macros
 R_OK - Flag meaning test for read permission.
 W_OK - Flag meaning test for write permission.
 X_OK - Flag meaning test for execute/search permission.
 F_OK - Flag meaning test for existence of the file.
Recap the APIs
 General IO
 int open (const char *filename, int flags[,
mode_t mode])
 The open function is declared in the header file `fcntl.h'.
 The open function creates and returns a new file descriptor for the
file named by filename.
 The flags argument controls how the file is to be opened.
 The argument mode is used only when a file is created, but it
doesn’t hurt to supply the argument in any case.
 The normal return value from open is a non-negative integer file
descriptor. In the case of an error, a value of -1 is returned instead.
 Reference
 See here for more about the feasible choices of the flags and/or
more argument.
Recap the APIs
 General IO
 int close (int filedes)
 The close function is declared in the header file `unistd.h'.
 The function close closes the file descriptor filedes. Closing a file has
the following consequences:
• The file descriptor is deallocated.
• Any record locks owned by the process on the file are unlocked.
• When all file descriptors associated with a pipe or FIFO have been
closed, any unread data is discarded.
 This function is a cancellation point in multi-threaded programs. This
is a problem if the thread allocates some resources (like memory, file
descriptors, semaphores or whatever) at the time close is called.
 The normal return value from close is 0; a value of -1 is returned in
case of failure.
Recap the APIs
 General IO
 ssize_t read (int filedes, void *buffer, size_t size)
 The read function is declared in the header file `unistd.h'.
 The read function reads up to size bytes from the file with descriptor
filedes, storing the results in the buffer. (This is not necessarily a
character string, and no terminating null character is added.)
 The return value is the number of bytes actually read. This might be
less than size;
– for example, if there aren’t that many bytes left in the file or if there
aren’t that many bytes immediately available.
– The exact behavior depends on what kind of file it is.
 A value of zero indicates end-of-file (except if the value of the size
argument is also zero).
 In case of an error, read returns -1.
Recap the APIs
 General IO
 ssize_t write (int filedes, const void *buffer, size_t
size)
 The write function is declared in the header file `unistd.h'.
 The write function writes up to size bytes from buffer to the file with
descriptor filedes. The data in buffer is not necessarily a character
string and a null character is output like any other character.
 The return value is the number of bytes actually written. This may be
size, but can always be smaller.
 Once write returns, the data is enqueued to be written and can be
read back right away, but it is not necessarily written out to permanent
storage immediately. You can use fsync when you need to be sure
your data has been permanently stored before continuing.
 In the case of an error, write returns -1.
Pthreads
 Pthreads - POSIX standard (IEEE 1003.1c) defining an API for thread
creation and synchronization. This is a specification for thread behaviour,
not an implementation. Different Unix-like OS implements this differently
 The C program shown next demonstrates the basic Pthreads API for
constructing a multithreaded program that calculates the summation of a
non-negative integer in a separate thread.
 In this Pthreads program, separate threads begin execution in a specified
function - the runner() function
 When this program begins, a single thread of control (the parent thread) begins
in main(). After some initialization, main() creates a second thread (the child
thread) running runner() function. Both threads share the global data sum
 This program follows the thread create/join strategy, whereby after creating
summation thread – the parent thread waits for the completion of child thread
 Once the summation thread has returned, the parent thread will output the value
of the shared data sum
Pthreads
Pthreads Example
 In a Pthreads program, separate threads begin execution, specifically
 All Pthreads programs must include the pthread.h header file
 The pthread_t declares the identifier for the thread to be created
 The pthread_attr_t represents the attributes for the thread (stack size and
scheduling information. Here default attributes are used)
 A thread created with pthread_create(), with the thread identifier, attributes,
function (i.e., the runner()), the integer parameter (i.e., argv[1])
 The program has two threads: the initial (or parent) thread in main() and the
summation (or child) thread performing summation in the runner() function
 The parent thread waits for its child to terminate by calling pthread_join()
function
 The summation thread will terminate when it calls function pthread_exit()
pthread_create function
 #include <pthread.h>
 int pthread_create( pthread_t * thread,
const pthread_attr_t * attr,
void * (*start_routine)(void*),
void * arg);
 thread, is a pointer to a structure of type pthread_t;
 attr, is used to specify any attributes this thread might have;
 Function pointer;
 arg, the argument to be passed to the function where the thread begins
execution.
Pthreads Example: Create & Join
Pthreads Example (Cont.)
Pthreads
Pthreads Example
Example (Cont.)
(Cont.)
Test the pthread program
 Compile the C program.
 pthread is required in the linking process to generate the executable

> gcc pthread_demo.c -lpthread -o pthread_demo

> ./pthread_demo 2
sum of 1..2 = 3

> ./pthread_demo 1000


sum of 1..1000 = 500500
Pthreads Code for Joining 10 Threads
 A simple method for waiting on multiple threads using pthread_join()
Pthreads Code for Joining 10 Threads
function is to enclose the operation within a simple for loop.
 For example, join on ten threads using the Pthread code below
Another Pthreads example
Trace the threads(1)

main Thread 1 Thread 2

starts running
prints “main: begin”
creates Thread 1
creates Thread 2
waits for T1
runs
prints “A”
returns
waits for T2
runs
prints “B”
returns
prints “main: end”
Trace the threads(2)
main Thread 1 Thread 2

starts running
prints “main: begin”
creates Thread 1
runs
prints “A”
returns
creates Thread 2
runs
prints “B”
returns
waits for T1
returns immediately; T1 is done
waits for T2
returns immediately; T2 is done
prints “main: end”
Time to consider…

Will there be any other possible outputs?

Will Thread1 runs after Thread2?


Trace the threads(3)

main Thread 1 Thread 2


starts running
prints “main: begin”
creates Thread 1
creates Thread 2
runs
prints “B”
returns
waits for T1
runs
prints “A”
returns
waits for T2
returns immediately; T2 is done
prints “main: end”
Extend this example…
What is the possible outputs?
How many possible outputs of this example?

Considering different orders, there should be 6 possible outputs

You might also like