NeXTSTEPDRIVERKIT:Chapter2 6

From 흡혈양파의 번역工房
Jump to navigation Jump to search
Synchronizing Driver Requests with the I/O Thread

I/O 스레드와 드라이버 요청의 동기화(Synchronizing Driver Requests with the I/O Thread)

A device driver receives requests to perform operations from various sources external to the driver via its exported methods. Both the user's kernel thread and the I/O thread may invoke the driver's exported methods against the driver. As the previous section "Threads in Kernel-Level Drivers" noted, a driver can run in three places: The user's kernel thread (the thread that synchronously receives user commands), in another kernel thread (a timeout function, for example), or in the I/O thread. This section discusses how to coordinate these activities in different threads.
장치 드라이버는 내보내는(exported) 메서드를 통해 드라이버 외부의 다양한 소스에서 작업 수행의 요청을 받습니다. "Threads in Kernel-Level Drivers" 절에서 언급했듯이, 드라이버는 다음 세 곳에서 실행될 수 있는데, 사용자의 커널 스레드 (사용자 명령을 동기식으로 수신하는 스레드), 다른 커널 스레드 (예 : 시간 초과 함수) 또는 I/O 스레드 등입니다. 이 절에서는 여러 스레드에서 이러한 활동을 조정하는 방법에 대해 설명합니다.


You may not need to be concerned about synchronizing these requests with your driver. Display drivers don't use an I/O thread. For other devices, the default I/O thread (which is started automatically by the network, SCSI controller, and sound device classes) handles this coordination for you. The driver's methods are invoked from the appropriate threads, and so on. Most display, network, SCSI controller, and sound drivers require no further integration.
이러한 요청들을 드라이버와 동기화하는데 신경 쓰지 않아도 됩니다. 디스플레이 드라이버는 I/O 스레드를 사용하지 않습니다. 다른 장치의 경우 기본 I/O 스레드(네트워크, SCSI 컨트롤러 및 사운드 장치 클래스에 의해 자동으로 시작)가 이 조정을 처리합니다. 드라이버의 메서드는 적절한 스레드에서 호출됩니다. 대부분의 디스플레이, 네트워크, SCSI 컨트롤러 및 사운드 드라이버에는 더 이상 통합할 필요가 없습니다.


For some devices, such as SCSI peripherals, you may need to coordinate these requests and services between the various threads. If you had to provide your own driver interface, for instance, you need to pay attention to these issues.
SCSI 주변 장치와 같은 일부 장치의 경우 다양한 스레드 사이에 이러한 요청과 서비스를 조정해야할 수도 있습니다. 예를 들어 자신의 드라이버 인터페이스를 제공해야 한다면 이러한 문제에 주의를 기울여야 합니다.


In keeping with the Driver Kit paradigm, exported methods should generally not perform I/O requests directly but send requests to the I/O thread. Only the I/O thread touches hardware and other critical resources. This way, no exported methods manipulate hardware or other critical resources--the I/O thread does all of the work. This structure eliminates the need to use the UNIX spl... functions to change priority, to disable interrupts, or to employ other mechanisms to prevent multiple threads from accessing the hardware and interfering with each other. The I/O thread can perform operations in a straightforward sequence as it chooses, without interference from other threads. The benefit is that your code will be simpler and more reliable, your design will be more comprehensible, and you'll eliminate deadlocks and race conditions.
Driver Kit 패러다임을 유지하면서, 내보내는 메소드는 일반적으로 I/O 요청을 직접 수행하지 않고 I/O 스레드로 요청을 보냅니다. I/O 스레드만 하드웨어 및 기타 중요한 리소스에 접근합니다. 이렇게하면 내보내는 메소드가 하드웨어나 다른 중요한 리소스를 조작하지 않아 I/O 스레드가 모든 작업을 수행합니다. 이 구조는 UNIX spl ... 함수를 사용하여 우선 순위를 변경하거나, 인터럽트를 비활성화 하거나, 여러 스레드가 하드웨어에 액세스하여 서로 간섭하지 못하게 하는 다른 메커니즘을 사용하지 않아도 됩니다. I/O 스레드는 다른 스레드의 간섭없이 원하는 순서대로 작업을 수행할 수 있습니다. 이점은 코드가 보다 간단하고 안정적이고, 설계가 더 이해하기 쉬워지며, 교착 상태 및 경쟁 조건을 제거한다는 것입니다.


