Προς το περιεχόμενο

Ερώτηση σε C


Ubersoldier

Προτεινόμενες αναρτήσεις

Καλησπέρα στο φόρουμ! Θέλω να φτιάξω ένα πρόγραμμα, το οποία αφορά σε δημιουργία ενός txt με 2000 τυχαίους a-z χαρακτήρες και μέτρημα των χαρακτήρων αυτών μέσω νημάτων, με εκτύπωση του τελικού αποτελέσματος. Ενώ έφτασα μέχρι το παρακάτω σημείο και ενώ γίνεται compile, όταν εκτελέσω παράγεται το txt αρχείο αλλά δεν έχω κάποιο output στο terminal ούτε και τερματίζει. υποθέτω κάπου τα έχω μαντάρα με τα signals αλλά εάν κάποιος μπορεί να βοηθήσει θα ήμουν ευγνώμων! Καλό ΣΚ και χρόνια πολλά!

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <semaphore.h>
 
#define FILENAME "data.txt"
#define NUM_THREADS 4
#define NUM_CHARS 2000
 
sem_t sem1, sem2;
pthread_mutex_t mutex;
 
void* read_file(void* arg) {
    char* filename = (char*) arg;
    int fd = open(filename, O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
 
    char buffer[NUM_CHARS / NUM_THREADS];
    int num_read, total_count = 0;
    while ((num_read = read(fd, buffer, sizeof(buffer))) > 0) {
        int count[26] = {0};
        for (int i = 0; i < num_read; i++) {
            if (buffer[i] >= 'a' && buffer[i] <= 'z') {
                count[buffer[i] - 'a']++;
            }
        }
 
        pthread_mutex_lock(&mutex);
        for (int i = 0; i < 26; i++) {
            total_count += count[i];
        }
        pthread_mutex_unlock(&mutex);
    }
 
    close(fd);
    return (void*) (intptr_t) total_count;
}
 
void write_file() {
    int fd = open(FILENAME, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
 
    srand(time(NULL));
    char buffer[NUM_CHARS];
    for (int i = 0; i < NUM_CHARS; i++) {
        buffer[i] = rand() % 26 + 'a';
    }
 
    if (write(fd, buffer, sizeof(buffer)) == -1) {
        perror("write");
        exit(EXIT_FAILURE);
    }
 
    close(fd);
}
 
void sig_handler(int signum) {
    if (signum == SIGINT || signum == SIGTERM) {
        printf("Are you sure? (y/n): ");
        char answer[2];
        fgets(answer, sizeof(answer), stdin);
        if (answer[0] == 'y') {
            exit(EXIT_SUCCESS);
        }
    }
}
 
int main() {
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
 
    sem_init(&sem1, 0, 0);
    sem_init(&sem2, 0, 0);
    pthread_mutex_init(&mutex, NULL);
 
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // child process
        write_file();
        sem_post(&sem1);
        exit(EXIT_SUCCESS);
    } else {
        // parent process
        sem_wait(&sem1);
 
        pthread_t threads[NUM_THREADS];
        for (int i = 0; i < NUM_THREADS; i++) {
            if (pthread_create(&threads[i], NULL, read_file, FILENAME) != 0) {
                perror("pthread_create");
                exit(EXIT_FAILURE);
            }
        }
 
        int total_count = 0;
        for (int i = 0; i < NUM_THREADS; i++) {
            void* result;
            if (pthread_join(threads[i], &result) != 0) {
                perror("pthread_join");
                exit(EXIT_FAILURE);
            }
            total_count += (int) (intptr_t) result;
        }
    }
    return 0;
}
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Βάζε και κανα comment ή κάτι το οποίο να μας εξηγεί τι θες να κάνεις... πάει λίγος καιρός απο τότε που έπιασα C και σημαφόρους 😋

Αν θυμάμαι καλά, και αν κατάλαβα καλά, τα κάνεις ανάποδα. Αν προσέξεις, το parent process περιμένει τον σημαφόρο 1. Το child process που δεν περιμένει κανέναν, γράφει στο αρχείο και μετά κάνει signal για να τρέξει ο parent που μετράει τι είναι να μετρήσει. Άρα εσύ πρώτα γράφεις ένα άδειο αρχείο και μετά μετράς τι είναι να μετρήσεις.

Άκυρο δεν κατάλαβα καλά το πρόβλημα σου. Αν δεν τερματίζει τότε πρέπει να δεις τις λούπες σου. Από αυτό που βλέπω, τα for loops είναι εντάξει: θα τρέξουν για ένα προκαθορισμένο αριθμό. Εγώ θα κοίταζα το while άμα κάπου κόλλησε. Εκεί τι επιστρέφει το read?

Επεξ/σία από Ponous
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

17 λεπτά πριν, Ponous είπε

Βάζε και κανα comment ή κάτι το οποίο να μας εξηγεί τι θες να κάνεις... πάει λίγος καιρός απο τότε που έπιασα C και σημαφόρους 😋

Αν θυμάμαι καλά, και αν κατάλαβα καλά, τα κάνεις ανάποδα. Αν προσέξεις, το parent process περιμένει τον σημαφόρο 1. Το child process που δεν περιμένει κανέναν, γράφει στο αρχείο και μετά κάνει signal για να τρέξει ο parent που μετράει τι είναι να μετρήσει. Άρα εσύ πρώτα γράφεις ένα άδειο αρχείο και μετά μετράς τι είναι να μετρήσεις.

Άκυρο δεν κατάλαβα καλά το πρόβλημα σου. Αν δεν τερματίζει τότε πρέπει να δεις τις λούπες σου. Από αυτό που βλέπω, τα for loops είναι εντάξει: θα τρέξουν για ένα προκαθορισμένο αριθμό. Εγώ θα κοίταζα το while άμα κάπου κόλλησε. Εκεί τι επιστρέφει το read?

Όντως, ως άπειρος, φαίνεται η μη χρήση σχόλιων. ουσιαστικά θέλω τα παρακάτω:
 

Όταν θα ξεκινάει , θα πρέπει να δημιουργεί δύο διεργασίες (μέσω της fork)

  • Η μία διεργασία θα κάνει αρχική εγγραφή στο αρχείο txt  
  • Η δεύτερη διεργασία θα είναι υπεύθυνη για την ανάγνωση από το αρχείο και την εξαγωγή των πόσων και ποιων χαρακτήρων έχει από την αλφάβητο, αφού έχει τελειώσει η εγγραφή από την πρώτη διεργασία. Η ανάγνωση θα πρέπει να παραλληλοποιείται μέσω πολλαπλών νημάτων (4 νήματα) χρησιμοποιώντας την Pthreads βιβλιοθήκη. Κάθε νήμα θα πρέπει να αναλαμβάνει την ανάγνωση ενός κομματιού (2000/4 = 500 χαρακτήρες) από το αρχείο (με τη βοήθεια της lseek) και να κρατάει τοπικά τα δικά του αποτελέσματα για την εμφάνιση κάθε γράμματος. Στο τέλος της επεξεργασίας θα πρέπει να φροντίζει να προσθέτει τα δικά του στοιχεία στην κεντρική δομή αποθήκευσης. Η δεύτερη διεργασία θα πρέπει επίσης να εκτυπώνει τα περιεχόμενα της κεντρικής δομής, δηλαδή πόσες εμφανίσεις έχει κάθε χαρακτήρας από το a έως το z. 
  • Οι δύο διεργασίες θα πρέπει να συγχρονίζουν τη σειρά εκτέλεσης μέσω σημαφόρων ώστε να διασφαλίζεται ότι πάντα θα ξεκινάει η 1η διεργασία και θα υπάρχει περιεχόμενο στο αρχείο. Η 2η διεργασία θα πρέπει να ξεκινάει μόνο εφόσον έχει τελειώσει το μέρος εγγραφής από την 1η.
  • Αν ληφθεί σήμα SIGINT ή SIGTERM να ρωτάει το χρήστη αν όντως θέλει να τερματίσει το πρόγραμμα, και αν ναι, τότε να το τερματίζει.
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
16 ώρες πριν, Ubersoldier είπε

Καλησπέρα στο φόρουμ! Θέλω να φτιάξω ένα πρόγραμμα, το οποία αφορά σε δημιουργία ενός txt με 2000 τυχαίους a-z χαρακτήρες και μέτρημα των χαρακτήρων αυτών μέσω νημάτων, με εκτύπωση του τελικού αποτελέσματος. Ενώ έφτασα μέχρι το παρακάτω σημείο και ενώ γίνεται compile, όταν εκτελέσω παράγεται το txt αρχείο αλλά δεν έχω κάποιο output στο terminal ούτε και τερματίζει. υποθέτω κάπου τα έχω μαντάρα με τα signals αλλά εάν κάποιος μπορεί να βοηθήσει θα ήμουν ευγνώμων! Καλό ΣΚ και χρόνια πολλά!

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <semaphore.h>
 
sem_t sem1, sem2;
pthread_mutex_t mutex;
 
int main() {
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
 
    sem_init(&sem1, 0, 0);
    //sem_init(&sem2, 0, 0);
    //pthread_mutex_init(&mutex, NULL);
 
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // child process
        write_file();
        sem_post(&sem1);
        exit(EXIT_SUCCESS);
    } else {
        // parent process
        sem_wait(&sem1);
       loipos kwdikas
    }
    return 0;
}

