Lesson 5 Lab

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 15

Module 5: Plug Things Together

Lab 1: Working with Files

Overview:
This lab ensures the learning and understanding of concepts such as I/O, pipes, makefile,
and libraries.
After performing the lab you will be able to:
1. Describe input/output streams
2. Discuss inter-process communication using pipes
3. Use named pipes
4. Discuss makefile
5. Create and use static and dynamic libraries

Business Scenario:
The user needs to work on different file-related functionalities which they will achieve by
understanding and using the programs mentioned in the lab.

Detailed Instructions:
Using Input/Output Stream
1. Let us see a scenario where you write a program for reading and writing a students
array in the binary operation mode.
2. The code is as below:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

// Student structure, size of the structure is 24 bytes


typedef struct _student{
char name[20];
float marks;
}Student;

#define ARRAY_PARAM(x) x, sizeof(x) / sizeof(x[0]) // expands single


argument array to two array, number of elements in it
#define OUTFILE "students.bin"
Module 5: Plug Things Together
void write_students(Student students[], int size){
FILE *out = fopen(OUTFILE, "wb");

if(out){
printf("\n-------------------------------------------------------------\n\n");
printf("Writing %d students to file %s\n", size, OUTFILE);
if(fwrite(students, sizeof(Student), size, out) != size)
printf("Error writing students to file\n");
else
printf("Successfully save %d students to file %s\n", size, OUTFILE);

fclose(out);
printf("\n-------------------------------------------------------------\n\n");
}
}

