Skip to content

Commit 0bb7b1c

Browse files
committed
initial commit
0 parents  commit 0bb7b1c

File tree

10 files changed

+747
-0
lines changed

10 files changed

+747
-0
lines changed

HW.pdf

113 KB
Binary file not shown.

Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
server: ./src/server.c client ./src/helpers.c
3+
gcc -g -pthread -Wall ./src/server.c ./src/helpers.c -o $@
4+
5+
client: ./src/client.c ./src/helpers.c
6+
gcc -g -pthread -Wall ./src/client.c ./src/helpers.c -o $@
7+
8+
clean:
9+
rm -f server client && rm -f ./logs/*.txt && clear
10+
11+
clear:
12+
rm -f server client && rm -f ./logs/*.txt && clear
13+
14+
reset:
15+
rm -f server client && rm -f ./logs/*.txt && killall client && clear
16+
17+
run: server client
18+
./server test.txt 2 10

README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Inter Process Communication
2+
3+
This repository contains an OS assigment concerning IPC and its solution. To better understand what the program does I suggest you read the assignment first, however it is written in Greek, so for those who don't know Greek there is a summary in English below.
4+
5+
# Assignment
6+
7+
Create a program that has one parent process and N child processes that read/write from a buffer. The parent process has access to a .txt file from which it writes a specific segment to the buffer and only it can write in the buffer, also the child processes may only read from the buffer. Multiple children can read at a time but the parent can't write when there is at least one child still reading. The way children read is by sending requests about a specific segment from the .txt file and a specific line from the segment,if the segment they are requesting is the one currently at the buffer they may read the line, if not they wait in Queue for the segment to change. The strategy used to change segments is a simple FIFO strategy. The server(parent process) will change the segment only when there is no other request for the current segment available and there are child processes waiting for some other segment.The .txt file to read from as well as the number of lines each segment of the .txt file will contain and the number of requests each child will make are passed as arguments when running the executable(e.g ./server test.txt 2 2 will read from test.txt with each segment containing 2 lines and each child sending 2 requests). The way child processes create requests is as follows with 30% probability choose a new random segment,with 70% probability choose the segment of your previous request, the line requested from the segment is completely random. After each request each child process will write in its log file the time it took to get the requested line and the server logs when each segment entered the buffer and when each segment exited the buffer.
8+
9+
# Compiling and running
10+
11+
To compile the files run ` make ` and to run the executable ` server ` type ` ./server file.txt X Y ` where file.txt is the file from which lines will be requested, X is an integer that tells us how many lines each segment will have and Y is an integer that corresponds to the amount of requests each child process will make.As an example run ` make run ` to see the program running with the file ` test.txt ` and 2 lines per segment and 10 requests per child. In order the clean the log files and .o files run `make clean`.
12+
13+
# Server
14+
The server in this implementation is only responsible for changing the segment currently inside the buffer. Before the main loop begins the server calls the helper functions to get the number of lines per segment,the maximum line and the number of requests per child. After that it initiates the semaphores as well as the shared memory(posix library used). For shared memory we use a struct shared_obj which holds information about the segment currently in use,the next segment to be put as well as the number of children waiting in queue, in addition we have a shared memory string that will be used as our buffer. To synchronize the processes we use 4 semaphores:
15+
- `write_server`: signals the server so that it changes the segment to the next requested segment or quits if all children are done requesting.
16+
- `ready`: signals to the child processes that the server is done changing the segment and they may continue reading.
17+
- `critical`: mutex semaphore used so only one process writes at the shared_obj struct.
18+
- `queue`: semaphore used to block child processes that are waiting in queue.
19+
20+
After the initialization of the semaphores and the shared memory the server starts creating child processes and waits for the signal to change the current segment. After all childrens' requests have been granted the server is signaled to exit.
21+
22+
23+
# Client
24+
Each client runs the file `client.c` where lines per segment,max line and total requests are passed as arguments as well as the id of the shared_obj, After linking to the shared memory and to the named semaphores the client starts sending requests in a loop until they have completed sending the requests.
25+
- If the flag `new` is set to 1 then the client makes a new request
26+
- Then they check whether the current segment is the same as the one they are requesting
27+
- If it is they read the line and `continue` to create another request
28+
- If it isn't they go to wait in the queue. In case they are the first to enter the queue they change the `next_seg` to their requested segment and wait in the queue. In case by them going to the queue now all alive processes(meaning all client processes still running) are waiting in the queue they signal the server to change the segment and they release all the other processes from the blocked semaphore. At the end they release the `critical` mutex semaphore so other clients can enter the queue.
29+
30+
After exiting the loop and completing the requests they check the following:
31+
- If by them exiting now all other processes are waiting in queue they signal the server and release the other children.
32+
- In the other case that they are the last child process to exit they signal the server to also exit from the loop of changing segments.
33+

include/helpers.h

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
char* get_line(const char* str, int line_num);
2+
void get_request(int old_sec, int *req_line, int *req_sec, int total_lines, int lines_per_sec, int max_sec);
3+
char *get_sector(char *source,int sector,int lines_per_sector,int max_line);
4+
int get_max_line(char *file);
5+
int get_lines(char *file);

include/struct.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#define WR_SERV "/server123"
2+
#define READY "/ready123"
3+
#define CS "/critical123"
4+
#define QUEUE "/fqueue123"
5+
#define N 10
6+
#define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP )
7+
typedef struct shared_obj{
8+
int ID_str;
9+
int in_q;
10+
int curr_seg;
11+
int next_seg;
12+
int dead;
13+
}shared_obj;

logs/Readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Folder to store logs from clients and server

src/client.c

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <semaphore.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include <sys/shm.h>
6+
#include <sys/stat.h>
7+
#include <fcntl.h>
8+
#include <sys/types.h>
9+
#include <unistd.h>
10+
#include <sys/types.h>
11+
#include <sys/wait.h>
12+
#include <time.h>
13+
#include <errno.h>
14+
#include "../include/struct.h"
15+
#include"../include/helpers.h"
16+
int main(int argc, char **argv)
17+
{
18+
//Initialize Random generator based on pid so each process has a different rand
19+
srand(time(NULL) - getpid());
20+
clock_t start, end;
21+
//Temporarily create a string to open a log file
22+
char *proc_id = malloc(sizeof(char) * 100);
23+
if(proc_id==NULL){
24+
perror("malloc at proc_id");
25+
exit(20);
26+
}
27+
sprintf(proc_id, "./logs/%d_log.txt", getpid());
28+
29+
//Delete previous log file in case proccess id is the same as a previous one
30+
if(remove(proc_id)==0){
31+
printf("Removed File succcessfully\n");
32+
}
33+
34+
FILE *logs;
35+
//Get necessary integers from arguments
36+
int id = atoi(argv[0]); //Id for memory block
37+
int max_sec = atoi(argv[1]); //Number of sections
38+
int max_line = atoi(argv[2]); //Number of chars in max line
39+
int loop = atoi(argv[3]); //Total requests to be sent
40+
int total_lines = atoi(argv[4]); //Total lines in each segment
41+
char *line;
42+
if (id == 0)
43+
{
44+
perror("Error at getting shm id from parent");
45+
exit(14);
46+
}
47+
//Attach memory
48+
shared_obj *memory = (shared_obj *)shmat(id, NULL, 0);
49+
if (memory == (void *)-1)
50+
{
51+
perror("Attachment.");
52+
exit(15);
53+
}
54+
// Attach shared string
55+
char *shared_string = (char *)shmat(memory->ID_str, NULL, 0);
56+
if(shared_string==(char *)-1){
57+
perror("Error at attaching child to shared_string");
58+
exit(16);
59+
}
60+
61+
//Open created semaphores for usage
62+
sem_t *write_server = sem_open(WR_SERV, 0);
63+
sem_t *ready = sem_open(READY, 0);
64+
sem_t *critical = sem_open(CS, 0);
65+
sem_t *queue = sem_open(QUEUE, 0);
66+
67+
//Variables needed
68+
int new = 1,old_req=-1,req_line,req_sec;
69+
70+
while (loop > 0)
71+
{
72+
//Create a new request
73+
if (new == 1)
74+
{
75+
get_request(old_req, &req_line, &req_sec, total_lines, max_line, max_sec);
76+
start = clock();
77+
old_req = req_sec;
78+
printf("Client: %d made request <%d,%d>\n", getpid(), req_line, req_sec);
79+
fflush(stdout);
80+
//Make new 0 so that if the loop come backs we dont create a new request and discard current
81+
new = 0;
82+
}
83+
84+
//In case requested segment is current segment go ahead and read
85+
if (req_sec == memory->curr_seg)
86+
{
87+
end = clock(); //Stop Clock
88+
line = get_line(shared_string, req_line); //Read requested line
89+
logs = fopen(proc_id, "a"); //Open log file in log folder
90+
fprintf(logs, "Response Time:%fs,<%d,%d>,Line:%s\n", ((double)(end - start)) / CLOCKS_PER_SEC, req_line, req_sec, line); //Write in logs
91+
printf("Client: %d got request <%d,%d>\n", getpid(), req_line, req_sec); //Print to let user know that request got completed
92+
loop--; //One request done
93+
new = 1; //Create new request
94+
fclose(logs);
95+
usleep(20000); //Sleep for 20ms
96+
continue;
97+
}
98+
else
99+
{
100+
//In case current section available isn't requested section wait for critical section
101+
//to change the variable in memory
102+
sem_wait(critical);
103+
printf("Client: %d got queued\n", getpid());
104+
fflush(stdout);
105+
//If u are first to enter Queue make your requested segment the next to be put
106+
if (memory->in_q == 0)
107+
{
108+
memory->next_seg = req_sec;
109+
printf("Server: Next segment will be %d\n", memory->next_seg);
110+
fflush(stdout);
111+
}
112+
113+
//Increase the number of processes waiting in queue
114+
memory->in_q++;
115+
if (memory->in_q == (N - memory->dead))
116+
{
117+
memory->in_q--;
118+
//In case the Queue is now full with all alive processes call server to change segment
119+
sem_post(write_server);
120+
sem_wait(ready);
121+
//Give critical to the next process
122+
sem_post(critical);
123+
}
124+
else
125+
{
126+
//If you Queue isn't Full give back critical and wait in Queue
127+
sem_post(critical);
128+
sem_wait(queue);
129+
}
130+
}
131+
}
132+
133+
//process exits and waits for critical to update number of dead processes
134+
sem_wait(critical);
135+
memory->dead++;
136+
137+
//In Case Queue is now full because all other processes are waiting in Queue or all processes are dead
138+
//change Segment and unblock
139+
140+
if (memory->in_q == N - memory->dead)
141+
{
142+
//Post to server so that segment updates
143+
sem_post(write_server);
144+
sem_wait(ready);
145+
146+
}
147+
//Give back critical
148+
sem_post(critical);
149+
150+
//Close open File and exit
151+
free(proc_id);
152+
return 0;
153+
}

0 commit comments

Comments
 (0)