Πρώτον κάτι άσχετο ότι δεν έχεις πουθενά cleanup όπως sem_destroy ή ό,τι άλλο χρειάζεται αλλά είναι στην αρχή το πρόγραμμά σου οπότε θα τα βάλεις μετά υποθέτω.  Eπίσης έχεις signal αντί για sigaction, κτλ. Ας έρθουμε όμως στο πρόβλημά σου.

Σκέψου τι γίνεται όταν τρέχεις το fork; Το παιδί δεν είναι 100% αντίγραφο του πατέρα; (με κάποιες εξαιρέσεις που αναφέρονται στην manpage του fork). Άρα, το sem1 που κάνεις wait στον πατέρα είναι το ίδιο με το sem1 που κάνεις post στο παιδί; Ή μήπως είναι διαφορετικό και για αυτό ο πατέρας κρεμάει και δεν τελειώνει ποτέ;

Το πρώτο σου πρόβλημα είναι ότι τρέχεις λάθος την sem_init. Δεν τις γράφει η manpage.

sem_init(3)                Library Functions Manual                sem_init(3)

SYNOPSIS
       #include <semaphore.h>

       int sem_init(sem_t *sem, int pshared, unsigned int value);

DESCRIPTION
       sem_init()  initializes the unnamed semaphore at the address pointed to
       by sem.  The value argument specifies the initial value for  the  sema-
       phore.

       The  pshared  argument indicates whether this semaphore is to be shared
       between the threads of a process, or between processes.

       If pshared has the value 0, then the semaphore is  shared  between  the
       threads  of  a  process,  and should be located at some address that is
       visible to all threads (e.g., a global variable, or  a  variable  allo-
       cated dynamically on the heap).

       If  pshared is nonzero, then the semaphore is shared between processes,
       and should be located in a region of shared  memory  (see  shm_open(3),
       mmap(2),  and  shmget(2)).   (Since a child created by fork(2) inherits
       its parent's memory mappings, it can also access the  semaphore.)   Any
       process  that  can  access  the shared memory region can operate on the
       semaphore using sem_post(3), sem_wait(3), and so on.

Εσύ δεν χρησιμοποιείς το semaphore για να συγχρονίσεις τα threads που δημιουργείς για το μέτρημα αλλά το χρησιμοποιείς για να συγχρονίσεις δύο διαφορετικές processes που δημιουργήθηκαν από το fork. Οπότε λοιπόν πρέπει να τρέξεις την sem_init με 1,0 και όχι με 0,0 που την έχεις. Μετά από αυτό, έχεις το 2ο πρόβλημα ότι για να μπορεί να προσπελάσουν το ίδιο sem και οι δύο processes, πρέπει αυτό να βρίσκεται σε shared μνήμη. Δεν αρκεί δηλαδή να είναι global όπως το έχεις τώρα.

Τι μπορείς να κάνεις τώρα λοιπόν για να παίξει:

Εκδοχή 1: Χρησιμοποιείς anonymous semaphores όπως κάνεις τώρα αλλά τα βάζεις σε shared μνήμη.

int main() {                                                                    
    signal(SIGINT, sig_handler);                                                
    signal(SIGTERM, sig_handler);                                               
    
    sem_t *sem1;                                                                
    int sfd = shm_open("/mysem1", O_CREAT | O_RDWR, 0600);                      
    ftruncate(sfd, sizeof(sem_t));                                              
    sem1 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);
    sem_init(sem1, 1, 0);                                                       
    //sem_init(&sem2, 0, 0);                                                    
    pthread_mutex_init(&mutex, NULL);
  
  ......
    
       total_count += (int) (intptr_t) result;                             
        }                                                                       
        munmap(sem1, sizeof(sem_t));                                            
        shm_unlink("/mysem1");     