I/O 스레드 시작

To start the default I/O thread, invoke IODirectDevice's startIOThread method. It forks the thread and invokes attachInterruptPort, which creates an interrupt port for the thread. The thread receives Mach messages on this port. A Mach message could be from the user's kernel thread requesting it to execute an I/O operation, or it could be from the kernel notifying the I/O thread that an interrupt occurred. Some of the device classes, such as those for SCSI controllers, network, and sound devices, start up the default I/O thread automatically.
기본 I/O 스레드를 시작하려면 IODirectDevice 의 startIOThread 메소드를 호출하십시오. 스레드를 분기하고 attachInterruptPort 를 호출해서 스레드의 인터럽트 포트를 만듭니다. 스레드는 이렇게 만들어진 포트에서 Mach 메시지를 수신합니다. Mach 메시지는 사용자의 커널 스레드가 I/O 작업을 실행하도록 요청하거나, I/O 스레드에 인터럽트가 발생했음을 커널이 알리는 메시지일 수도 있습니다. SCSI 컨트롤러, 네트워크 및 사운드 장치와 같은 일부 장치 클래스는 자동으로 기본 I/O 스레드를 시작합니다.


Note: Even though it is called an interrupt port, the I/O thread receives all its Mach messages on this port--not just interrupt messages.
Note: 인터럽트 포트라고해도 I/O 스레드는 인터럽트 메시지가 아닌, 이 포트에서 모든 Mach 메시지를 수신합니다.


To start a custom I/O thread, call the function IOForkThread(). Its argument is a function, which consists of a while loop that waits for and executes commands from the rest of the driver. This function runs in the kernel's I/O task. Like the default I/O thread, only this function should touch the hardware.
사용자 정의 I/O 스레드를 시작하려면 함수 IOForkThread() 를 호출하십시오. 이 함수의 인수는 드라이버에서 명령을 기다리고 실행하는 while 루프로 구성되어 있습니다. 이 함수는 커널의 I/O 작업에서 실행됩니다. 기본 I/O 스레드와 마찬가지로 이 기능만 하드웨어에 닿아야 합니다.


I/O 스레드를 동기화하기

A device driver's exported methods execute in response to some action initiated by a user program. A method may have two flavors of communication with the I/O thread. In some cases, an exported method needs to do synchronous communication with the I/O thread--that is, the exported method sends some work to the I/O thread and waits until that work is done. In other cases, an exported method does asynchronous I/O--it just sends some work to the I/O thread and continues executing, without waiting for the work to be done.
장치 드라이버의 내보내는 메소드는 사용자 프로그램에 의해 시작된 일부 동작에 응답하여 실행됩니다. 메서드는 I/O 스레드와 통신하는 두 가지 느낌(flavors) 지니고 있습니다. 경우에 따라 내보내는 메소드가 I/O 스레드와의 동기식 통신을 수행해야합니다. 즉, 내보내는 메소드가 일부 작업을 I/O 스레드로 보내고 작업이 완료될 때까지 대기합니다. 다른 경우, 내보내진(exported) 메소드는 비동기 I/O 를 수행합니다. 다른 경우라면, 완료될 때까지 기다리지 않고 I/O 스레드로 작업을 보내고 실행을 계속합니다.


In either case, the I/O thread may not be ready to perform the requested hardware operation when the user thread requests it. Therefore, there must be a way to synchronize the interface functions with the I/O thread. This synchronization is essentially automatic if you use the default I/O thread, because the thread takes requests only when it's ready to handle them.
두 경우 모두 사용자 스레드가 요청한 하드웨어 작업을 I/O 스레드가 수행할 준비가 되지 않았을 수도 있습니다. 따라서 인터페이스 함수를 I/O 스레드와 동기화하는 방법이 있어야 합니다. 이 동기화는 기본 I/O 스레드를 사용하는 경우 본질적으로 자동화가 되어 있습니다. 왜냐하면 스레드가 스레드를 처리할 준비가 되었을 때만 요청을 처리하기 때문입니다.


