Κατέβασμα παρουσίασης
Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε
1
Διαδιεργασιακή επικοινωνία
Σωληνώσεις, κατονομασμένες σωληνώσεις, ουρές μηνυμάτων, σημαφόροι, διαμοιραζόμενη μνήμη, δίοδοι, ροές
2
Γενικά Η διαδιεργασιακή επικοινωνία επιτρέπει στις διεργασίες:
Να συγχρονίζονται Να ανταλλάσσουν δεδομένα Υπάρχουν πολλαπλοί εναλλακτικοί τρόποι που υποστηρίζονται από διαφορετικά πρότυπα/υλοποιήσεις: Τεχνική POSIX XPG3 SVR3 SVR4 4.3BSD 4.3+BSD Σωληνώσεις μίας κατεύθυνσης Κατονομασμένες σ.μ.κ. Αμφικατευθυνόμενες σωληνώσεις Κατονομασμένες α.σ. Ουρές μηνυμάτων Σημαφόροι Διαμοιραζόμενη μνήμη Δίοδοι Ροές
3
Σωληνώσεις μίας κατεύθυνσης
Η παλαιότερη μορφή διαδιεργασιακής επικοινωνίας Μία σωλήνωση μίας κατεύθυνσης είναι ένα ζεύγος περιγραφέων αρχείων p[0] και p[1] Ό,τι γράφεται στον περιγραφέα αρχείων p[1] (εγγραφής μόνο) είναι διαθέσιμο προς ανάγνωση από τον περιγραφέα αρχείων p[0] (ανάγνωσης μόνο) Δημιουργία σωληνώσεων μίας κατεύθυνσης #include <unistd.h> int pipe(int filedes[2]); Και οι δύο περιγραφείς αρχείων δημιουργούνται στην ίδια διεργασία Κληροδοτούνται όμως στις θυγατρικές Οι σωληνώσεις μπορούν να χρησιμοποιηθούν για επικοινωνία διεργασιών με κοινό πρόγονο (που θα φροντίσει για την εγκαθίδρυση του διαύλου επικοινωνίας)
4
Δημιουργία σωληνώσεων
Διεργασία Γονική διεργασία Θυγατρική διεργασία fork fd[0] fd[1] fd[0] fd[1] fd[0] fd[1] σωλήνωση σωλήνωση Πυρήνας Πυρήνας (α) Δημιουργία σωλήνωσης (β) Σωλήνωση μετά τη fork()
5
Δημιουργία σωληνώσεων
Γονική διεργασία Θυγατρική διεργασία Γονική διεργασία Θυγατρική διεργασία fork fork fd[0] fd[1] fd[1] fd[0] σωλήνωση σωλήνωση Πυρήνας Πυρήνας Αποστολή δεδομένων από τη θυγατρική στη γονική διεργασία Αποστολή δεδομένων από τη γονική στη θυγατρική διεργασία (γ) Αποσαφήνιση ρόλων με κλείσιμο περιγραφέων
6
Εγγραφή/ανάγνωση σε κλειστές σωληνώσεις
Η ανάγνωση από σωληνώσεις όπου το άκρο εγγραφής έχει κλείσει έχει ως αποτέλεσμα: Την ανάγνωση δεδομένων που έχουν γραφεί πριν το κλείσιμο και δεν έχουν διαβαστεί ακόμη, εφ’ όσον υπάρχουν. Η read επιστρέφει το πλήθος των bytes H read να επιστρέφει την τιμή 0 προς υπόδειξη του τέλους αρχείου, αν δεν υπάρχουν άλλα δεδομένα Η εγγραφή σε σωληνώσεις που το άκρο ανάγνωσης έχει κλείσει οδηγεί στην αποστολή του σήματος SIGPIPE. Αν το σήμα αγνοείται ή οδηγείται σε χειριστή, η write θα επιστρέψει –1 και η μεταβλητή errno θα τεθεί στην τιμή EPIPE
7
Παράδειγμα [pipe1.c] #include <stdio.h>
#include <unistd.h> #include <errno.h> #include <stdlib.h> #define MAXLINE 512 int main(void) { int n, fd[2]; pid_t pid; char line[MAXLINE]; if (pipe(fd) < 0) {perror("creating pipe"); exit(1); } if ((pid = fork()) < 0) {perror("cannot fork"); exit(1); } else if (pid > 0) { /* γονική διεργασία */ close(fd[0]); /* κλείσιμο του άκρου ανάγνωσης */ write(fd[1], "message through pipe\n", 21); close(fd[1]); } else { /* θυγατρική διεργασία */ close(fd[1]); /* κλείσιμο του άκρου εγγραφής */ n = read(fd[0], line, MAXLINE); write(STDOUT_FILENO, line, n); close(fd[0]); } exit(0);
8
Παράδειγμα 2 [pipe2.c] int main(void) {
int n, fd[2]; pid_t pid; char buff[512]; if (pipe(fd) < 0) {perror("creating pipe"); exit(1); } if ((pid = fork()) < 0) {perror("cannot fork"); exit(1); } else if (pid > 0) { /* γονική διεργασία */ close(fd[0]); /* κλείσιμο του άκρου ανάγνωσης */ for (n = 0; n < 100; n++) { sprintf(buff, "Message %d through pipe\n", n); write(fd[1], buff, strlen(buff)); } close(fd[1]); waitpid(pid, NULL, 0); } else { /* θυγατρική διεργασία */ close(fd[1]); /* κλείσιμο του άκρου εγγραφής*/ if (fd[0] != STDIN_FILENO) if (dup2(fd[0], STDIN_FILENO) == -1) {perror("dup2"); exit(1);} close(fd[0]); execlp("more", "more", NULL); perror("execlp returned!"); exit(0);
9
popen, pclose Η διαδικασία «δημιουργία θυγατρικής διεργασίας, διευθέτηση κανονικής εισόδου (ή εξόδου)» είναι αρκετά συνηθισμένη Το πρότυπο POSIX παρέχει ειδικές συναρτήσεις βιβλιοθήκης, popen, pclose #include <stdio.h> FILE *popen(const char *cmdstr, const char *type); int pclose(FILE *fp); Η παράμετρος cmdstr είναι μία γραμμή εντολών που εκτελείται μέσω του φλοιού Bourne (sh –c cmdstr) Η παράμετρος type καθορίζει την κατεύθυνση Αν είναι "r" ο δείκτης αρχείου είναι ανάγνωσης μόνο και συνδέεται με την κανονική έξοδο της νέας διεργασίας Αν είναι "w" ο δείκτης αρχείου είναι εγγραφής μόνο και συνδέεται με την κανονική είσοδο της νέας διεργασίας
10
popen, pclose Γονική διεργασία Θυγατρική διεργασία fp stdout
mode = "r" Γονική διεργασία Θυγατρική διεργασία fp stdin mode = "w"
11
popen, pclose – Παράδειγμα [popen.c]
#include <stdio.h> #include <errno.h> #include <stdlib.h> int main(void) { int n; FILE *pipe2child; if ((pipe2child = popen("more", "w")) == NULL) { perror("creating pipe"); exit(1); } for (n = 0; n < 100; n++) { if (fprintf(pipe2child, "Message %d through pipe\n", n) < 0) { perror("writing line");} } pclose(pipe2child); return 0;
12
popen, pclose – Παράδειγμα [popen1.c]
#include <stdio.h> #include <errno.h> #include <stdlib.h> int main(void) { pid_t pid; FILE *pipe2parent; char buff[1024]; if ((pipe2parent = popen("ls -lt *.c", "r")) == NULL) { perror("creating pipe"); exit(1); } while (fgets(buff, 1024, pipe2parent) != NULL) printf("%s", buff); if (ferror(pipe2parent)) perror("reading data"); pclose(pipe2parent); return 0; }
13
Υλοποίηση popen, pclose
static pid_t childpid[OPEN_MAX]; FILE * popen(const char *cmdstring, const char *mod) { int i, pf[2]; pid_t pid; FILE *fp; if ((mod[0]!='r' && mod[0]!='w') || mod[1]!='\0’) { /* only "r", "w" */ errno = EINVAL; /* required by POSIX.2 */ return(NULL); } if (pipe(pf) < 0) return NULL; /* errno set by pipe() */ if ( (pid = fork()) < 0) { close(pf[0]); close(pf[1]); return NULL; /* errno set by fork() */ else if (pid == 0) { /* child */ if (*mod == 'r') { close(pf[0]); if (pf[1] != STDOUT_FILENO){dup2(pf[1], STDOUT_FILENO); close(pf[1]);} } else { /* *mod == 'w' */ close(pf[1]); if (pf[0] != STDIN_FILENO) {dup2(pf[0], STDIN_FILENO); close(pf[0]);} } /* close all descriptors in childpid[] */ for (i = 0; i < maxfd; i++) if (childpid[i] > 0) close(i); execl(SHELL, "sh", "-c", cmdstring, (char *) 0); _exit(127);
14
Υλοποίηση popen, pclose
/* συνέχεια popen - parent */ if (*type == 'r') { close(pf[1]); if ( (fp = fdopen(pf[0], type)) == NULL) return(NULL); } else { close(pf[0]); if ( (fp = fdopen(pf[1], type)) == NULL) return(NULL); } childpid[fileno(fp)] = pid; /* remember child pid for this fd */ return(fp); int pclose(FILE *fp) { int fd, stat; pid_t pid; fd = fileno(fp); if ( (pid = childpid[fd]) == 0) return(-1); /* fp not a popen() file */ childpid[fd] = 0; if (fclose(fp) == EOF) return(-1); while (waitpid(pid, &stat, 0) < 0) if (errno != EINTR) return(-1); /* error other than EINTR from waitpid() */ return(stat); /* return child's termination status */
15
Αμφίδρομη επικοινωνία
Οι σωληνώσεις, όπως υποστηρίζονται από το POSIX και αρκετές υλοποιήσεις, είναι μονόδρομες Για αμφίδρομη επικοινωνία χρειαζόμαστε δύο μονόδρομες σωληνώσεις, μία προς κάθε κατεύθυνση Γονική διεργασία Θυγατρική διεργασία fd1[0] fd1[1] fd2[1] fd2[0]
16
Αμφίδρομη επικοινωνία – Παράδειγμα [bi-pipe1.c]
int main(void) { int num, dbl, nbytes, pipe2child[2], pipe2parent[2]; pid_t pid; if (pipe(pipe2child) == -1) {perror("creating pipe2child"); exit(1);} if (pipe(pipe2parent) == -1) {perror("creating pipe2parent"); exit(1);} if ((pid = fork()) == -1) {perror("cannot fork"); exit(1);} else if (pid > 0) { /* parent */ close(pipe2child[0]); close(pipe2parent[1]); for (num = 0; num < 20; num++) { if (write(pipe2child[1], &num, sizeof(int)) != sizeof(int)) { perror("sending data to child"); break;} if (read(pipe2parent[0], &dbl, sizeof(int)) != sizeof(int)) { perror("getting data from child"); break; } printf("2 * %d = %d\n", num, dbl); } close(pipe2child[1]); close(pipe2parent[0]); else { /* child */ while ((nbytes = read(pipe2child[0], &num, sizeof(int))) == sizeof(int)) { dbl = 2 * num; if (write(pipe2parent[1], &dbl, sizeof(int)) != sizeof(int)) { perror("sending data to parent"); break; } return 0;}
17
Προσοχή στην ενδιάμεση μνήμη! [bi-pipe-buf.c]
int main(void) { int num, dbl, nbytes, pipe2child[2], pipe2parent[2]; pid_t pid; FILE *infp, *outfp; if (pipe(pipe2child) == -1) {perror("creating pipe2child"); exit(1);} if (pipe(pipe2parent) == -1) {perror("creating pipe2parent"); exit(1);} if ((pid = fork()) == -1) {perror("cannot fork"); exit(1);} else if (pid > 0) { /* parent */ close(pipe2child[0]); close(pipe2parent[1]); infp = fdopen(pipe2parent[0], "r"); outfp = fdopen(pipe2child[1] , "w"); for (num = 0; num < 20; num++) { if (fprintf(outfp, "%d\n", num ) < 0) { perror("sending data to child"); break;} if (fscanf(infp, "%d", &dbl) != 1) { perror("getting data from child"); break; } printf("2 * %d = %d\n", num, dbl); } fclose(infp); fclose(outfp); } else { /* child */ close(pipe2child[1]); close(pipe2parent[0]); infp = fdopen(pipe2child[0] , "r"); outfp = fdopen(pipe2parent[1] , "w"); while ((num = fscanf(infp, "%d", &dbl)) == 1) { dbl = 2 * num; if (fprintf(outfp, "%d\n", dbl) < 0) { perror("sending data to parent"); break; } return 0;}
18
Κατονομασμένες σωληνώσεις
Οι σωληνώσεις της popen εξασφαλίζουν επικοινωνία μεταξύ διεργασιών με κοινό πρόγονο (που θα φροντίσει για την εγκαθίδρυση του διαύλου επικοινωνίας) Για την επικοινωνία διεργασιών χωρίς κοινό πρόγονο μπορούν να χρησιμοποιηθούν οι κατονομασμένες σωληνώσεις (ή FIFOs) Οι κατονομασμένες σωληνώσεις μπορούν να χρησιμοποιηθούν: Για επικοινωνία μίας κατεύθυνσης μεταξύ δύο διεργασιών Για σχήμα επικοινωνίας όπου πολλές διεργασίες αποστέλλουν δεδομένα και μία διεργασία τα διαβάζει Φυσικά μπορούν να χρησιμοποιηθούν και για επικοινωνία διεργασιών που έχουν κοινό πρόγονο, αντί για απλές σωληνώσεις Μία κατονομασμένη σωλήνωση απεικονίζεται σε ένα αντικείμενο του συστήματος αρχείων, το οποίο μπορεί να ανοιχθεί, γραφεί, διαβαστεί, διαγραφεί κ.λπ. όπως τα κανονικά αρχεία
19
Κατονομασμένες σωληνώσεις
Τρόπος λειτουργίας: Μία διεργασία δημιουργεί την κατονομασμένη σωλήνωση #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); Π.χ. mkfifo("/tmp/myfifo", 0622); Μία διεργασία ανοίγει την κατονομασμένη σωλήνωση για ανάγνωση (συνήθως η ίδια που τη δημιούργησε) fd = open("/tmp/myfifo", O_RDONLY); nbytes = read(fd, buff, byteCount); Μία ή περισσότερες διεργασίες ανοίγουν τη σωλήνωση για εγγραφή fd = open("/tmp/myfifo", O_WRONLY); nbytes = write(fd, buff, byteCount); Η σύνδεση των δύο άκρων γίνεται μέσω του ονόματος της σωλήνωσης
20
Κατονομασμένες σωληνώσεις
Διεργασία 1 Κατονομασμένη σωλήνωση (1) mkfifo(…) FIFO (2) open(…, O_RDONLY) (4) read(…) (6) close(…) (7) unlink(…) (3) open(…, O_WRONLY) (4) write(…) (5) close(…) Διεργασία 2, 3 κ.λπ.
21
Κατονομασμένες σωληνώσεις [npipe1.c]
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <errno.h> #define FIFO_NAME "/tmp/mylog" int main(void) { int fd, nbytes; char buff[512]; if (mkfifo(FIFO_NAME, 0600) == -1) { perror("cannot mkfifo"); exit(1); } if ((fd = open(FIFO_NAME, O_RDONLY)) == -1) { perror("opening FIFO"); unlink(FIFO_NAME); exit(1); } while (read(fd, buff, 512) > 0) printf("%s", buff); close(fd); if (unlink(FIFO_NAME) == -1) {perror("cannot unlink");} return 0; }
22
Κατονομασμένες σωληνώσεις
Οι κατονομασμένες σωληνώσεις είναι μονής κατευθύνσεως. Αν χρειάζεται επιστροφή απάντησης, απαιτείται χρήση 2ης κατονομασμένης σωλήνωσης Αν έχουμε πολλαπλούς εγγραφείς πρέπει: Ο κάθε εγγραφέας να έχει το δικό του κανάλι επιστροφής απάντησης (κατονομασμένη σωλήνωση) Ο αναγνώστης να είναι σε θέση να ξέρει σε πιο κανάλι θα διοχετεύσει την απάντηση αναγνώστης write write read Κανάλι επιστροφής 1 Κανάλι αιτήσεων Κανάλι επιστροφής 2 write write read read Εγγραφέας 1 Εγγραφέας 2
23
Κατονομασμένες σωληνώσεις – Αναγνώστης [npipe-srv.c]
#define FIFO_NAME "/tmp/mylog" #define MAX_STR 512 typedef struct {char str[MAX_STR]; char replyPipe[L_tmpnam + 1];} message; int main(void) { int fd, nbytes, replyFd, ctr; char *strStart; message m; if (mkfifo(FIFO_NAME, 0622) == -1) { perror("cannot mkfifo"); exit(1); } if ((fd = open(FIFO_NAME, O_RDONLY)) == -1) { perror("opening FIFO"); unlink(FIFO_NAME); exit(1); } while (1) { nbytes = read(fd, &m, sizeof(m)); if (nbytes > 0) { for (strStart = m.str; *strStart; strStart++) *strStart = toupper(*strStart); printf("reply is %s; posted to %s\n", m.str, m.replyPipe); if ((replyFd = open(m.replyPipe, O_WRONLY)) != -1) { write(replyFd, m.str, strStart - m.str + 1); close(replyFd); } else perror("cannot open reply channel"); close(fd); if (unlink(FIFO_NAME) == -1) {perror("cannot unlink");} return 0; }
24
Κατονομασμένες σωληνώσεις – Εγγραφέας [npipe-clnt.c]
#define FIFO_NAME "/tmp/mylog" #define MAX_STR 512 typedef struct {char str[MAX_STR]; char replyPipe[L_tmpnam + 1];} message; int main(void) { int fd, nbytes, replyFd = -1, ctr, strStart; message m; char buff[MAX_STR]; if (tmpnam(m.replyPipe) == NULL) {perror("generating pipe name"); exit(1);} printf("input pipe is %s\n", m.replyPipe); if (mkfifo(m.replyPipe, 0622) == -1) {perror("cannot mkfifo"); exit(1); } if ((fd = open(FIFO_NAME, O_WRONLY)) == -1) { perror("opening request FIFO"); unlink(m.replyPipe); exit(1); } while (1) { printf("Enter input: "); gets(m.str); nbytes = write(fd, &m, sizeof(m)); if ((replyFd = open(m.replyPipe, O_RDONLY)) == -1) { perror("opening reply FIFO"); unlink(m.replyPipe); exit(1); } if ((nbytes = read(replyFd, buff, 512)) < 0) perror("reading reply"); printf("reply = %.*s\n", nbytes, buff); close(replyFd); } close(fd); if (unlink(m.replyPipe) == -1) {perror("cannot unlink");} return 0; }
25
Συμπεριφορά κατονομασμένων σωληνώσεων
Οι αναγνώσεις διατηρούν τα όρια των αιτήσεων εγγραφής, εφ’ όσον η κάθε μία είναι μικρότερη σε μέγεθος από PIPE_BUF Μία αίτηση εγγραφής γράφει 100 bytes και μία δεύτερη 50 Η αίτηση ανάγνωσης για 500 bytes θα επιστρέψει τα πρώτα 100 Η κλήση συστήματος για το άνοιγμα μιας κατονομασμένης σωλήνωσης (open): αναστέλλει τον αναγνώστη μέχρι να ανοιχθεί η σωλήνωση και από εγγραφέα αναστέλλει τον εγγραφέα μέχρι να ανοιχθεί η σωλήνωση και από αναγνώστη Η συμπεριφορά αυτή μπορεί να τροποποιηθεί αν στον τρόπο προσπέλασης στην αίτηση ανοίγματος παρατεθεί η ένδειξη O_NONBLOCK To άνοιγμα για ανάγνωση επιστρέφει αμέσως, ακόμη κι αν δεν υπάρχουν εγγραφείς Το άνοιγμα για εγγραφή, αν δεν υπάρχουν αναγνώστες, αποτυγχάνει και θέτει το errno στην τιμή ENXIO
26
Συμπεριφορά κατονομασμένων σωληνώσεων
Το άνοιγμα με O_NONBLOCK όμως επηρεάζει τις read και write: To read σε μία σωλήνωση χωρίς δεδομένα και η οποία δεν είναι ανοικτή από καμία διεργασία για εγγραφή επιστρέφει 0 (τέλος αρχείου) To read σε μία σωλήνωση χωρίς δεδομένα και η οποία είναι ανοικτή από τουλάχιστον μία διεργασία για εγγραφή επιστρέφει -1 και θέτει το errno στην τιμή EAIGAIN Η εγγραφή για πλήθος δεδομένων nbytes <= PIPE_BUF επιστρέφει nbytes, αν επιτύχει, ή –1 αν η ενδιάμεση μνήμη είναι γεμάτη και θέτει τη μεταβλητή errno στην τιμή EAIGAIN Η εγγραφή για πλήθος δεδομένων nbytes > PIPE_BUF επιστρέφει το πλήθος των bytes που γράφτηκαν (αν η ενδιάμεση μνήμη δεν ήταν γεμάτη) -1 και θέτει το errno στην τιμή EAIGAIN, αν δεν γράφτηκαν καθόλου bytes To O_NONBLOCK του ανοίγματος πιθανόν να μπορεί να καθαριστεί με την κατάλληλη κλήση της fcntl: fcntl(readPipe, F_SETFL, O_RDONLY); fcntl(writePipe, F_SETFL, O_WRONLY);
27
Κατονομασμένες σωληνώσεις – Αναγνώστης [npipe-srv1.c]
#define FIFO_NAME "/tmp/mylog" #define MAX_STR 512 typedef struct {char str[MAX_STR]; char replyPipe[L_tmpnam + 1];} message; int main(void) { int fd, nbytes, replyFd, ctr; char *strStart; message m; if (mkfifo(FIFO_NAME, 0622) == -1) {perror("cannot mkfifo"); exit(1); } if ((fd = open(FIFO_NAME, O_RDONLY)) == -1) { perror("opening FIFO"); unlink(FIFO_NAME); exit(1); } while (1) { if (read(fd, &m, sizeof(message)) != sizeof(message)) {perror("reading fifo"); exit(1);} if ((replyFd = open(m.replyPipe, O_WRONLY)) == -1) { perror("Opening reply pipe"); exit(1); } while (m.str[0]) { for (strStart = m.str; *strStart; strStart++) *strStart = toupper(*strStart); printf("reply is %s; posted to %s\n", m.str, m.replyPipe); write(replyFd, m.str, strStart - m.str + 1); read(fd, m.str, MAX_STR); close(replyFd); close(fd); if (unlink(FIFO_NAME) == -1) {perror("cannot unlink");} return 0; }
28
Κατονομασμένες σωληνώσεις – Εγγραφέας [npipe-clnt1.c]
#define FIFO_NAME "/tmp/mylog" #define MAX_STR 512 typedef struct {char str[MAX_STR]; char replyPipe[L_tmpnam + 1];} message; int main(void) { int fd, nbytes, replyFd, ctr=0; message m; char buff[MAX_STR]; if (tmpnam(m.replyPipe) == NULL) {perror("generating pipe name"); exit(1);} printf("input pipe is %s\n", m.replyPipe); if (mkfifo(m.replyPipe, 0622) == -1) {perror("cannot mkfifo"); exit(1); } if ((replyFd = open(m.replyPipe, O_RDONLY)) == -1) { perror("opening reply FIFO"); unlink(m.replyPipe); exit(1); } if ((fd = open(FIFO_NAME, O_WRONLY)) == -1) { perror("opening request FIFO"); unlink(m.replyPipe); exit(1); } while (1) { printf("Enter input: "); gets(m.str); if (ctr++ == 0) write(fd, &m, sizeof(message)); else write(fd, m.str, strlen(m.str) + 1); if (m.str[0] == '\0') break; nbytes = read(replyFd, buff, MAX_STR); printf("%d bytes read: %s\n", nbytes, m.str); } close(fd); close(replyFd); if (unlink(m.replyPipe) == -1) {perror("cannot unlink");} return 0; }
29
Κατονομασμένες σωληνώσεις – Εγγραφέας [npipe-clnt1_1.c]
#define FIFO_NAME "/tmp/mylog" #define MAX_STR 512 typedef struct {char str[MAX_STR]; char replyPipe[L_tmpnam + 1];} message; int main(void) { int fd, nbytes, replyFd, ctr=0; message m; char buff[MAX_STR]; if (tmpnam(m.replyPipe) == NULL) {perror("generating pipe name"); exit(1);} printf("input pipe is %s\n", m.replyPipe); if (mkfifo(m.replyPipe, 0622) == -1) { perror("cannot mkfifo"); exit(1); } if ((fd = open(FIFO_NAME, O_WRONLY)) == -1) { perror("opening request FIFO"); unlink(m.replyPipe); exit(1); } if ((replyFd = open(m.replyPipe, O_RDWR)) == -1) { perror("opening reply FIFO"); unlink(m.replyPipe); exit(1); } fcntl(replyFd, F_SETFL, O_RDONLY); while (1) { printf("Enter input: "); gets(m.str); if (ctr++ == 0) write(fd, &m, sizeof(message)); else write(fd, m.str, strlen(m.str) + 1); if (m.str[0] == '\0') break; nbytes = read(replyFd, buff, MAX_STR); printf("%d bytes read: %s\n", nbytes, buff); } close(fd); close(replyFd); if (unlink(m.replyPipe) == -1) {perror("cannot unlink");} return 0; }
30
Κατονομασμένες σωληνώσεις – Εγγραφέας
if ((replyFd = open(myPipeName, O_RDONLY | O_NONBLOCK)) == -1) { perror("opening reply FIFO"); unlink(myPipeName); exit(1); } if ((fd = open(FIFO_NAME, O_WRONLY)) == -1) { perror("opening request FIFO"); unlink(myPipeName); exit(1); } fcntl(replyFd, F_SETFL, fcntl(replyFd, F_GETFL) & ~O_NONBLOCK & ~O_NDELAY); while (1) { printf("Enter input: "); gets(str); if (ctr++ == 0) write(fd, &m, sizeof(message)); else write(fd, m.str, strlen(m.str) + 1); if (m.str[0] == '\0') break; errno = 0; while ((nbytes = read(replyFd, buff, 512)) == 0) { if (errno != 0 && errno != EAGAIN) break; } if (nbytes > 0) printf("reply = %s\n", buff); else perror("reading");
31
Κατονομασμένοι σημαφόροι
Οι κατονομασμένοι σημαφόροι μπορούν να χρησιμοποιηθούν για αμοιβαίο αποκλεισμό και συγχρονισμό μεταξύ διεργασιών Συναρτήσεις: sem_t *sem_open(const char *name, int oflag, /* unsigned long mode, unsigned int value */ ...); int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_post(sem_t *sem); int sem_close(sem_t *sem); int sem_unlink(sem_t *sem);
32
Κατονομασμένοι σημαφόροι (2)
sem_t *sem_open(const char *name, int oflag, /* unsigned long mode, unsigned int value */ ...); Δημιουργεί ή παρέχει πρόσβαση σε σημαφόρο Το όνομα «name» συνίσταται να ξεκινάει με / π.χ. /empty /empty_012345 Για μέγιστη μεταφερσιμότητα συνίσταται να έχει μέγεθος έως 14 χαρακτήρες Η συμπεριφορά για ονόματα που δεν ξεκινάνε με / ορίζεται από την υλοποίηση Αν (oflag & O_CREAT) != 0 επιχειρείται να δημιουργηθεί ο σημαφόρος, αν δεν υπάρχει Αν παρατίθεται το O_CREAT πρέπει να παρατίθεται το mode (δικαιώματα πρόσβασης) και το value (αρχική τιμή) Αν (oflag & (O_ΕXCL | O_CREAT)) != 0 τότε επιχειρείται να δημιουργηθεί ο σημαφόρος, ενώ είναι αποτυχία αν ήδη υπάρχει Σε περίπτωση αποτυχίας επιστρέφεται η τιμή SEM_FAILED Παράδειγμα: sem_open("/empty", O_CREAT, 0600, 20);
33
Κατονομασμένοι σημαφόροι (3)
int sem_wait(sem_t *sem); Εκτελείται down πάνω στον σημαφόρο Αν ο σημαφόρος έχει την τιμή 0, η διεργασία αναστέλλεται, αν έχει τιμή >0 μειώνεται κατά 1 int sem_trywait(sem_t *sem); Όμοια με τη sem_wait, ΕΚΤΟΣ αν ο σημαφόρος έχει τιμή 0, οπότε η διεργασία ΔΕΝ αναστέλλεται, αλλά επιστρέφεται -1 int sem_post(sem_t *sem); Εκτελεί signal πάνω στον σημαφόρο Αν υπάρχουν υπό αναστολή διεργασίες πάνω στον σημαφόρο, για μία από αυτές αίρεται η αναστολή και συνεχίζει. Αν δεν υπάρχουν διεργασίες υπό αναστολή, η τιμή του σημαφόρου αυξάνεται κατά 1
34
Κατονομασμένοι σημαφόροι (4)
int sem_close(sem_t *sem); Δηλώνει ότι η διεργασία δεν θα εκτελέσει άλλες πράξεις πάνω στον σημαφόρο int sem_unlink(sem_t *sem); Ζητά τη διαγραφή του σημαφόρου Ο σημαφόρος δεν απομακρύνεται από το σύστημα μέχρις ότου όλες οι αναφορές προς αυτόν (που έχουν δημιουργηθεί με τη sem_open) να καταστραφούν (με τη sem_close)
35
Κατονομασμένοι σημαφόροι –αμοιβαίος αποκλεισμός (sem_mutex1.c)
int main(int argc, char *argv[]) { sem_t *theSem; int mode = 0, unl = 0, i = 0; if ((argc > 1) && (argv[1][0] == 'c')) {mode = O_CREAT | O_EXCL; unl = 1; } if ((theSem = sem_open("/mutex22a", mode, S_IRUSR | S_IWUSR, 1)) == SEM_FAILED) { perror("creating /mutex22a"); exit(1); } while (i++ < 15) { sleep(rand() % 4 + 1); sem_wait(theSem); printf("pid = %ld start...", (long)(getpid())); fflush(stdout); sleep(rand() % 4 + 1); printf("end\n"); sem_post(theSem); if (unl) sem_unlink("/mutex22a"); return 0;
36
Κατονομασμένοι σημαφόροι –συγχρονισμός [1/2](sem_cons.c)
int main(int argc, char *argv[]) { sem_t *full, *empty; int i = 0; if ((empty = sem_open("/empty", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 5)) == SEM_FAILED) { perror("creating /empty"); exit(1); } if ((full = sem_open("/full", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0)) == SEM_FAILED) { perror("creating /full"); exit(1); } while (i++ < 15) { sem_wait(full); sem_post(empty); printf("got item\n"); sleep(rand() % 4 + 1); } sem_unlink("/full"); sem_unlink("/empty"); return 0;
37
Κατονομασμένοι σημαφόροι –συγχρονισμός [2/2](sem_prod.c)
int main(int argc, char *argv[]) { sem_t *full, *empty; int i = 0; if ((empty = sem_open("/empty", 0)) == SEM_FAILED) { perror("attaching to /empty"); exit(1);} if ((full = sem_open("/full", 0)) == SEM_FAILED) { perror("attaching to /full"); exit(1); } while (i++ < 15) { sem_wait(empty); sem_post(full); printf("put item\n"); sleep(rand() % 2 + 1); } return 0;
38
Διαμοιραζόμενη μνήμη, Σημαφόροι, Ουρές μηνυμάτων (SYSV IPC)
Δομές πυρήνα που επιτρέπουν την επικοινωνία μεταξύ διεργασιών Κάθε δομή έχει (μεταξύ άλλων): Ένα κλειδί (συνήθως εκτεταμένος ακέραιος) που καθορίζεται από τον δημιουργό της δομής. Έναν προσδιοριστή που την καθορίζει μοναδικά στο σύστημα (μη αρνητικός ακέραιος). Ο προσδιοριστής ανατίθεται από τον πυρήνα και είναι γνωστός στον δημιουργό Ιδιοκτήτη και δικαιώματα, που καθορίζουν το ποιος επιτρέπεται να κάνει τι. Τα δικαιώματα ορίζονται με ειδικές κλήσεις Οι διεργασίες για να επικοινωνήσουν χρειάζεται: ή να συμφωνήσουν στο ίδιο κλειδί ή να κοινοποιήσει ο δημιουργός τον προσδιοριστή (μέσα από fork ή παραμέτρους για γονική/θυγατρική διεργασία ή μέσω συστήματος αρχείων για μη σχετιζόμενες διεργασίες)
39
Διαμοιραζόμενη μνήμη Δημιουργία/άνοιγμα:
int shmget(key_t key, size_t size, int shmflg); key το κλειδί Αν ως κλειδί χρησιμοποιηθεί η ειδική τιμή IPC_PRIVATE πάντοτε δημιουργείται μία «ανώνυμη» διαμοιραζόμενη μνήμη (συνήθως) για τη διεργασία αυτή και τις θυγατρικές της, ανεξάρτητα από τις ενδείξεις της τρίτης παραμέτρου size πλήθος bytes στη διαμοιραζόμενη μνήμη shmflag δικαιώματα και ενδείξεις ανοίγματος, όπου ενδείξεις είναι: τίποτε : πρέπει να έχει ήδη δημιουργηθεί διαμοιραζόμενη μνήμη με το συγκεκριμένο κλειδί οπότε και ανοίγεται, αλλιώς αποτυχία IPC_CREAT : Αν δεν υπάρχει, δημιουργείται, αν υπάρχει ανοίγεται IPC_EXCL : Μαζί με την IPC_CREAT, αν υπάρχει αποτυγχάνει Προσάρτηση στον χώρο της διεργασίας: void *shmat(int shmid, const void *shmaddr, int shmflg); Η διαμοιραζόμενη μνήμη με τον προσδιοριστή shmid (αποτέλεσμα της shmget) καθίσταται προσπελάσιμη στη διεύθυνση shmaddr. Αν η παράμετρος είναι NULL, το σύστημα διαλέγει και επιστρέφει μία κατάλληλη διεύθυνση Αν (shmflag & SHM_RDONLY) != 0, η μνήμη είναι ανάγνωσης μόνο Αποπροσάρτηση από τον χώρο διεργασίας void *shmdt(const void *shmaddr);
40
Διαμοιραζόμενη μνήμη – Διεργασία 1 [shmem1.c]
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHMEM_KEY ((key_t)2198) #define SHMEM_SIZE 512 int main(void) { int shmid; char *shared_mem; if ((shmid = shmget(SHMEM_KEY, SHMEM_SIZE, IPC_CREAT | IPC_EXCL | 0600)) == -1) { perror("creating shmem"); exit(1); } if ((shared_mem = shmat(shmid, NULL, 0)) == NULL) { perror("attaching shmem"); exit(1);} *shared_mem = 0; strcpy(shared_mem + 1, "mia papia mia poia papia"); *shared_mem = 1; while (*shared_mem != 2) sleep(1); printf("%s\n", shared_mem + 1); if (shmdt(shared_mem) == -1) perror("detaching shared memory"); if (shmctl(shmid, IPC_RMID, 0) == -1) perror("removing shmem"); return 0; }
41
Διαμοιραζόμενη μνήμη – Διεργασία 2 [shmem2.c]
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHMEM_KEY ((key_t)2198) #define SHMEM_SIZE 512 int main(void) { int shmid; char *shared_mem; if ((shmid = shmget(SHMEM_KEY, SHMEM_SIZE, 0)) == -1) { perror("creating shmem"); exit(1); } if ((shared_mem = shmat(shmid, NULL, 0)) == NULL) { perror("attaching shmem"); exit(1); } while (*shared_mem != 1) sleep(1); printf("%s\n", shared_mem + 1); strcpy(shared_mem + 1, "mia papia me papia"); *shared_mem = 2; if (shmdt(shared_mem) == -1) perror("detaching shared memory"); return 0; }
42
Διαμοιραζόμενη μνήμη - Διαχείριση
int shmctl(int shmid, int cmd, struct shmid_ds *buf); cmd = IPC_RMID : διαγραφή τμήματος IPC_STAT : Ανάγνωση της κατάστασης στη δομή που δείχνεται από την 3η παράμετρο IPC_SET : Αλλαγή της ενεργού ταυτότητας και των δικαιωμάτων πρόσβασης, όπως καθορίζεται στο πεδίο shm_perm στην 3η παράμετρο SHM_LOCK : Κλείδωμα στη μνήμη (μόνο root) SHM_UNLOCK : ξεκλείδωμα από τη μνήμη (μόνο root) buf->shm_perm { uid_t uid; gid_t gid; mode_t mode; key_t key; } Ο τύπος struct shmid_ds περιλαμβάνει στοιχεία όπως shm_segsz (segment size), shm_cpid (creator pid), shm_lpid (pid of last shmat/shmdt) κ.λπ.
43
Σημαφόροι Δημιουργία/Άνοιγμα:
int semget(key_t key, int nsems, int semflg); key το κλειδί Αν ως κλειδί χρησιμοποιηθεί η ειδική τιμή IPC_PRIVATE πάντοτε δημιουργείται ένα «ανώνυμο» τμήμα σημαφόρων (συνήθως) για τη διεργασία αυτή και τις θυγατρικές της, ανεξάρτητα από τις ενδείξεις της τρίτης παραμέτρου nsems πλήθος σημαφόρων στην ενότητα αυτή semflag δικαιώματα και ενδείξεις ανοίγματος, όπου ενδείξεις είναι: τίποτε : πρέπει να έχει ήδη δημιουργηθεί τμήμα σημαφόρων με το συγκεκριμένο κλειδί οπότε και ανοίγεται, αλλιώς αποτυχία IPC_CREAT : Αν δεν υπάρχει, δημιουργείται, αν υπάρχει ανοίγεται IPC_EXCL : Μαζί με την IPC_CREAT, αν υπάρχει αποτυγχάνει Οι σημαφόροι δεν προσαρτώνται/αποπροσαρτώνται στις διεργασίες Οι ενέργειες (signal, wait) γίνονται με τη semop, η διαχείριση με τη semctl int semop(int semid, struct sembuf *sops, size_t nsops); int semctl(int semid, int semnum, int cmd, ...);
44
Σημαφόροι - semop Πράξη: Δυνατά sem_flags:
int semop(int semid, struct sembuf *sops, size_t nsops); Ο πίνακας sops περιέχει nsops πράξεις που πρέπει να γίνουν σε σημαφόρο του τμήματος σημαφόρων semid Πράξη: struct sembuf { short sem_num; short sem_op; short sem_flag; } Αν sem_op > 0, η τιμή προστίθεται στον σημαφόρο με αριθμό sem_num Αν sem_op == 0 η διεργασία αναστέλλεται μέχρι ο σημαφόρος να γίνει 0 Αν sem_op < 0: αν abs(sem_op) >= τιμή σημαφόρου, η τιμή σημαφόρου μειώνεται κατά abs(sem_op) και η κλήση επιστρέφει αλλιώς η διεργασία αναστέλλεται χωρίς να μειώνεται η τιμή του σημαφόρου Δυνατά sem_flags: IPC_NOWAIT: Η διεργασία δεν αναστέλλεται ποτέ. Αν η κλήση επρόκειτο να οδηγήσει σε αναστολή, επιστρέφει –1 και θέτει το errno στην τιμή EAIGAIN SEM_UNDO: Αν η πράξη ολοκληρωθεί, η τιμή (- sem_op) προστίθεται στην τιμή της προσαρμογής σημαφόρου (semadj) για την καλούσα διεργασία. Αν μία διεργασία τερματιστεί, η τιμή προσαρμογής σημαφόρου για τη διεργασία αυτή προστίθεται στην τρέχουσα τιμή του σημαφόρου
45
Σημαφόροι - semctl int semctl(int semid, int semnum, int cmd /*, union semnum arg */ ); Όταν απαιτείται 4η παράμετρος πρέπει να είναι του τύπου: union semun { int val; struct semid_ds *buf; ushort_t *array;}; Εκτέλεση ενέργειας σε έναν σημαφόρο ή σε όλους τους σημαφόρους του τμήματος semid GETVAL – Επιστρέφει την τιμή του σημαφόρου SETVAL – Θέτει την τιμή του σημαφόρου σε arg.val, καθαρίζει τη semadj GETNCNT – Επιστρέφει το πλήθος των διεργασιών που έχουν ανασταλεί περιμένοντας να αυξηθεί η τιμή του σημαφόρου GETZCNT – Επιστρέφει το πλήθος των διεργασιών που έχουν ανασταλεί περιμένοντας να γίνει η τιμή του σημαφόρου 0 GETALL – Επιστρέφει την τιμή όλων των σημαφόρων στον πίνακα arg.array SETALL – Θέτει τις τιμές όλων των σημαφόρων όπως ορίζεται στον πίνακα arg.array IPC_RMID – Διαγράφει το σύνολο σημαφόρων IPC_STAT – Διαβάζει όλες τις πληροφορίες για τους σημαφόρους στον πίνακα buf IPC_SET – Ορισμός ιδιοκτήτη, ομάδας και δικαιωμάτων του τμήματος, όπως ορίζεται στο arg.buf.sem_perm
46
Σημαφόροι – Παράδειγμα [sem1.c]
#define SEM_KEY ((key_t)2789) #define NUM_SEMS 2 int main(void) { int semid res; ushort_t semValues[2], count; int; union semun { int val; struct semid_ds *buf; ushort_t *array; } arg ; struct sembuf theSemOp; if ((semid = semget(SEM_KEY, NUM_SEMS, IPC_CREAT | IPC_EXCL | 0600)) == -1) { perror("creating sem"); exit(1); } arg.val = 0; if (semctl(semid, 0, SETVAL, arg) == -1) {perror("set sem 0 val"); exit(1); } arg.val = 20; if (semctl(semid, 1, SETVAL, arg) == -1) {perror("set sem 1 val"); exit(1); } arg.array = semValues; if (semctl(semid, 0, GETALL, arg) == -1) {perror("Getting values"); exit(1); } for (count = 0; count < 2; count++) printf("sem id = %hu, value = %hu\n", count, semValues[count]); while (1) { theSemOp.sem_num = 0; theSemOp.sem_op = -1; theSemOp.sem_flg = 0; if (semop(semid, &theSemOp, (size_t)1) == -1) {perror("semop"); exit(1); } if ((res=semctl(semid, 1, GETVAL, arg))==-1) perror("getting value sem 1"); else printf("Semaphore 1 value is %d\n", res); } return 0;}
47
Σημαφόροι – Παράδειγμα [sem2.c]
#define SEM_KEY ((key_t)2789) #define NUM_SEMS 2 int main(void) { int semid, number; ushort_t semValues[2], count; struct sembuf theSemOp; if ((semid = semget(SEM_KEY, NUM_SEMS, 0)) == -1) { perror("accessing sem"); exit(1); } while (1) { printf("Enter value to request: "); scanf("%d", &number); theSemOp.sem_num = 1; theSemOp.sem_op = -number; theSemOp.sem_flg = 0; if (semop(semid, &theSemOp, (size_t)1) == -1) { perror("semop"); exit(1); } theSemOp.sem_num = 0; theSemOp.sem_op = 1; theSemOp.sem_flg = 0; } return 0;
48
Σημαφόροι – Παράδειγμα [sem3.c]
#define SEM_KEY ((key_t)2789) #define NUM_SEMS 2 int main(void) { int semid, number; ushort_t semValues[2], count; struct sembuf theSemOp[2]; if ((semid = semget(SEM_KEY, NUM_SEMS, 0)) == -1) { perror("accessing sem"); exit(1); } while (1) { printf("Enter value to give: "); scanf("%d", &number); theSemOp[0].sem_num = 0; theSemOp[0].sem_op = 1; theSemOp[0].sem_flg = 0; theSemOp[1].sem_num = 1; theSemOp[1].sem_op = number; theSemOp[1].sem_flg = 0; if (semop(semid, theSemOp, (size_t)2) == -1) { perror("semop"); exit(1); } } return 0;
49
Ουρές μηνυμάτων Σημεία όπου διεργασίες-εγγραφείς εναποθέτουν μηνύματα που εξάγουν οι διεργασίες-αναγνώστες Δημιουργία: int msgget(key_t key, int msgflg); key το κλειδί Αν ως κλειδί χρησιμοποιηθεί η ειδική τιμή IPC_PRIVATE πάντοτε δημιουργείται μία «ανώνυμη» ουρά μηνυμάτων (συνήθως) για τη διεργασία αυτή και τις θυγατρικές της, ανεξάρτητα από τις ενδείξεις της τρίτης παραμέτρου msgflg δικαιώματα και ενδείξεις ανοίγματος, όπου ενδείξεις είναι: τίποτε : πρέπει να έχει ήδη δημιουργηθεί ουρά μηνυμάτων με το συγκεκριμένο κλειδί οπότε και ανοίγεται, αλλιώς αποτυχία IPC_CREAT : Αν δεν υπάρχει, δημιουργείται, αν υπάρχει ανοίγεται IPC_EXCL : Μαζί με την IPC_CREAT, αν υπάρχει αποτυγχάνει
50
Αποστολή / Λήψη μηνυμάτων
int msgsnd(int msqid, const void *msgp, size_t megsz, int msgflg); ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg); Η παράμετρος msgp έχει τη δομή: struct mymsg {long int mtype; char mtext[1];}; Η καλούσα διεργασία μπορεί να αναστέλλεται: Αν στέλνει μήνυμα σε γεμάτη ουρά (μέγιστο πλήθος bytes ή μέγιστο πλήθος μηνυμάτων) Αν ζητήσει μήνυμα από άδεια ουρά εκτός αν στο msgflg της κλήσης περιλαμβάνεται η ένδειξη IPC_NOWAIT Κατά την παραλαβή η παράμετρος msgsz υποδεικνύει το μέγιστο μέγεθος μηνύματος (χωρητικότητα mtext) Το μήνυμα μπορεί να περικοπεί αν msgflag & MSG_NOERROR != 0 Αν msgtyp == 0 επιστρέφεται το επόμενο μήνυμα ανεξαρτήτως τύπου Αν msgtyp > 0 επιστρέφεται το επόμενο μήνυμα με τύπο == msgtyp Αν msgtyp < 0 επιστρέφεται το επόμενο μήνυμα με τύπο <= abs(msgtyp) Ο τύπος μπορεί να χρησιμοποιηθεί από τις εφαρμογές για διαχωρισμό μηνυμάτων σε κατηγορίες (π.χ. αίτηση-απάντηση, διαφορετικοί τύποι αιτήσεων, καθορισμός παραλήπτη)
51
Διαχείριση ουρών μηνυμάτων
int msgctl(int msqid, int cmd, struct msqid_ds *buf); Εκτέλεση εντολών στην ουρά μηνυμάτων. Δυνατές εντολές: IPC_STAT: λήψη πληροφοριών για την ουρά στη δομή που δείχνει ο buf IPC_SET: Ορισμός ιδιοκτήτη, ομάδας και δικαιωμάτων της ουράς, όπως ορίζεται στο buf->msg_perm. Καθορισμός της παραμέτρου msg_qbytes όπως ορίζεται στο buf->msg_qbytes (μέγιστο πλήθος bytes που μπορούν να αναμένουν στην ουρά) IPC_RMID: διαγραφή της ουράς από το σύστημα
52
Ουρές μηνυμάτων – Παράδειγμα [msgq1.c]
#define MSGQ_KEY ((key_t)2791) #define MAXMSG 2048 int main(void) { int qid; int res; struct mymsg { long int type; char msgtxt[MAXMSG];} theMessage; char *cp; if ((qid = msgget(MSGQ_KEY, IPC_CREAT | IPC_EXCL | 0600)) == -1) { perror("creating queue"); exit(1); } while (1) { msgrcv(qid, &theMessage, MAXMSG, 1, 0); printf("Got message, type = %ld, txt = %s\n", theMessage.type, theMessage.msgtxt); for (cp = theMessage.msgtxt; *cp != '\0'; cp++) *cp = toupper(*cp); theMessage.type = 2; msgsnd(qid, &theMessage, cp - theMessage.msgtxt + 1, 0); } return 0;
53
Ουρές μηνυμάτων – Παράδειγμα [msgq2.c]
#define MSGQ_KEY ((key_t)2791) #define MAXMSG 2048 int main(void) { int qid; int res; struct mymsg { long int type; char msgtxt[MAXMSG]; } theMessage; char *cp; if ((qid = msgget(MSGQ_KEY, 0)) == -1) { perror("creating queue"); exit(1); } while (1) { printf("Enter string: "); gets(theMessage.msgtxt); if (theMessage.msgtxt[0] == '\0') break; theMessage.type = 1; msgsnd(qid, &theMessage, strlen(theMessage.msgtxt) + 1, 0); msgrcv(qid, &theMessage, MAXMSG, 2, 0); printf("Got reply, type = %ld, txt = %s\n", theMessage.type, theMessage.msgtxt); } return 0;
54
Ουρές μηνυμάτων – Παράδειγμα [msgq3.c]
#define MSGQ_KEY ((key_t)2791) int main(void) { int qid; int res; struct mymsg { long int type; struct numbers {int a, b;} theNums; } theMessage; char *cp; if ((qid = msgget(MSGQ_KEY, IPC_CREAT | IPC_EXCL | 0600)) == -1) { perror("creating queue"); exit(1); } while (1) { msgrcv(qid, &theMessage, sizeof(struct numbers), 1, 0); theMessage.type = 2; theMessage.theNums.a += theMessage.theNums.b; msgsnd(qid, &theMessage, sizeof(struct numbers), 0); } return 0;
55
Ουρές μηνυμάτων – Παράδειγμα [msgq4.c]
#define MSGQ_KEY ((key_t)2791) int main(void) { int qid; int res; struct mymsg { long int type; struct numbers {int a, b;} theNums; } theMessage; char *cp; if ((qid = msgget(MSGQ_KEY, 0)) == -1) { perror("opening queue"); exit(1); } while (1) { theMessage.type = 1; printf("Enter a: "); scanf("%d", &theMessage.theNums.a); printf("Enter b: "); scanf("%d", &theMessage.theNums.b); msgsnd(qid, &theMessage, sizeof(struct numbers), 0); msgrcv(qid, &theMessage, sizeof(struct numbers), 2, 0); printf("Reply = %d\n", theMessage.theNums.a); } return 0;
56
Δίοδοι (Sockets) Γενικός μηχανισμός επικοινωνίας διεργασιών
Μπορεί να χρησιμοποιηθεί για επικοινωνία διεργασιών στο ίδιο μηχάνημα (AF_UNIX) σε διαφορετικά μηχανήματα πάνω από από δικτυακά πρωτόκολλα (AF_INET, AF_INET6, AF_IPX, AF_SNA κ.ο.κ.) Είναι δύο κατευθύνσεων, επιτρέποντας την αμφίδρομη επικοινωνία Μοντέλο χρήσης Μία διεργασία (1) δημιουργεί τη δίοδο (2) την αντιστοιχίζει σε διεύθυνση (3) καθορίζει μέγιστο πλήθος συνδέσεων και (4) περιμένει συνδέσεις προς αυτή Οι άλλες διεργασίες (1) δημιουργούν δίοδο και τη συνδέουν στη διεύθυνση Το κάθε άκρο στέλνει τα δεδομένα που επιθυμεί Η επικοινωνία τερματίζεται
57
Δίοδοι - Δημιουργία type τρόπος επικοινωνίας
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); domain Είδος πρωτοκόλλου (PF_UNIX, PF_INET, PF_INET6) Πρέπει να ταιριάζει με την οικογένεια διευθύνσεων (επόμενη διαφάνεια) type τρόπος επικοινωνίας SOCK_STREAM (αξιόπιστη ακολουθία bytes – πιθανώς να μην διατηρούν τα όρια των εγγραφών) SOCK_DGRAM (αναξιόπιστη ακολουθία πακέτων περιορισμένου μέγιστου μεγέθους) SOCK_SEQPACKET (αξιόπιστη ακολουθία πακέτων – δεν έχει υλοποιηθεί) protocol – Ορίζει το πρωτόκολλο που θα υλοποιήσει την επικοινωνία (σχεδόν πάντα 0 επιλογή από το σύστημα)
58
Δίοδοι – Αντιστοίχιση σε διεύθυνση
#include <sys/types.h> #include <sys/socket.h> int bind(int socket, struct sockaddr *name, int name_len); socket προσδιοριστής διόδου που επέστρεψε η socket name καθορισμός διεύθυνσης Αντί του (γενικού) struct sockaddr συνήθως χρησιμοποιούνται εξειδικεύσεις, ανάλογα με τον τύπο της διεύθυνσης #include <sys/un.h> struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* fs-path */ }; #include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; /* AF_INET */ in_port_t sin_port; struct in_addr sin_addr; }; name_len μήκος διεύθυνσης σε bytes
59
Δίοδοι – Καθορισμός πλήθους συνδέσεων & αποδοχή συνδέσεων
#include <sys/types.h> #include <sys/socket.h> int listen(int socket, int backlog); int accept(int socket, struct sockaddr *addr, socklen_t *addrlen); Αποδοχή μίας σύνδεσης στη δίοδο πρέπει να έχει γίνει bind, listen Επιστρέφει στην παράμετρο addr τη διεύθυνση του ομότιμου μέλους της επικοινωνίας Το μέγιστο μήκος διεύθυνσης καθορίζεται από την τιμή εισόδου της παραμέτρου addrlen. Κατά την έξοδο, η παράμετρος addrlen έχει το πραγματικό μήκος της διεύθυνσης που αποθηκεύτηκε και στην παράμετρο addrlen το μήκος της διεύθυνσης Σε περίπτωση επιτυχίας επιστρέφει έναν προσδιοριστή αρχείου, που μπορεί να χρησιμοποιηθεί σε κλήσεις read και write
60
Δίοδοι – σύνδεση και ανταλλαγή δεδομένων
Δημιουργία διόδου (socket) Σύνδεση: #include <sys/types.h> #include <sys/socket.h> int connect(int socket, struct sockaddr *name, int namelen); Σε επιτυχή εκτέλεση επιστρέφει έναν προσδιοριστή αρχείου που μπορεί να χρησιμοποιηθεί σε κλήσεις read και write Στο τέλος της επικοινωνίας πρέπει να καλείται η close για τον προσδιοριστή αρχείων
61
Δίοδοι – Παράδειγμα [sock-stream-srv.c]
int main(int argc, char *argv[]) { int s, s1, res, name1_len; struct sockaddr_in name, name1; char buf[BUFSIZ], *cp; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Cannot create a socket"); exit(2); } name.sin_family = AF_INET; name.sin_addr.s_addr = INADDR_ANY; name.sin_port = htons(2412); if (bind(s, (struct sockaddr *)&name, sizeof(name)) < 0) { perror("Unable to bind socket"); exit(3); } if (listen(s, 5) < 0) { perror("listen() failed"); exit(2); } printf("Bound socket %d to port 2412\n", s); while (1) { name1_len = sizeof(name1); if ((s1 = accept(s, &name1, &name1_len)) < 0) { perror("accept() failed\n"); break; } printf("Accepted con: %x.%x.%x.%x\n", name1.sin_addr.S_un._S_un_b.s_b1, name1.sin_addr.S_un._S_un_b.s_b2, name1.sin_addr.S_un._S_un_b.s_b3, name1.sin_addr.S_un._S_un_b.s_b4); while ((res = read(s1, buf, BUFSIZ)) > 0) { for (cp = buf; cp < buf + res; cp++) *cp = toupper(*cp); if (write(s1, buf, res) < 0) { perror("Error sending back data"); break; } } close(s1); close(s); return 0;}
62
Δίοδοι – Παράδειγμα [sock-stream-clnt.c]
int main(int argc, char *argv[]) { int s, s1, len, res, i; struct sockaddr_in name, name1; struct hostent *hp; char buf[BUFSIZ], hostname[70]; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Cannot create a socket\n"); exit(2); } hp = gethostbyname((argc == 1) ? "localhost" : argv[1]); name.sin_family = AF_INET; name.sin_port = htons(2412); bcopy((char *)hp->h_addr, (char *)&(name.sin_addr), hp->h_length); if (connect(s, (struct sockaddr *)&name, sizeof(name)) < 0) { perror("connect() failed"); exit(2); } while (1) { printf("Enter string: "); gets(buf); if (buf[0] == '\0') break; if (write(s, buf, strlen(buf) + 1) < 0) { perror("cannot send data"); exit(3); } if ((res = read(s, buf, BUFSIZ)) > 0) printf("Read reply %s\n", buf); else { perror("reading reply"); break; } } close(s); return 0;
63
Δίοδοι χωρίς σύνδεση – αποστολή και λήψη
Στέλνουμε δεδομένα με sendto ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, int tolen); s το αποτέλεσμα της socket msg η διεύθυνση μνήμης με τα δεδομένα len το πλήθος των bytes flags κανονικά 0 (δυνατότητες: MSG_OOB) to η διεύθυνση που θα πάνε τα δεδομένα tolen το πλήθος bytes στη διεύθυνση to Λαμβάνουμε δεδομένα με recvfrom ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen); Οι παράμετροι αντίστοιχα με ανωτέρω, μόνο που στη διεύθυνση from το σύστημα τοποθετεί τη διεύθυνση του αποστολέα και στο fromlen το μήκος της διεύθυνσης αυτής δυνατότητες flags : MSG_PEEK, MSG_OOB, MSG_WAITALL
64
Δίοδοι χωρίς σύνδεση [dgram-srv.c]
int main(int argc, char *argv[]) { int sock, length, fromlen, n; struct sockaddr_in server, sockaddr_in from; char buf[1024]; if (argc < 2) { fprintf(stderr, "ERROR, no port provided\n"); exit(0); } sock=socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) error("Opening socket"); length = sizeof(server); bzero(&server,length); server.sin_family=AF_INET; server.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(atoi(argv[1])); if (bind(sock,(struct sockaddr *)&server,length)<0) {perror("binding"); exit(1);} fromlen = sizeof(struct sockaddr_in); while (1) { n = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen); if (n < 0) {perror("recvfrom"); exit(1); } write(1,"Received a datagram: ",21); write(1,buf,n); n = sendto(sock,"Got your message\n",17, 0,(struct sockaddr *)&from,fromlen); if (n < 0) {perror("sendto"); exit(1);} } }
65
Δίοδοι χωρίς σύνδεση [dgram-clnt.c]
int main(int argc, char *argv[]) { int sock, length, n; struct sockaddr_in server, from; struct hostent *hp; char buffer[256]; if (argc != 3) { printf("Usage: server port\n"); exit(1); } sock= socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) {perror("socket"); exit(1);} server.sin_family = AF_INET; hp = gethostbyname(argv[1]); if (hp==NULL) {perror("Unknown host"); exit(1);} bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_port = htons(atoi(argv[2])); length=sizeof(struct sockaddr_in); printf("Please enter the message: "); fgets(buffer,255,stdin); n=sendto(sock,buffer, strlen(buffer),0,&server,length); if (n < 0) {perror("Sendto"); exit(1);} n = recvfrom(sock, buffer, 256, 0, &from, &length); if (n < 0) {perror("recvfrom"); exit(1);} write(1,"Got an ack: ",12); write(1,buffer,n); close(sock); return 0; }
Παρόμοιες παρουσιάσεις
© 2024 SlidePlayer.gr Inc.
All rights reserved.