void read_students(Student **outPtr, int *size){


FILE *in = fopen(OUTFILE, "rb");

if(in){
printf("\n-------------------------------------------------------------\n\n");
// Get the size of file
fseek(in, 0L, SEEK_END); // Move the file pointer to the end of file
int pos = ftell(in); // Get the current pointer position in the file
rewind(in); // Reset the file pointer to begining of the file
int numStudents = pos / sizeof(Student); // Divide the position with
size of student to get count

printf("Reading %d students from file %s\n", numStudents, OUTFILE);

Student *students = calloc(numStudents, sizeof(Student));


*size = numStudents;

if(students){
*outPtr = students;
if(fread(students, sizeof(Student), numStudents, in) != numStudents)
printf("Error reading students from file %s\n", OUTFILE);
else
printf("Successfully read %d students from file %s\n",
numStudents, OUTFILE);
}
fclose(in);
printf("\n-------------------------------------------------------------\n\n");
}
}
Module 5: Plug Things Together
int main(void){
int size = 0;
int index = 0;
float sum = 0.0f;

// Construct array of 5 students


Student students[] = {
{.name="Richard",.marks=59.80},
{.name="Fred",.marks=88.20},
{.name="Carl",.marks=99.40},
{.name="Peter",.marks=79.50},
{.name="John",.marks=79.30}
};

// Write students to file


write_students(ARRAY_PARAM(students));

Student *pStudents = NULL;


// Read students from file
read_students((Student**)&pStudents, &size);

for(index = 0 ; index < size ; index++){


printf("%s Marks: %.02f\n", pStudents[index].name,
pStudents[index].marks);
sum += pStudents[index].marks;
}

printf("Average score of the class: %.02f\n", (float)sum/size);


free(pStudents);

3. To compile and run the program, copy the source code into a .c file and compile it
using the compilation command.
4. Once the program is compiled successfully, execute the program.
Module 5: Plug Things Together
5. The output of the program is:
-------------------------------------------------------------

Writing 5 students to file students.bin


Successfully save 5 students to file students.bin

-------------------------------------------------------------

-------------------------------------------------------------

Reading 5 students from file students.bin


Successfully read 5 students from file students.bin

-------------------------------------------------------------

Richard Marks: 59.80


Fred Marks: 88.20
Carl Marks: 99.40
Peter Marks: 79.50
John Marks: 79.30
Average score of the class: 81.24
Module 5: Plug Things Together
Inter-process communication using pipe
1. This program helps us to perform data sharing using pipe across related processes.
2. The program is as below:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

// Student structure, size of the structure is 24 bytes


typedef struct _student{
char name[20];
float marksEnglish;
float marksMaths;
float marksScience;
float averageMarks;
}Student;

// Descriptor structure for managing parent and child pipes


typedef struct _descriptor{
int parent[2];
int child[2];
}Descriptor;

// Enumeration for READ and WRITE pipe file descriptor


enum{
READ,
WRITE
};

int main(void){
int size = 0;

// Construct array of 5 students


Student students[] = {

{.name="Richard",.marksEnglish=59.80f, .marksMaths=87.80f, .marksScienc


e=98.30f},

{.name="Fred",.marksEnglish=46.30f, .marksMaths=23.80f, .marksScience=


76.00f},

{.name="Carl",.marksEnglish=99.80f, .marksMaths=57.20f, .marksScience=5


5.00f},

{.name="Alex",.marksEnglish=94.20f, .marksMaths=44.00f, .marksScience=


67.00f},
Module 5: Plug Things Together

{.name="Stacy",.marksEnglish=43.50f, .marksMaths=67.00f, .marksScience=


88.30f},
};

size = sizeof(students)/sizeof(students[0]);

Descriptor pipes[size];
pid_t childPids[size];

for(int index = 0; index < size ; index++){


//Create pipes
if(pipe(pipes[index].parent) || pipe(pipes[index].child)){
printf("Could not create pipe %d\n", index);
exit(1);
}

// Spawn child process with fork


if((childPids[index] = fork()) < 0){
printf("Error in fork\n");
exit(1);
}
else if(childPids[index] == 0){ // Child spawned

// Close pipe descriptors we don't need in the child process


close(pipes[index].parent[READ]);
close(pipes[index].child[WRITE]);

Student s;

// Read student sent by parent process


read(pipes[index].child[READ], &s, sizeof(Student));

printf("Child %d - Calculating average marks of %s\n", getpid(),


s.name);

// Calculate average marks for student


s.averageMarks = (s.marksEnglish + s.marksMaths +
s.marksScience) / 3.0f;

// Send the student back to the parent process


write(pipes[index].parent[WRITE], &s, sizeof(Student));

exit(0); // forked child exists after processing student


}
else{ // Parent process
Module 5: Plug Things Together
// Close pipe descriptors we don't need in the parent process
close(pipes[index].parent[WRITE]);
close(pipes[index].child[READ]);

printf("Parent %d - Sending student %s to child process\n", getpid(),


students[index].name);

// Send student to child process for calculating average marks


write(pipes[index].child[WRITE], &students[index], sizeof(Student));

Student s;
// Read incoming student from child denoted by index
read(pipes[index].parent[READ], &s, sizeof(Student));
printf("Parent %d - Average marks of %s = %.02f\n", getpid(), s.name,
s.averageMarks);

// Enable following line if you encounter synchronization issues


//wait(&status);
}
}
}

3. To compile and run the program, copy the source code into a .c file and compile it.
4. Once the program is compiled successfully, execute the program.
5. The program returns the below output:
Parent 9293 - Sending student Richard to child process
Child 9294 - Calculating average marks of Richard
Parent 9293 - Average marks of Richard = 81.97
Parent 9293 - Sending student Fred to child process
Child 9295 - Calculating average marks of Fred
Parent 9293 - Average marks of Fred = 48.70
Parent 9293 - Sending student Carl to child process
Child 9296 - Calculating average marks of Carl
Parent 9293 - Average marks of Carl = 70.67
Parent 9293 - Sending student Alex to child process
Child 9297 - Calculating average marks of Alex
Parent 9293 - Average marks of Alex = 68.40
Parent 9293 - Sending student Stacy to child process
Module 5: Plug Things Together
Child 9298 - Calculating average marks of Stacy
Parent 9293 - Average marks of Stacy = 66.27
Module 5: Plug Things Together
Using named pipes
1. In this lab, we will walk you through a program which performs data sharing using
named pipe across processes.
2. The code for this objective is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define FIFOFILE "./echoserver"

void runServer(void){
int sharedFile;

printf("Server started...\n");

// mkfifo(<pathname>,<permission>)
mkfifo(FIFOFILE, 0666);

char msg[80];
while(1)
{
// Server waits for incoming data
sharedFile = open(FIFOFILE,O_RDONLY);
read(sharedFile, msg, 80);
close(sharedFile);

// Now open in write mode and write


sharedFile = open(FIFOFILE,O_WRONLY);
// Write the same string received to the file
write(sharedFile, msg, strlen(msg)-1);
close(sharedFile);
memset(msg, 0, 80);
}
}

void runClient(void){
int sharedFile;

printf("Client started...\nType some messages for the server...\n");

// mkfifo(<pathname>, <permission>)
mkfifo(FIFOFILE, 0666);
Module 5: Plug Things Together

char msg[80];
while (1)
{
// Open FIFO for write only
sharedFile = open(FIFOFILE, O_WRONLY);

// Collect user input to be saved to fifo file


// 80 is maximum length
fgets(msg, 80, stdin);

// Write message to fifo file


write(sharedFile, msg, strlen(msg));
close(sharedFile);
memset(msg, 0, 80);

// Open FIFO for Read only


sharedFile = open(FIFOFILE, O_RDONLY);

// Read from FIFO


read(sharedFile, msg, sizeof(msg));

// Print the echo message


printf("Server Says: %s\n", msg);
close(sharedFile);
memset(msg, 0, 80);
}
}

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


{
if(argc < 2){
printf("Insufficient arguments supplied\n");
exit(1);
}

if(strcasecmp(argv[1], "server") == 0)
runServer();
else if(strcasecmp(argv[1], "client") == 0)
runClient();
else
printf("Invalid argument supplied\n");
return 0;
}

3. To compile and run the program, copy the source code into a .c file and compile it.
Module 5: Plug Things Together
4. Once the program is compiled successfully, execute the program using the
respective command.
5. The program has two parts – a server part that can be executed using ‘server’ as
command line argument, and client that is executed using ‘client’ as command line
argument.
6. The output returned is:
Run as server: <executable name> server
Run as client: <executable name> client
Module 5: Plug Things Together
Makefile
1. This lab helps you in understanding makefile and writing one for compiling
programs.
2. The program comprises of three C files and a makefile that need to be copied in a
single directory to perform compilation.
3. Create a directory named include in the current directory.
4. Save the contents of math_utils.h inside the directory.
5. The program code is as below:
math_utils.c
int add(int x, int y){
return (x + y);
}

math_utils.h
int add(int x, int y);

math_user.c
#include<stdio.h>
#include<math_utils.h>

int main(void){

printf("ADD RESULT: %d\n",add(12,10));

makefile
# Directory structure
# .c files
# makefile
# |--- include
# |--- lib
# |--- bin
# |--- obj

CC=gcc
DEP_HEADERS=math_utils.h
MKDIR_P=mkdir -p

ODIR=./obj
BINDIR=./bin
Module 5: Plug Things Together

IDIR=./include
LDIR=./lib

CFLAGS=-I$(IDIR)
LFLAGS=-L$(LDIR)

LIBS=-lm

OFILES = math_utils.o math_user.o


OBJ = $(patsubst %,$(ODIR)/%,$(OFILES))
DEPS = $(patsubst %,$(IDIR)/%,$(DEP_HEADERS))

# rule to execute all prerequisites


all: directory demo

directory:
$(MKDIR_P) $(ODIR) $(BINDIR)

# rule to generate .o files from .c file


$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

# rule for generating exeutable


demo: $(OBJ)
$(CC) -o $(BINDIR)/$@ $^ $(CFLAGS) $(LFLAGS) $(LIBS)

# remove .o and demo executable


clean:
rm $(ODIR)/*.o $(BINDIR)/demo

# remove obj and bin directories


cleanall:
rm -rf $(ODIR) $(BINDIR)

6. To compile the program, type the following command at the command prompt from
the current directory which contains two C files and a makefile.
make all
7. You can also try and see the behavior by typing other rules such as directory, clean,
and cleanall.
Module 5: Plug Things Together
Creating and using static library
1. For creating and using a static library, write a program that consists of three files.
2. You have to copy all the three files to a directory and then compile them.
3. The code for creating and using the static library is:
math_utils.c
int add(int x, int y){
return (x + y);
}
math_utils.h
int add(int, int);

math_user.c
#include<stdio.h>
#include<math_utils.h>

int main(void){

printf("ADD RESULT: %d\n",add(12,10));

4. The generation of a library is a three-step process, where the first step generates an
object file and the second process generates a static library file.
5. The third step consists of compiling the program executable.
6. Type the following command in sequence at the command prompt.

gcc -c math_utils.c
This command will generate an object file math_utils.o
7. Now, type the following command to generate the library libmutila.a

ar rc libmutils.a math_utils.o
The library must have been generated by the above command in the current
directory.
8. Now, generate the executable binary by linking to the library file.

gcc math_user.c -o math_user –I./ -L./ -lmutils


The above command should generate the binary math_user in the current directory.
9. Execute the binary which must return the output as shown below:

ADD RESULT: 22
Module 5: Plug Things Together
Creating and using Shared/Dynamic library
1. To create and use a shared or dynamic library, first create a program that consists of
three files in total.
2. Copy all the three files to a directory and then compile them.
3. The code for this purpose is given below:
math_utils.c
int add(int x, int y){
return (x + y);
}
math_utils.h
int add(int, int);

math_user.c
#include<stdio.h>
#include<math_utils.h>

int main(void){

printf("ADD RESULT: %d\n",add(12,10));

4. The generation of a library is a three-step process where the first step generates an
object file, and the second process generates shared library file.
5. The third step includes compiling the program executable.
6. Type the following command in sequence at the command prompt.
gcc –c math_utils.c
The above command would generate an object file math_utils.o
7. Type the following command to generate shared library libmutils.so
gcc -shared math_utils.o -o libmutils.so
The library must be generated by the above command in the current directory.
8. Now, generate the executable binary by linking to the library file.
gcc math_user.c -o math_user –I./ -L./ -lmutils
The above command should generate the binary math_user in the current directory.
9. Execute the binary which should output output as shown below.

ADD RESULT: 22

You might also like