Coordination between the driver's user-level exported methods and the I/O thread can occur in two ways:
드라이버의 사용자 수준 내보내기(exported) 메서드와 I/O 스레드 사이의 조정은 두 가지 방식으로 발생될 수 있습니다:

  • Using Mach messages, but it's recommended that they be used only with the default I/O thread. See "Synchronizing Using Mach Messages" later in this section.
    Mach 메시지를 사용하지만 기본 I/O 스레드 에서만 사용하는 것이 좋습니다. 이 섹션 뒷부분의 "Synchronizing Using Mach Messages" 를 참조하십시오.
  • Using a type of lock known as a condition lock. See "Synchronizing Using Condition Locks" later in this section. They're fast and easy to use. NXConditionLock is documented in the Mach Kit in NEXTSTEP General Reference.

상태 잠금이라고하는 잠금 유형을 사용합니다. 이 섹션 뒷부분의 "Synchronizing Using Condition Locks" 를 참조하십시오. 빠르고 쉽게 사용할 수 있습니다. NXConditionLock 은 NEXTSTEP General Reference 의 Mach Kit 에 문서화되어 있습니다.


Sometimes, for performance or other reasons, a driver might have its exported methods perform some I/O directly without going through the I/O thread. An Ethernet driver might be an example of this. The method that's called when a client wants to send a packet out to the network might perform no I/O--it might just add a DMA frame to the device's DMA queue. The exported method could do this directly without waking up the I/O thread. The Ethernet I/O thread would basically just service interrupts and dispatch incoming packets. A lock in the driver would protect access to the hardware in the case where the output method has to start up an idle DMA channel.
때로는 성능이나 다른 이유로 인해 드라이버는 내보내는 메소드가 I/O 스레드를 거치지 않고 직접 일부 I/O 를 수행하도록 할 수 있습니다. 이더넷 드라이버가 좋은 예제일 수 있습니다. 클라이언트가 네트워크로 패킷을 보내려고 할 때 호출되는 메서드는 I/O 를 수행하지 않을 수도 있습니다. 이 메서드는 장치의 DMA 큐에 DMA 프레임을 추가하는 것만 합니다. 내보내는 메소드는 I/O 스레드를 깨우지 않고 직접 이 작업을 수행할 수 있습니다. 이더넷 I/O 스레드는 기본적으로 인터럽트를 처리하고 들어오는 패킷을 전달합니다. 드라이버의 lock 은 출력 메소드가 유휴 DMA 채널을 시작해야하는 경우 하드웨어에 대한 액세스를 보호합니다.


Mach 메시지를 사용한 동기화(Synchronizing Using Mach Messages)

A user-level process typically doesn't communicate directly with the driver. The user-level process communicates with a set of UNIX entry points or with a loadable kernel server, as indicated in "Interfacing with the Driver" These entry points or loadable kernel server can then communicate with the I/O thread via Objective C messages (through the driver's exported methods) or Mach messages. Both synchronous and asynchronous I/O requests can be performed using Mach messages between the exported methods and the I/O thread.
일반적으로 사용자 수준 프로세스는 드라이버와 직접 통신하지 않습니다.


A way of communicating with the I/O thread is supported by the default I/O thread provided by IODirectDevice. In this scheme, each request is sent to the IODirectDevice's interrupt port, using a message ID. The file /NextDeveloper/Headers/driverkit/interruptMsg.h defines a set of messages. The only information in a message is its ID. Command buffers or other data, for instance, are not part of the message. The default I/O thread invokes one of the following methods, based on the message ID received:
사용자 수준 프로세스는 UNIX 진입 지점 세트 또는 로드 가능한 커널 서버와 통신하는데, "Interfacing with the Driver"에서 설명한대로 이러한 진입 지점 또는로드 가능한 커널 서버는 Objective C 메시지(드라이버가 내보내는 메소드를 통해) 또는 Mach 메시지를 통해 I/O 스레드와 통신할 수 있습니다.

  • Message ID
    • Method Invoked
  • IO_TIMEOUT_MSG
    • timeoutOccurred
  • IO_COMMAND_MSG
    • commandRequestOccurred
  • IO_DEVICE_INTERRUPT_MSG
    • interruptOccurred
  • IO_DEVICE_INTERRUPT_MSG_FIRST to IO_DEVICE_INTERRUPT_MSG_LAST
    • interruptOccurredAt:
  • (anything else)
    • otherOccurred:


You implement these methods to respond appropriately to the condition.
이러한 메소드를 구현하여 조건에 적절하게 응답합니다.


Interrupt messages are sent automatically by the kernel. If you want to use the other types of Mach messages, your driver or some other module it works with must explicitly send them. An advantage of using Mach messages to notify the I/O thread of requests is that the thread can service incoming I/O requests while waiting for interrupt messages.
인터럽트 메시지는 커널에 의해 자동으로 전송됩니다. 다른 종류의 Mach 메시지를 사용하려면 드라이버나 다른 모듈이 명시적으로 메시지를 보내야 합니다. Mach 메시지를 사용해서 I/O 스레드에 요청을 알리는 장점은 스레드가 인터럽트 메시지를 기다리는 동안 들어오는 I/O 요청을 처리할 수 ​​있다는 것입니다.


You can also devise your own Mach messages and invoke whatever I/O thread methods you choose in response to them. You would implement the receiveMsg method in IODirectDevice to dequeue the next Mach message from the interrupt port.
또한 사용자 자신의 Mach 메시지를 고안하고 이에 대한 응답으로 선택한 I/O 스레드 메소드를 호출할 수 있습니다. 인터럽트 포트에서 다음 Mach 메시지를 큐에서 빼내기 위해 IODirectDevice 에서 receiveMsg 메소드를 구현할 것이다.


The IOSCSIController class is an example of this. The SCSI bus is capable of performing overlapped I/O requests, in which one I/O request can be started while another is in progress and is disconnected from the bus. In this case, the IOSCSIController I/O thread receives I/O requests through Mach messages.
IOSCSIController 클래스는 이에 대한 예입니다. SCSI 버스는 다른 I/O 요청이 진행 중이며, 버스에서 연결이 끊어져있는 동안 I/O 요청을 시작할 수 있는 중첩된 I/O 요청을 수행할 수 있습니다. 이경우 IOSCSIController 의 I/O 스레드는 Mach 메시지를 통해 I/O 요청을 받습니다.


IOSCSIController itself doesn't manage, allocate, or use any Mach ports at all. It depends on startIOThread to set up one port, the standard interrupt port. Everything else is done by subclasses of IOSCSIController. IOSCSIController subclasses currently use the interrupt port for all Mach interprocess communication, including command messages and timeout messages. The messages are distinguished by their message ID, not the port to which they are sent.
IOSCSIController 자체는 Mach 포트를 전혀 관리, 할당 또는 사용하지 않습니다. startIOThread 에 따라 표준 인터럽트 포트인 하나의 포트를 설정합니다. 다른 모든 것은 IOSCSIController 의 하위 클래스에 의해 수행됩니다. IOSCSIController 하위 클래스는 현재 명령 메시지 및 시간 초과 메시지를 포함하여 모든 Mach 프로세스 사이의 통신에 인터럽트 포트를 사용합니다. 메시지는 전송된 포트가 아니라 메시지 ID 로 구별됩니다.


The example SCSI driver in /NextDeveloper/Examples/DriverKit/Adaptec1542B is a good illustration of these techniques.
/NextDeveloper/Examples/DriverKit/Adaptec1542B 의 예제 SCSI 드라이버는 이러한 기술을 잘 보여줍니다.


