macOS GCD - Grand Central Dispatch
Βασικές Πληροφορίες
Το Grand Central Dispatch (GCD), γνωστό και ως libdispatch (libdispatch.dyld
), είναι διαθέσιμο τόσο σε macOS όσο και σε iOS. Είναι μια τεχνολογία που αναπτύχθηκε από την Apple για τη βελτιστοποίηση της υποστήριξης εφαρμογών για την ταυτόχρονη (πολυνηματική) εκτέλεση σε πολυπύρηνο υλικό.
Το GCD παρέχει και διαχειρίζεται ουρές FIFO στις οποίες η εφαρμογή σας μπορεί να υποβάλει εργασίες σε μορφή block objects. Τα blocks που υποβάλλονται στις ουρές αποστολής εκτελούνται σε ένα pool νημάτων πλήρως διαχειριζόμενο από το σύστημα. Το GCD δημιουργεί αυτόματα νήματα για την εκτέλεση των εργασιών στις ουρές αποστολής και προγραμματίζει αυτές τις εργασίες να τρέχουν στους διαθέσιμους πυρήνες.
Συνοπτικά, για να εκτελέσετε κώδικα παράλληλα, οι διεργασίες μπορούν να στείλουν blocks κώδικα στο GCD, το οποίο θα φροντίσει για την εκτέλεσή τους. Συνεπώς, οι διεργασίες δεν δημιουργούν νέα νήματα. Το GCD εκτελεί τον δοσμένο κώδικα με το δικό του pool νημάτων (το οποίο μπορεί να αυξηθεί ή να μειωθεί ανάλογα με τις ανάγκες).
Αυτό είναι πολύ χρήσιμο για τη διαχείριση της παράλληλης εκτέλεσης με επιτυχία, μειώνοντας σημαντικά τον αριθμό των νημάτων που δημιουργούν οι διεργασίες και βελτιστοποιώντας την παράλληλη εκτέλεση. Αυτό είναι ιδανικό για εργασίες που απαιτούν μεγάλη παράλληλη εκτέλεση (brute-forcing;) ή για εργασίες που δεν πρέπει να αποκλείουν το κύριο νήμα: Για παράδειγμα, το κύριο νήμα στο iOS χειρίζεται τις αλληλεπιδράσεις με το UI, οπότε οποιαδήποτε άλλη λειτουργικότητα που θα μπορούσε να κάνει την εφαρμογή να κολλήσει (αναζήτηση, πρόσβαση σε ιστοσελίδα, ανάγνωση αρχείου...) διαχειρίζεται με αυτόν τον τρόπο.
Blocks
Ένα block είναι ένα αυτόνομο τμήμα κώδικα (όπως μια συνάρτηση με ορίσματα που επιστρέφει μια τιμή) και μπορεί επίσης να καθορίσει δεσμευμένες μεταβλητές.
Ωστόσο, στο επίπεδο του μεταγλωττιστή τα blocks δεν υπάρχουν, είναι os_object
s. Κάθε ένα από αυτά τα αντικείμενα αποτελείται από δύο δομές:
block literal:
Ξεκινά με το πεδίο
isa
, που δείχνει στην κλάση του block:NSConcreteGlobalBlock
(blocks από__DATA.__const
)NSConcreteMallocBlock
(blocks στη στοίβα)NSConcreateStackBlock
(blocks στη στοίβα)Έχει
flags
(που υποδεικνύουν τα πεδία που υπάρχουν στην περιγραφή του block) και μερικά δεσμευμένα bytesΟ δείκτης συνάρτησης για κλήση
Ένας δείκτης στην περιγραφή του block
Δεσμευμένες μεταβλητές block (αν υπάρχουν)
block descriptor: Το μέγεθός του εξαρτάται από τα δεδομένα που υπάρχουν (όπως υποδεικνύεται στα προηγούμενα flags)
Έχει μερικά δεσμευμένα bytes
Το μέγεθός του
Συνήθως θα έχει ένα δείκτη σε μια υπογραφή στυλ Objective-C για να γνωρίζει πόσο χώρο χρειάζεται για τις παραμέτρους (σημαία
BLOCK_HAS_SIGNATURE
)Αν υπάρχουν αναφερόμενες μεταβλητές, αυτό το block θα έχει επίσης δείκτες σε ένα βοηθό αντιγραφής (αντιγραφή της τιμής στην αρχή) και βοηθό διάθεσης (απελευθέρωσή της).
Ουρές
Μια ουρά αποστολής είναι ένα αντικείμενο με όνομα που παρέχει ταξινόμηση FIFO των blocks για εκτέλεση.
Τα blocks τοποθετούνται σε ουρές για να εκτελεστούν, και αυτές υποστηρίζουν 2 λειτουργίες: DISPATCH_QUEUE_SERIAL
και DISPATCH_QUEUE_CONCURRENT
. Φυσικά η σειριακή δεν θα έχει προβλήματα συνθήκης αγώνα καθώς ένα block δεν θα εκτελεστεί μέχρι να ολοκληρωθεί το προηγούμενο. Αλλά ο άλλος τύπος ουράς μπορεί να το έχει.
Προεπιλεγμένες ουρές:
.main-thread
: Απόdispatch_get_main_queue()
.libdispatch-manager
: Διαχειριστής ουράς του GCD.root.libdispatch-manager
: Διαχειριστής ουράς του GCD.root.maintenance-qos
: Εργασίες χαμηλότερης προτεραιότητας.root.maintenance-qos.overcommit
.root.background-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Υψηλότερη προτεραιότητα.root.background-qos.overcommit
Σημειώστε ότι θα είναι το σύστημα που θα αποφασίσει ποια νήματα θα χειριστούν ποιες ουρές κάθε φορά (πολλά νήματα μπορεί να εργαστούν στην ίδια ουρά ή το ίδιο νήμα μπορεί να εργαστεί σε διαφορετικές ουρές σε κάποιο σημείο)
Χαρακτηριστικά
Όταν δημιουργείτε μια ουρά με το dispatch_queue_create
το τρίτο όρισμα είναι ένα dispatch_queue_attr_t
, το οποίο συνήθως είναι είτε DISPATCH_QUEUE_SERIAL
(που στην πραγματικότητα είναι NULL) είτε DISPATCH_QUEUE_CONCURRENT
που είναι ένας δείκτης σε μια δομή dispatch_queue_attr_t
που επιτρέπει τον έλεγχο ορισμένων παραμέτρων της ουράς.
Αντικείμενα αποστολής
Υπάρχουν αρκετά αντικείμενα που χρησιμοποιεί το libdispatch και οι ουρές και τα blocks είναι μόνο 2 από αυτά. Είναι δυνατόν να δημιουργήσετε αυτά τα αντικείμενα με το dispatch_object_create
:
block
data
: Μπλοκ δεδομένωνgroup
: Ομάδα blocksio
: Ασύγχρονα αιτήματα I/O`mach
Και αυτό είναι ένα παράδειγμα χρήσης παραλληλισμού με το dispatch_async
:
Swift
libswiftDispatch
είναι μια βιβλιοθήκη που παρέχει δεσμεύσεις Swift στο πλαίσιο Grand Central Dispatch (GCD) το οποίο αρχικά έχει γραφτεί σε C.
Η βιβλιοθήκη libswiftDispatch
τυλίγει τα APIs του C GCD σε μια διεπαφή που είναι πιο φιλική προς τη γλώσσα Swift, κάνοντας το πιο εύκολο και πιο ευανάγνωστο για τους προγραμματιστές Swift να εργαστούν με το GCD.
DispatchQueue.global().sync{ ... }
DispatchQueue.global().async{ ... }
let onceToken = DispatchOnce(); onceToken.perform { ... }
async await
var (data, response) = await URLSession.shared.data(from: URL(string: "https://api.example.com/getData"))
Παράδειγμα κώδικα:
Frida
Το παρακάτω σενάριο του Frida μπορεί να χρησιμοποιηθεί για ενσωμάτωση σε αρκετές λειτουργίες dispatch
και εξαγωγή του ονόματος της ουράς, του αναστροφής και του block: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Ghidra
Προς το παρόν το Ghidra δεν κατανοεί ούτε τη δομή ObjectiveC dispatch_block_t
, ούτε αυτή της swift_dispatch_block
.
Έτσι, αν θέλετε να τις κατανοήσει, μπορείτε απλά να τις δηλώσετε:
Στη συνέχεια, βρείτε ένα σημείο στον κώδικα όπου χρησιμοποιούνται:
Σημειώστε όλες τις αναφορές που γίνονται στο "block" για να καταλάβετε πώς μπορείτε να καταλάβετε ότι η δομή χρησιμοποιείται.
Κάντε δεξί κλικ στη μεταβλητή -> Αλλαγή τύπου μεταβλητής και επιλέξτε σε αυτήν την περίπτωση swift_dispatch_block
:
Το Ghidra θα επαναγράψει αυτόματα τα πάντα:
Αναφορές
Last updated