반응형
파일 디스크립터에 대한 작업을 Stream-Based 또는 Random-Access Semantics를 사용하여 관리하는 객체입니다.
An object that manages operations on a file descriptor using either stream-based or random-access semantics.
oflag
- O_RDONLY: 파일을 읽기 전용 (read-only)으로 엽니다.
- O_WRONLY: 파일을 쓰기 전용 (write-only)으로 엽니다.
- O_RDWR: 파일을 읽기와 쓰기 모두 가능한(read-write) 모드로 엽니다.
- O_CREAT: 파일이 존재하지 않으면 새로 생성합니다. 파일이 이미 존재할 경우, 이 옵션은 아무런 영향을 미치지 않습니다.
- O_TRUNC: 파일을 쓰기 전용 또는 읽기-쓰기 모드로 열 때, 파일 크기를 0으로 잘라냅니다. 이 옵션은 파일의 내용을 삭제하므로 주의해서 사용해야 합니다.
- O_APPEND: 파일을 쓰기 전용 또는 읽기-쓰기 모드로 열 때, 쓰기 작업이 항상 파일의 끝에서 이루어지도록 합니다.
mode
파일이나 디렉토리의 퍼미션 (Permission)과 관련된 정보를 나타냅니다.
mode_t는 일반적으로 부호 없는 정수(unsigned integer)로 구현되며, 파일의 읽기, 쓰기, 실행 권한과 소유자, 그룹, 다른 사용자에 대한 접근 권한을 표현합니다.
mode에 대한 매크로 목록은 아래와 같습니다.
- S_IRUSR: 소유자에게 읽기 권한 부여
- S_IWUSR: 소유자에게 쓰기 권한 부여
- S_IXUSR: 소유자에게 실행 권한 부여
- S_IRGRP: 그룹에게 읽기 권한 부여
- S_IWGRP: 그룹에게 쓰기 권한 부여
- S_IXGRP: 그룹에게 실행 권한 부여
- S_IROTH: 다른 사용자에게 읽기 권한 부여
- S_IWOTH: 다른 사용자에게 쓰기 권한 부여
- S_IXOTH: 다른 사용자에게 실행 권한 부여
※ 파일 잠금 (File Locking)
오직 특정한 시간에 한 명의 사용자나 프로세스 접근만을 허용함으로써 컴퓨터 파일에 접근을 제한하는 구조입니다.
🛠 소스 코드 (Source Code)
더보기
/*
* Copyright (c) 2023 Universal-SystemKit. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// swiftlint:disable all
#if os(iOS) || os(macOS)
import Darwin
import Dispatch
import Foundation
@objc public class SKDispatchFile: NSObject, SKClass {
// MARK: - Typealias
public typealias FileIOReadCompletion = (Data, Int32) -> Swift.Void
public typealias FileIOWriteCompletion = (Int32) -> Swift.Void
// MARK: - Object Properties
public static var label: String = "com.SystemKit.SKDispatchFile"
public static var identifier: String = "870EBBA7-3167-4147-BE3A-82E0C4A108A2"
private let mode: mode_t
private let implementQueue: DispatchQueue
// MARK: - Initalize
public init(qualityOfService qos: DispatchQoS = .default, mode: mode_t = 0o777) {
self.mode = mode
self.implementQueue = DispatchQueue(label: SKDispatchFile.label, qos: qos, attributes: .concurrent)
}
}
// MARK: - Private Extension SKDispatchFile
private extension SKDispatchFile {
static func closeChannel(channel: DispatchIO, filePath: String, error: Int32) {
let result: Int32 = flock(channel.fileDescriptor, LOCK_UN)
logger.info("[SKDispatchFile][\(filePath)][error: \(result)] Unlock FileDescriptor")
channel.close(flags: DispatchIO.CloseFlags.stop)
logger.info("[SKDispatchFile][\(filePath)][error: \(error)] Close DispatchIO Operation")
}
final func createFileChannel(filePath: String, _ oflag: Int32) -> Optional<DispatchIO> {
let fileDescriptor = open(filePath, oflag, self.mode)
if fileDescriptor == EOF {
logger.error("[SKDispatchFile][\(filePath)] Could't create file descriptor")
return nil
}
// 파일 작업을 수행하기 전에 다른 프로세스 및 쓰레드에 대하여 원자성을 보장하기 위하여 파일 잠금을 수행합니다.
flock(fileDescriptor, LOCK_EX)
let channel = DispatchIO(type: .stream, fileDescriptor: fileDescriptor, queue: self.implementQueue) { error in
// The handler to execute once the channel is closed.
logger.info("[SKDispatchFile][\(filePath)][error: \(error)] DispatchIO Cleanup")
}
return channel
}
}
// MARK: - Public Extension SKDispatchFile
public extension SKDispatchFile {
/**
파일에 대하여 쓰기 작업을 수행합니다.
Perform a write operation on the file.
- Version: `1.0.0`
- Authors: `ChangYeop-Yang`
- NOTE: `스레드 안전 (Thread Safety)`
- Parameters:
- contents: 파일에 쓰기 작업을 수행하기 위한 `Data`를 입력받는 매개변수
- filePath: 쓰기 작업을 수행 할 파일 경로를 입력받는 매개변수
- completion: 쓰기 작업을 통한 `(Int32) -> Swift.Void` 형태의 결과물을 전달하는 매개변수
*/
final func write(contents: Data, filePath: String, _ completion: @escaping FileIOWriteCompletion) {
// 쓰기 작업을 수행하기 위한 DispatchIO 생성합니다.
guard let channel = self.createFileChannel(filePath: filePath, O_WRONLY | O_CREAT) else { return }
let body: (UnsafeRawBufferPointer) throws -> Swift.Void = { pointer in
// If the baseAddress of this buffer is nil, the count is zero.
guard let baseAddress = pointer.baseAddress else {
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: EOF)
return
}
let bytes = UnsafeRawBufferPointer(start: baseAddress, count: contents.count)
let data = DispatchData(bytes: bytes)
channel.write(offset: off_t.zero, data: data, queue: self.implementQueue) { done, data, error in
// 파일 쓰기 작업에 오류가 발생한 경우에는 읽기 작업을 종료합니다.
guard error == Int32.zero else {
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: error)
return
}
// 정상적으로 파일 쓰기 작업이 완료 된 경우에 파일 내용을 전달합니다.
if done {
// 쓰기 작업에 수행 한 모든 파일 내용을 전달합니다.
completion(error)
// 파일 쓰기 작업을 위해서 생성 한 DispatchIO을 닫습니다.
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: error)
}
}
}
do { try contents.withUnsafeBytes(body) }
catch let error as NSError {
let errcode: Int32 = Int32(error.code)
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: errcode)
return
}
}
/**
파일에 대하여 읽기 작업을 수행합니다.
Perform a read operation on the file.
- Version: `1.0.0`
- Authors: `ChangYeop-Yang`
- NOTE: `스레드 안전 (Thread Safety)`
- Parameters:
- filePath: 읽기 작업을 수행하는 파일 경로를 입력받는 매개변수
- completion: 읽기 작업을 통한 `(Data, Int32) -> Swift.Void` 형태의 결과물을 전달하는 매개변수
*/
final func read(filePath: String, _ completion: @escaping FileIOReadCompletion) {
// 파일 읽기 작업을 수행하기 전에 해당 파일이 실제로 존재하는지 확인합니다.
guard FileManager.default.fileExists(atPath: filePath) else {
logger.error("[SKDispatchFile][\(filePath)] The file does not exist at the specified path")
return
}
// 읽기 작업을 수행하기 위한 DispatchIO 생성합니다.
guard let channel = createFileChannel(filePath: filePath, O_RDONLY) else { return }
var rawData = Data()
channel.read(offset: off_t.zero, length: Int.max, queue: self.implementQueue) { done, data, error in
// 파일 읽기 작업에 오류가 발생한 경우에는 읽기 작업을 종료합니다.
guard error == Int32.zero else {
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: error)
return
}
// 정상적으로 파일로부터 데이터를 읽어들이는 경우
if let contentsOf = data { rawData.append(contentsOf: contentsOf) }
// 정상적으로 파일 읽기 작업이 완료 된 경우에 파일 내용을 전달합니다.
if done {
// 읽어들인 모든 파일 내용을 전달합니다.
completion(rawData, error)
// 파일 읽기 작업을 위해서 생성 한 DispatchIO을 닫습니다.
SKDispatchFile.closeChannel(channel: channel, filePath: filePath, error: error)
}
}
}
}
#endif
🚀 REFERENCE
반응형
'# 애플 [Apple] > macOS' 카테고리의 다른 글
[🍎 macOS] DistributedNotificationCenter (0) | 2023.08.30 |
---|---|
[🍎 macOS] CFMessagePort (0) | 2023.05.16 |
[🍎 macOS] macOS 디스플레이 정보 가져오는 방법 (How to get display information on macOS) (0) | 2023.03.27 |
[🍎 macOS] 번들 (Bundle) (0) | 2022.02.24 |
[🍎 macOS] AppKit과 UIKit 차이점 (What is the difference between AppKit and UIKit) (0) | 2022.02.23 |
댓글