An older technique that created a custom Mach message that included the command buffer is no longer used. It's been replaced by the mechanism of enqueuing a command buffer on some well-known location (such as an instance variable) and sending a command message to the interrupt port. This results in commandRequestOccurred being invoked by the I/O thread, as noted above.
명령 버퍼를 포함하는 사용자 지정 Mach 메시지를 만든 이전 기술은 더이상 사용되지 않습니다. 그것은 잘 알려진 위치(예 : 인스턴스 변수)에 명령 버퍼를 대기 행렬에 넣고, 인터럽트 포트에 명령 메시지를 보내는 메커니즘으로 대체되었습니다. 위에서 언급한 것처럼 I/O 스레드에 의해 commandRequestOccurred 가 호출됩니다.


상태 잠금을 사용한 동기화(Synchronizing Using Condition Locks)

Condition locks are provided by the Mach Kit's NXConditionLock class, which works at both user and kernel level. For information about NXConditionLock beyond what's given here, see NEXTSTEP General Reference.
상태 잠금(Condition locks)은 사용자 및 커널 수준에서 작동하는 Mach Kit 의 NXConditionLock 클래스에 의해 제공됩니다. NXConditionLock 에 대한 자세한 내용은 NEXTSTEP General Reference 를 참조하십시오.

Using Mach messages and condition locks for synchronization aren't necessarily mutually exclusive. For instance, you could use a condition lock on a buffer as illustrated in "Using a Command Buffer" below and have the I/O thread wait for Mach messages on its interrupt port. However, the following two synchronization techniques are mutually exclusive:
동기화를 위해 Mach 메시지와 상태 잠금을 사용하는 것이 상호 배타적일 필요는 없습니다. 예를 들어 아래의 "Using a Command Buffer" 에서 설명한대로 버퍼에 상태 잠금을 사용하고 I/O 스레드가 인터럽트 포트에서 Mach 메시지를 기다리게 할 수 있습니다. 그러나 다음 두 동기화 기술은 상호 배타적입니다:

  • I/O thread waiting for messages on its interrupt port
    인터럽트 포트에서 메시지를 기다리는 I/O 스레드
  • I/O thread waiting for work using a condition lock (as shown in the example below)
    상태 잠금을 사용하여 작업을 기다리는 I/O 스레드 (아래 예제 참조)


A general technique for passing I/O information from a driver's exported methods to its I/O thread using condition locks is shown below and illustrated with an example.
상태 잠금을 사용해서 드라이버의 내보내는 메소드에서 I/O 스레드로 I/O 정보를 전달하는 일반적인 기술은 아래에서 확인할 수 있으며 예제로 설명되어 있습니다.


명령 버퍼 사용하기(Using a Command Buffer)

Some known location, perhaps an instance variable in the driver object, can be used to pass commands from the exported driver methods to the I/O thread. This variable may contain a structure (called cmdBuf_t in the following example) that serves as a command buffer, the fundamental unit of communication between exported methods and the I/O thread. You would define the command buffer differently for each driver--it must contain all the information needed by the I/O thread to perform a single I/O request. For example, a command buffer for a disk driver might contain a disk address, a virtual address, a byte count, and a read/write command flag. The command buffer might also contain fields by which the I/O thread can indicate completion status--for example, a device-specific status field and a field indicating the number of bytes transferred.
일부 알려진 위치, 드라이버 개체의 인스턴스 변수를 사용해서 드라이버 메서드의 내보내는 명령을 I/O 스레드에 전달할 수 있습니다. 이 변수에는 내보내는 메소드와 I/O 스레드 양쪽 통신의 기본 단위인 명령 버퍼로 사용되는 구조(다음 예제에서 cmdBuf_t 를 호출부분)가 포함될 수 있습니다. 각 드라이버마다 명령 버퍼를 다르게 정의해야합니다. 단일 I/O 요청을 수행하기 위해 I/O 스레드가 필요로하는 모든 정보를 포함해야 합니다. 예를 들어 디스크 드라이버의 명령 버퍼에는 디스크 주소, 가상 주소, 바이트수 및 read/write 명령 플래그가 포함될 수 있습니다. 명령 버퍼에는 I/O 스레드가 완료 상태(예 : 장치별 상태 필드 및 전송된 바이트 수를 나타내는 필드)를 나타낼 수 있는 필드가 포함될 수도 있습니다.


