Mac OS X Internals: A Systems Approach
9.17. Core Foundation IPC
Core Foundation is an important Mac OS X framework that provides fundamental data types and several essential services, including a variety of IPC mechanisms. 9.17.1. Notifications
The Core Foundation (CF) framework provides the CFNotificationCenter data type, which, along with its associated functions, can be used for sending and receiving intraprocess and interprocess notifications. A CF notification is a message consisting of the following three elements:
The CF notification API supports the following three types of notification centers (a process can have at most one of each type): local center, distributed center, and Darwin notify center. The local center is process-local, and the other two are for distributed notifications. The distributed center provides access to distnoted (see Section 9.16.1), whereas the Darwin notify center provides access to notifyd (see Section 9.16.2). A reference to any of these centers is obtained by calling the appropriate CFNotificationCenterGet* function, which returns a CFNotificationCenterRef data type. // distributed notification center (/usr/sbin/notifyd) CFNotificationCenterRef CFNotificationCenterGetDarwinNotifyCenter(void); // distributed notification center (/usr/sbin/distnoted) CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void); // process-local notification center CFNotificationCenterRef CFNotificationCenterGetLocalCenter(void); Once you have a reference to a notification center, you can add an observer, remove an observer, or post notifications. The same set of functions is used to perform these operations regardless of the notification center type. Figure 962 shows a program that posts notifications to the distributed center. Figure 962. A program for posting Core Foundation distributed notifications
Figure 963 shows a program that registers an observer of all distributed notifications, after which it runs in a loop, printing information about each received notification. When it receives a notification named cancel, it removes the observer and terminates the loop. Note that the observer program uses the concept of a run loop, which is discussed in Section 9.17.2. Figure 963. A program for observing Core Foundation distributed notifications
Let us now test the poster and observer programs from Figures 962 and 963, respectively. $ gcc -Wall -o cfposter CFNotificationPoster.c -framework CoreFoundation $ gcc -Wall -o cfobserver CFNotificationObserver.c -framework CoreFoundation $ ./cfobserver # another shell prompt $ ./cfposter system mach Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> system mach (null) $ ./cfposter cancel junk Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> cancel junk (null) $
As we noted in Section 9.16.1, an observer of all distributed notifications will receive notifications from other posters. If you let the cfobserver program run for some time, you will see a variety of notifications being sent by different parts of the operating system. ... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.carbon.core.DirectoryNotification /.vol/234881027/244950 (null) ... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.screensaver.willstop (null) (null) ... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.Cookies.Synced 448 (null) ...
The object identifier 448 in the notification named com.apple. Cookies.Synced is the process ID of the Safari application. The iTunes application is a poster of interesting notifications that contain dictionaries with detailed song informationsuch as details of a new song as it starts to play. 9.17.2. The Run Loop
A run loop is an event loop that monitors sources of input to a task, and when an input source becomes ready for processing (i.e., the source has some activity), the run loop dispatches control to all entities that have registered interest in the sources. Examples of such input sources include user-input devices, network connections, timer events, and asynchronous callbacks.
A CFRunLoop is an opaque Core Foundation object that provides the run-loop abstraction. Carbon and Cocoa both use CFRunLoop as a building block to implement higher-level event loops. For example, the NSRunLoop class in Cocoa is implemented atop CFRunLoop.
The following points are noteworthy about run loops.
Figure 964 shows the types of objects that can be placed into a run loop, the functions used for creating the objects, and the functions used for adding them to a run loop. Figure 964. Creating and adding run-loop input sources
9.17.2.1. Run-Loop Observers
A run-loop observer (CFRunLoopObserver) is a run-loop input source that generates events at one or more specified locations, or activity stages, within the run loop. The stages of interest are specified as the bitwise OR of individual stage identifiers when the observer is created. For example, the kCFRunLoopEntry stage represents the entrance of the run loopit will be hit each time the run loop starts running as a result of either CFRunLoopRun() or CFRunLoopRunInMode().
Note that the term observer is somewhat confusing in that so far we have used the term to refer to entities that receive notifications; here, an observer generates notifications, although it is observing the run loop's activities in doing so. 9.17.2.2. Run-Loop Sources
A run-loop source (CFRunLoopSource) abstracts an underlying source of events, such as a Mach port (CFMachPort), a message port (CFMessagePort), or a network socket (CFSocket). The arrival of a message on one of these communication end points is an asynchronous event that represents input to a run loop. Core Foundation also allows custom input sources to be created. Given an underlying primitive type supported by Core Foundation as an input source, a CFRunLoopSource object must be created before the source can be added to a run loop. For example, given a CFSocket, the CFSocketCreateRunLoopSource() function returns a reference to a CFRunLoopSource, which can then be added to a run loop using CFRunLoopAddSource(). Let us look at some properties of the input sources provided by Core Foundation. CFMachPort
A CFMachPort is a wrapper around a native Mach port, but it allows the port to be used only for receiving messagesCore Foundation does not provide a function for sending messages. However, CFMachPortGetPort() retrieves the underlying native Mach port, which can then be used with the Mach APIs to send messages. Conversely, CFMachPortCreateWithPort() creates a CFMachPort from an existing native Mach port. If an existing port is not being used, CFMachPortCreate() can be used to create both a CFMachPort and the underlying native port. Both creation functions accept as an argument a callback function, which is called when a message arrives on the port. The callback is passed a pointer to the raw messagespecifically, the mach_msg_header_t structure. CFMachPortSetInvalidationCallBack() can be used to set another callback function that would be invoked when the port is invalidated. CFMessagePort
A CFMessagePort is a wrapper around two native Mach portsunlike a CFMachPort, a CFMessagePort supports bidirectional communication. Like a CFMachPort, it can be used only for local (non-networked) intraprocess or interprocess communication since Mac OS X does not provide network-transparent Mach IPC. In a typical use of CFMessagePort, a process creates a local port through CFMessagePortCreateLocal(), specifying a string name with which to register the port. The name can also be set later or changed by using CFMessagePortSetName(). Thereafter, another process can call CFMessagePortCreateRemote() with the same string name to create a CFMessagePort that is connected to the remote (to this process) port. Let us look at an example of using a CFMessagePort. Figure 965 shows a server that creates a CFMessagePort and advertises its name. The server then creates a run-loop source from the port and adds it to the main run loop. On receiving a message, the only service the server provides is printing the contents of the messageno reply is sent. Figure 965. A CFMessagePort server
Figure 966 shows the source for a client of the CFMessagePort server. The client uses the remote port's name (shared between the client and the server) to create a connection and sends a few bytes of data to the server. Figure 966. A CFMessagePort client
Let us now test the CFMessagePort server and client programs. $ gcc -Wall -o client CFMessagePortClient.c -framework CoreFoundation $ gcc -Wall -o server CFMessagePortServer.c -framework CoreFoundation $ ./server # another shell prompt $ ./client message sent message received <CFData 0x300990 [0xa0728150]>{length = 4, capacity = 4, bytes = 0x01020304}
CFSocket
A CFSocket is conceptually similar to a CFMessagePort with the key difference being that BSD sockets are used as the underlying communication channel. A CFSocket can be created in several ways: from scratch, from an existing native socket, or even from a native socket that is already connected. A CFSocket supports callbacks for several types of socket activity, for example, when there is data to read (kCFSocketReadCallBack), when the socket is writable (kCFSocketWriteCallBack), when an explicitly backgrounded connection attempt finishes (kCFSocketConnectCallBack), and so on. Figure 967 shows a client program that uses a CFSocket to connect to a well-known time server and retrieves the current time. Figure 967. A CFSocket client
CFRunLoopTimer
A CFRunLoopTimer is a special case of a run-loop source that can be set to fire at some time in the future, either periodically or one time only. In the latter case, the timer is automatically invalidated. A CFRunLoopTimer is created using CFRunLoopTimerCreate(), which takes a callback function as an argument. The timer can then be added to a run loop.
A run loop must be running to be able to process a timer. A timer can only be added to one run loop at a time, although it can be in multiple modes in that run loop.
Figure 968 shows a program that creates a periodic timer, adds it to the main run loop, and sets the run loop running for a given time. While the run loop is running, the timer gets processed and the associated callback is invoked. Figure 968. Using a CFRunLoopTimer
|
Категории