Ορίζεις το sem1 ώς δείκτη για να μπορέσει να σου επιστρέψει τιμή το mmap, δημιουργείς ένα νέο "αρχείο" με όνομα mysem1 (σε linux θα μπορείς να το δεις στον κατάλογο /dev/shm) ώστε να έχεις ένα file descriptor να δώσεις στο mmap, με την ftruncate του δίνεις το μέγεθος που πρέπει να έχει το sem_t, και μετά με την mmap δημιουργείς ένα δείκτη για το sem1 το οποίο να μπορούν να το δουν όλες οι διεργασίες. Το ψωμί είναι το MAP_SHARED όπως καταλαβαίνεις. Έπειτα τρέχεις κανονικά sem_init όπως πριν με την διαφορά ότι η 2η παράμετρος είναι 1 αντί για 0 που είχες. Στο τέλος κάνεις munmap και σβήνεις το "αρχείο". Αυτή είναι η συνήθης διαδικασία αλλά θα μπορούσες αντί να δώσεις "file-backed mapping" στο mmap να δημιουργήσεις ένα anonymous mapping. Τότε ξεχνάς τα shm_open, ftruncate, shm_unlink και έχεις απλά κάτι σαν το παρακάτω

	sem_t *sem1;                                                                
    sem1 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
    sem_init(sem1, 1, 0);  