The command buffer contains a variable for a token that indicates which hardware operation the I/O thread should perform. This variable may be the value of an enum, for instance.
명령 버퍼에는 I/O 스레드가 수행해야할 하드웨어 조작을 나타내는 토큰에 대한 변수가 들어 있습니다. 이 변수는 인스턴스에 대한 열거형(enum) 값일 수 있습니다.


The command buffer also contains an NXConditionLock (called cmdBufLock in the example below), which manages access to the command buffer. An exported method (a write routine, for example) sets the lock unconditionally when it wants the I/O thread to execute a command. It sets up the command buffer for the operation it wants to perform and releases the lock with the condition NOT_COMPLETE. It then waits on the lock until its state is COMPLETE, which results in the user thread sleeping until the I/O thread sets the lock condition to COMPLETE. Meanwhile, the I/O thread is waiting on the lock until its state is NOT_COMPLETE and it has a command to execute. When those conditions are satisfied, the I/O thread then sets the lock. When it finishes executing the command, it releases the lock and sets its state to COMPLETE, which is the cue for the user thread to wake up.
명령 버퍼에는 명령 버퍼에 대한 접근을 관리하는 NXConditionLock(아래 예에서 cmdBufLock 호출)이 포함되어 있습니다. 내보내는 메소드(예 : 쓰기 루틴)는 I/O 스레드가 명령을 실행할 때 잠금(lock)을 무조건 설정합니다. 수행하려는 조작에 대한 명령 버퍼를 설정하고 NOT_COMPLETE 상태로 잠금을 해제합니다. 이후에 상태가 COMPLETE 가 될 때까지 잠금(lock)을 기다리면, I/O 스레드가 잠금 상태를 COMPLETE 로 설정할 때까지 사용자 스레드는 잠자기 상태가 됩니다. 한편, I/O 스레드는 상태(state)가 NOT_COMPLETE 이고 실행 명령이있을 때까지 잠금을 기다리고 있습니다. 이러한 조건이 충족되면 I/O 스레드는 잠금을 설정합니다. 명령 실행이 끝나면 잠금을 해제하고 상태(state)를 COMPLETE 로 설정하는데, 이 상태는 사용자 스레드가 깨어나는 신호입니다.


다중 요청 관리(Managing Multiple Requests)

You can also queue multiple requests with condition locks. This lock works independently of the lock indicating a command completion.
상태 잠금(condition locks)을 사용하여 여러 요청을 대기시킬 수 있습니다. 이 잠금은 명령 완료를 나타내는 잠금과는 독립적으로 작동합니다.


Declare an instance variable (which may be in the driver object) that's the head of a queue of command buffers. Command buffers are added to the queue by exported methods and removed from the queue by the I/O thread.
명령 버퍼의 head 에 인스턴스 변수(드라이버 객체에 있을 수 있음)를 선언하십시오. 명령 버퍼는 내보내는 메서드에 의해서 큐에 추가되며, I/O 스레드에 의해서 큐에서 제거됩니다.


Declare an instance variable that's an NXConditionLock (this variable is called ioQueueLock in the following example). This lock protects the queue and provides a way for the I/O thread to sleep until it has work to do. This lock has two states, QUEUE_EMPTY and QUEUE_NOT_EMPTY. Note that each command buffer has its own condition lock (cmdBufLock in the example below) to control completion of the I/O request specified in that particular buffer.
NXConditionLock 인 인스턴스 변수를 선언하십시오 (이 변수는 다음 예제에서 ioQueueLock 이라고 함). 이 잠금(lock)은 대기열을 보호하고 I/O 스레드가 수행할 때까지 잠자기 상태로 유지하는 방법을 제공합니다. 이 잠금에는 QUEUE_EMPTY 와 QUEUE_NOT_EMPTY 의 두 가지 상태가 있습니다. 각 명령 버퍼에는 해당 버퍼에 지정된 I/O 요청의 완료를 제어하기 위해서 (아래 예에서 cmdBufLock) 자체 조건 잠금(own condition lock)이 있습니다.


예제

Here's an example of an exported method that communicates with the I/O thread synchronously. This example shows how locks can be used to synchronize with a custom I/O thread in lieu of command messages to the interrupt port. It also shows how to queue multiple requests. Italicized text delineated in angle brackets, that is << >>, is to be filled in with device-specific code.
다음은 I/O 스레드와 동기적으로 통신하는 내보내는 메소드의 예입니다. 이 예는 명령 메시지 대신에, 인터럽트 포트에 대한 사용자 지정 I/O 스레드와의 동기화에 잠금을 사용하는 방법을 보여줍니다. 또한 여러 요청을 큐에 저장하는 방법을 보여줍니다. 꺾쇠 괄호로 표시된 기울임꼴 텍스트 (즉, << >>) 는 장치 관련 코드로 채워집니다.

- (IOReturn)makeIORequest:(int)anArgument
{
    cmdBuf_t cmdBuf;

    /* Initialize lock */
    [cmdBuf.cmdBufLock lock];

    << Fill in cmdBuf fields appropriate for this I/O. >>
        /* Unlock and set cmdBufLock to condition NOT_COMPLETE. */
        [cmdBuf.cmdBufLock unlockWith:NOT_COMPLETE];

    /*
     * Enqueue this command buffer and let the I/O thread
     * know that it has work to do.
     */
    [ioQueueLock lock];
    << Enqueue cmdBuf on ioQueue. >>
        [ioQueueLock unlockWith:QUEUE_NOT_EMPTY];

    /*
     * Wait for I/O thread to process the command buffer and signal
     * completion.
     *
     * NOTE: The following is necessary only for synchronous I/O.
     */
    [cmdBuf.cmdBufLock lockWhen:COMPLETE]; //ONLY FOR SYNCHRONOUS
    [cmdBuf.cmdBufLock unlock];

    /*
     * I/O is complete.
     */
    << Free necessary data from cmdBuf. >>
        << Return I/O result. >>
}


The I/O thread invokes the following method while waiting for work from the exported methods:
I/O 스레드는 내보내는 메서드에서 작업을 기다리는 동안에 다음 메서드를 호출합니다.

- (cmdBuf_t *)waitForWork
{
    cmdBuf_t *cmdBuf;

    [ioQueueLock lockWhen:QUEUE_NOT_EMPTY];
    << Dequeue head of ioQueue, save in cmdBuf. >>
        if(<< ioQueue is empty >>)
            [ioQueueLock unlockWith:QUEUE_EMPTY];
        else
            [ioQueueLock unlockWith:QUEUE_NOT_EMPTY];
    return cmdBuf;
}


The I/O thread executes the request and wakes up the user thread as follows:
I/O 스레드는 다음과 같이 요청을 실행하며 사용자 스레드를 깨웁니다.

- (void)performIO:(cmdBuf_t *)cmdBuf
{
    << Execute I/O request >>
        [cmdBuf->cmdBufLock lock];
    [cmdBuf->cmdBufLock unlockWith:COMPLETE];
}


I/O 태스크 외부로 메시지 보내기

When a driver executes outside the I/O task, it no longer has send rights to ports that it has in the I/O task. A workaround for this problem is to use the msg_send_from_kernel() function instead of msg_send() to send the message to the port. The port must first be converted to a form that's valid in the kernel's IPC space, using IOConvertPort(). An example of using msg_send_from_kernel() is in the IOSCSIController class specification.
드라이버가 I/O 작업의 외부에서 실행되면 I/O 작업에 있는 포트에는 더이상 송신 권한이 없습니다. 이 문제를 해결하려면 msg_send() 대신에 msg_send_from_kernel() 함수를 사용해서 메시지를 포트로 보냅니다. 포트는 먼저 IOConvertPort() 를 사용해서 커널의 IPC 공간에서 유효한 형식으로 변환되어야 합니다. msg_send_from_kernel() 을 사용하는 예제는 IOSCSIController 클래스 사양(spec)에 있습니다.