Εκδοχή 2: Χρησιμοποιείς named semaphores.

Τα unnamed semaphores που χρησιμοποίησες είναι η light εκδοχή των semaphores τα οποία χρησιμοποιείς εύκολα μέσα στο ίδιο process. Τα named semaphores δημιουργούνται από τη μάνα τους σε shared μνήμη οπότε είσαι οκ χωρίς να μπλέκεις. Σκέψου μπακάλικα ότι το σύστημα κάνει για σένα αυτά τα βήματα που περιγράψαμε πριν.

int main() {                                                                    
    signal(SIGINT, sig_handler);                                                
    signal(SIGTERM, sig_handler);                                               
                                                                                
    sem_t *sem1;                                                                
    sem1 = sem_open("/mysem1", O_CREAT, 0600, 0);    
  
  ......
            total_count += (int) (intptr_t) result;                             
        }                                                                       
        sem_close(sem1);
    
  

Δοκίμασε όποιο σε βολεύει και πες μας απορίες.

Edit: Ξέχασα να αναφέρω τα header filers που ορίζονται οι παραπάνω συναρτήσεις. Νομίζω θα χρειαστείς <sys/mman.h> αλλά θα τα δεις στην manpage καλύτερα.

Επεξ/σία από imitheos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ενταξει, ωραιο snip. Απλα δε χωρανε threads και forks.

Ας πουμε, η read οταν την καλεις δεν διαβαζει χ byte, σε αντιθεση διαβαζει ενα μεγαλο κομματι (chunk) και απο αυτο κανει αντιγραφει τα x byte στο buffer σου. Το κομματι αυτο ειναι σιγουρα μεγαλυτερο απο τα 500 byte που θελειες αρα ουσιαστικα αντι να διαβασεις 500 * 4 εσυ διαβαζεις 4* κατι πολυ μεγαλυτερο.

Επισης ας πουμε οτι βαζεις μεγαλυτερο buffer. Τοτε εχεις το θεμα της συνδεσης, η συνδεση με του σκληρο ειναι αργη, το thread σου στο 1% θα κανει την αντιγραφη και στο 99% θα κοιμαται. 

mutex_lock/mutex_unlock: Αυτο ειναι ενας μηχανισμος αποτρποπης εγγραφης/διαβασματος μιας μνημης ταυτοχρονοα απο δυο διαφορετικα thread. Κατι το οποιο ειναι αχρηστο στο θεμα σου, εφοσον το total count ανοικει στο εκαστοτε thread, αν το total count ηταν global, τοτε θα ειχε καποιο νοημα. Εσυ το επιστρεφεις σας αποτελεσμα στο τερματιμο του καθε thread αρα ειναι ηδη συγχρονισμενο.

interrupts, forks, samephone idk

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

10 ώρες πριν, παπι είπε

Ας πουμε, η read οταν την καλεις δεν διαβαζει χ byte, σε αντιθεση διαβαζει ενα μεγαλο κομματι (chunk) και απο αυτο κανει αντιγραφει τα x byte στο buffer σου. Το κομματι αυτο ειναι σιγουρα μεγαλυτερο απο τα 500 byte που θελειες αρα ουσιαστικα αντι να διαβασεις 500 * 4 εσυ διαβαζεις 4* κατι πολυ μεγαλυτερο.

Ναι το κομμάτι της read έχει πολλά λογικά λάθη αλλά δεν τα ανέφερα αφενός γιατί υπέθεσα ότι είναι στην αρχή και θα τα υλοποιήσει μετά (μια και ανέφερε πχ lseek), αφετέρου γιατί ήθελα να δει πρώτα το θέμα με τα semaphores και να πιάσουμε μετά τα υπόλοιπα.

BTW, nice to cu παπί. Χαθήκαμε όλοι από το φόρουμ του προγραμματισμού.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Για να μην πάει η read να διαβάσει προτού έχει γίνει το write, έλεγα να κάνω χρήση semaphore. Αλλά πιστεύω πως εν τέλει είναι πιο εύκολο να χρησιμοποιήσω μια sleep για την υλοποίηση. Επιπρόσθετα, τα threads λογικά με μια mutex θα μπορέσουν στο τέλος να κάνουν το total_count, αθροίζοντας τα partial_count τους. (δηλαδή (sum_count=sum_count+partial_count διαδοχικά)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ο σκοπος ειναι καποια ασκηση, ή θες να μαθεις. Αν ειναι το δευτερο, φτιαξε προγραμμα που βαζει φιλτρα σε εικονες. Σχετικα ευκολο στην υλοποιηση, πολυ καλο για multithreading, και κυριως θα εχεις ενα οπτικο αποτελεσμα

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...