Linux 파일 I/O: 원칙 및 방법
파일은 Linux 시스템에서 가장 기본적이고 일반적으로 사용되는 데이터 저장 방법으로 텍스트 파일, 바이너리 파일, 장치 파일, 디렉터리 파일 등이 될 수 있습니다. 파일을 읽고 쓰는 것은 Linux 프로그래밍에서 가장 중요한 작업 중 하나입니다. 여기에는 파일 설명자, 버퍼, 시스템 호출 및 라이브러리 기능과 같은 개념이 포함됩니다. 이 기사에서는 열기, 닫기, 읽기, 쓰기, 위치 지정, 자르기, 동기화 및 기타 작업을 포함하여 Linux 파일 I/O의 기본 원리와 방법을 소개하고 예제를 통해 사용법과 주의 사항을 설명합니다.
파일 설명자
후속 시스템 호출(read(2), write(2), lseek(2), fcntl(2) 등)에 사용되는 음수가 아닌 작은 정수입니다($man 2 open). 일반적으로 3개의 열린 파일 설명자가 있습니다:
- 0: STDIN_FIFLENO, 표준 입력 stdin
- 1: STDOUT_FILENO, 표준 출력 stdout
- 2: STDERR_FILENO, 표준 오류 stderror
fd 원칙
- fd는 0부터 시작하여 사용되지 않은 가장 작은 설명자를 찾고 파일 테이블 포인터와 파일 테이블 설명자 사이에 해당 관계를 설정합니다(VS pid는 계속 상승하고 가득 차면 다시 돌아옵니다)
- 파일 디스크립터는 열린 파일을 나타내는 데 사용되는 int이지만 파일의 관리 정보는 파일 디스크립터에 저장할 수 없습니다. open() 함수를 사용하여 파일을 열 때 OS는 해당 파일의 관련 정보를 로드합니다. file into 그러나 보안 및 효율성 등의 요인으로 인해 파일 테이블과 같은 데이터 구조는 직접 작업에 적합하지 않습니다. 대신 구조에 번호를 할당하고 이 번호를 파일 디스크립터로 사용합니다.
- OS는 각 프로세스에 대해 내부적으로 파일 설명자 마스터 테이블을 유지 관리합니다. 새 파일 설명자가 필요한 경우 마스터 테이블에서 사용되지 않은 가장 작은 설명자를 검색하여 반환합니다. 그러나 실제로는 음수가 아닌 정수, 즉 0~OPEN_MAX(현재 시스템에서는 1024)이며, 그 중 0, 1, 2가 시스템에 의해 점유되어 각각 stdin, stdout 및 stderror를 나타냅니다
- close()를 사용하여 fd를 닫을 때 fd와 파일 테이블 구조 간의 대응은 전체 테이블에서 제거되지만 파일 테이블 구조는 반드시 삭제되지는 않습니다. 파일 테이블이 다른 fd와 일치하지 않는 경우에만 해당됩니다. (즉, 파일 테이블은 동시에 여러 fd에 대응할 수 있습니다.) 파일 테이블이 삭제되기 전에 close()는 파일 설명자 자체의 정수 값을 변경하지 않으며 파일 설명자가 파일을 나타낼 수 없게 만들 뿐입니다. 파일
- 중복 fdVS 복사본 fd:dup은 int new_fd=old_fd 대신 old_fd에 해당하는 파일 테이블 포인터를 new_fd에 복사합니다.
-
UNIX는 세 가지 데이터 구조를 사용하여 열린 파일을 설명합니다. 즉, 각 프로세스에서 현재 프로세스가 연 파일을 설명하는 데 사용되는 파일 설명자 테이블, 현재 파일 상태를 나타내는 파일 상태 식별 테이블 및 파일 i-노드입니다. 파일(인덱스 노드)의 V 노드 테이블
을 찾는 데 사용됩니다. 이 Vnode 구조는 Linux에서 사용되지 않고 일반적인 inode 구조이지만 본질적인 차이점은 없습니다. 파일을 읽을 때 파일 시스템

파일 설명자 플래그
close-on-exec
현재 시스템에는 하나의 파일 설명자 플래그
- 일반적으로 다른 프로그램을 실행하기 위해 exec를 호출하게 되는데 이때 자식 프로세스의 텍스트, 데이터, 힙, 스택은 새로운 프로그램으로 대체됩니다. 물론 이때는 파일 디스크립터를 담고 있는 변수가 더 이상 존재하지 않으며, 쓸모없는 파일 디스크립터를 닫을 수도 없습니다. 따라서 일반적으로 우리는 하위 프로세스를 포크하고 하위 프로세스에서 직접 닫기를 실행하여 쓸모 없는 파일 설명자를 끄고 exec를 실행합니다. 그러나 복잡한 시스템에서는 하위 프로세스를 포크할 때 얼마나 많은 파일 설명자(소켓 핸들 등 포함)가 열려 있는지 더 이상 알 수 없습니다. 이때 하나씩 정리하는 것은 정말 어렵습니다. 우리가 기대하는 것은 자식 프로세스를 포크하기 전에 파일 핸들을 열 때 이를 지정하는 것입니다. 자식 프로세스를 포크한 후 exec를 실행할 때 이 핸들을 닫을 것입니다." 그래서 close-on-exec 이 있습니다.
- 각 파일 설명자에는 close-on-exec 플래그가 있습니다. 시스템 기본적으로 이 플래그의 마지막 비트는 0으로 설정됩니다. 이 플래그는 꺼져 있습니다. 그런 다음 하위 프로세스가 exec 함수를 호출하면 하위 프로세스는 파일 설명자를 닫지 않습니다. 이때 상위 프로세스와 하위 프로세스는 동일한 파일 테이블 항목, 동일한 파일 오프셋 등을 가지고 파일을 공유합니다.
-
fcntl()
的FD_CLOEXEC
和open()
的O_CLOEXEC
用来设置文件的close-on-exec
close-on-exec 플래그가 1로 설정되면 이 플래그가 켜집니다. 이때 자식 프로세스가 exec 함수를 호출하기 전에 시스템은 이미 자식 프로세스에게 파일 설명자를 닫도록 요청했습니다.
참고: 새 버전에서는 열 때 CLOEXEC 설정을 지원하지만 컴파일 중에 오류가 계속 표시됩니다. 오류: 'O_CLOEXEC'가 선언되지 않았습니다(이 함수에서 처음 사용). 이 기능은 매크로(_GNU_SOURCE)를 설정하여 활성화해야 합니다.
으아악파일 상태 플래그
파일 상태 플래그는 열린 파일의 속성을 나타내는 데 사용됩니다. 파일 상태 플래그는 파일 설명자를 복제하여 동일한 열린 파일의 상태를 공유할 수 있지만 파일 설명자 플래그는 그럴 수 없습니다.
- 액세스 모드: 읽기 전용, 쓰기 전용, 읽기-쓰기 등 파일의 액세스 모드를 지정합니다. open()에 의해 설정되고 fcntl()에 의해 반환되지만 변경할 수 없습니다
- 오픈타임 플래그: open()이 실행될 때의 작업을 나타냅니다. 이 플래그는 open()이 실행된 후에 저장되지 않습니다. 작동 모드: open()을 통해 설정되는 읽기 및 쓰기 작업에 영향을 주지만 fcntl()을 사용하여 읽거나 변경할 수 있습니다
- open()
으아악 FQ
: Bitwise ORed 이유:FA: 다음 모델이 있다고 추측됩니다. 옵션을 나타내기 위해 한 비트가 1이고 나머지는 모두 0인 문자열 문자열을 사용하고 옵션은 다음과 같을 수 있습니다. 0/1 문자열을 얻기 위한 "비트 ANDed"는 전체 플래그의 상태를 나타냅니다. 참고: 아래쪽 세 자리는 액세스 모드를 나타냅니다
creat()
O_WRONLY |O_TRUNC|O_CREAT 플래그를 사용하여 open()을 호출하는 것과 동일합니다.
#include
int creat(const char *pathname, mode_t mode);
로그인 후 복사
dup()、dup2()、dup3()
//复制一个文件描述符的指向,新的文件描述符的flags和原来的一样,成功返回new_file_descriptor, 失败返
回-1并设errno
#include
int dup(int oldfd); //使用未被占用的最小的文件描述符编号作为新的文件描述符
int dup2(int oldfd, int newfd);
#include
#include
int dup3(int oldfd, int newfd, int flags);
#include
#include
int res=dup2(fd,fd2);
if(-1==res){
perror("dup2"),exit(-1);
로그인 후 복사

read()
//从fd对应的文件中读count个byte的数据到以buf开头的缓冲区中,成功返回成功读取到的byte的数目,失败返回-1设errno
#include
ssize_t read(int fd, void *buf, size_t count);
#include
#include
int res=read(fd,buf,6);
if(-1==fd)
perror("read"),exit(-1);
로그인 후 복사
write()
//从buf指向的缓冲区中读取count个byte的数据写入到fd对应的文件中,成功返回成功写入的byte数目,文件的位置指针会向前移动这个数目,失败返回-1设errno
#include
ssize_t write(int fd, const void *buf, size_t count);//不需要对buf操作, 所以有const, VS read()没有const
#include
#include
int res=write(fd,"hello",sizeof("hello"));
if(-1==res)
perror("write"),exit(-1);
로그인 후 복사
#include int creat(const char *pathname, mode_t mode);
//复制一个文件描述符的指向,新的文件描述符的flags和原来的一样,成功返回new_file_descriptor, 失败返 回-1并设errno #include int dup(int oldfd); //使用未被占用的最小的文件描述符编号作为新的文件描述符 int dup2(int oldfd, int newfd); #include #include int dup3(int oldfd, int newfd, int flags); #include #include int res=dup2(fd,fd2); if(-1==res){ perror("dup2"),exit(-1);
//从fd对应的文件中读count个byte的数据到以buf开头的缓冲区中,成功返回成功读取到的byte的数目,失败返回-1设errno #include ssize_t read(int fd, void *buf, size_t count); #include #include int res=read(fd,buf,6); if(-1==fd) perror("read"),exit(-1);
//从buf指向的缓冲区中读取count个byte的数据写入到fd对应的文件中,成功返回成功写入的byte数目,文件的位置指针会向前移动这个数目,失败返回-1设errno #include ssize_t write(int fd, const void *buf, size_t count);//不需要对buf操作, 所以有const, VS read()没有const #include #include int res=write(fd,"hello",sizeof("hello")); if(-1==res) perror("write"),exit(-1);
Note: 上例中即使只有一个字符’A’,也要写”A”,因为”A”才是地址,’A’只是个int
lseek()
l 表示long int, 历史原因
//根据移动基准whence和移动距离offset对文件的位置指针进行重新定位,返回移动后的位置指针与文件开头的距离,失败返回-1设errno #include #include off_t lseek(int fd, off_t offset, int whence); /*whence: SEEK_SET:以文件开头为基准进行偏移,0一般不能向前偏 SEEK_CUR:以当前位置指针的位置为基准进行偏移,1向前向后均可 SEEK_END:以文件的结尾为基准进行偏移,2向前向后均可向后形成”文件空洞” #include #include int len=lseek(fd,-3,SEEK_SET); if(-1==len){ perror("lseek"),exit(-1);
fcntl()
//对fd进行各种操作,成功返回0,失败返回-1设errno #include #include int fcntl(int fd, int cmd, ... ); //...表示可变长参数 /*cmd: Adversory record locking: F_SETLK(struct flock*) //设建议锁 F_SETLKW(struct flock*) //设建议锁,如果文件上有冲突的锁,且在等待的时候捕获了一个信号,则调用被打断并在信号捕获之后立即返回一个错误,如果等待期间没有信号,则一直等待 F_GETLK(struct flock*) //尝试放锁,如果能放锁,则不会放锁,而是返回一个含有F_UNLCK而其他不变的l_type类型,如果不能放锁,那么fcntl()会将新类型的锁加在文件上,并把当前PID留在锁上 Duplicating a file descriptor: F_DUPFD (int) //找到>=arg的最小的可以使用的文件描述符,并把这个文件描述符用作fd的一个副本 F_DUPFD_CLOEXEC(int)//和F_DUPFD一样,除了会在新的文件描述符上设置close-on-exec F_GETFD (void) //读取fd的flag,忽略arg的值 F_SETFD (int) //将fd的flags设置成arg的值. F_GETFL (void) //读取fd的Access Mode和其他的file status flags; 忽略arg F_SETFL (long) //设置file status flags为arg F_GETOWN(void) //返回fd上接受SIGIO和SIGURG的PID或进程组ID F_SETOWN(int) //设置fd上接受SIGIO和SIGURG的PID或进程组ID为arg F_GETOWN_EX(struct f_owner_ex*) //返回当前文件被之前的F_SETOWN_EX操作定义的文件描述符R F_SETOWN_EX(struct f_owner_ex*) //和F_SETOWN类似,允许调用程序将fd的I/O信号处理权限直接交给一个线程,进程或进程组 F_GETSIG(void) //当文件的输入输出可用时返回一个信号 F_SETSIG(int) //当文件的输入输出可用时发送arg指定的信号 */ /*…: 可选参素,是否需要得看cmd,如果是加锁,这里应是struct flock* struct flock { short l_type; //%d Type of lock: F_RDLCK(读锁), F_WRLCK(写锁), F_UNLCK(解锁) short l_whence; //%d How to interpret l_start, 加锁的位置参考标准:SEEK_SET, SEEK_CUR, SEEK_END off_t l_start; //%ld Starting offset for lock, 加锁的起始位置 off_t l_len; //%ld Number of bytes to lock , 锁定的字节数 pid_t l_pid; // PID of process blocking our lock, (F_GETLK only)加锁的进程号,,默认给-1 }; */
建议锁(Adversory Lock)
限制加锁,但不限制读写, 所以只对加锁成功才读写的程序有效,用来解决不同的进程 同时对同一个文件的同一个位置 “写”导致的冲突问题
读锁是一把共享锁(S锁):共享锁+共享锁+共享锁+共享锁+共享锁+共享锁
写锁是一把排他锁(X锁):永远孤苦伶仃
释放锁的方法(逐级提高):
- 将锁的类型改为:F_UNLCK, 再使用fcntl()函数重新设置
- close()关闭fd时, 调用进程在该fd上加的所有锁都会自动释放
- 进程结束时会自动释放所有该进程加过的文件锁
Q:为什么加了写锁还能gedit或vim写???
A:可以写, 锁只可以控制能否加锁成功, 不能控制对文件的读写, 所以叫”建议”锁, 我加了锁就是不想让你写, 你非要写我也没办法. vim/gedit不通过能否加锁成功来决定是否读写, 所以可以直接上
Q: So如何实现文件锁控制文件的读写操作????
A:可以在读操作前尝试加读锁, 写操作前尝试加写锁, 根据能否加锁成功决定能否进行读写操作
int fd=open("./a.txt",O_RDWR); //得到fd if(-1==fd) perror("open"),exit(-1); struct flock lock={F_RDLCK,SEEK_SET,2,5,-1}; //设置锁 //此处从第3个byte开始(包含第三)锁5byte int res=fcntl(fd,F_SETLK,&lock); //给fd加锁 if(-1==res) perror("fcntl"),exit(-1);
ioct1()
这个函数可以实现其他文件操作函数所没有的功能,大多数情况下都用在设备驱动程序里,每个设备驱动程序可以定义自己专用的一组ioctl命令,系统则为不同种类的设备提供通用的ioctl命令
//操作特殊文件的设备参数,成功返回0,失败返回-1设errno #include int ioctl(int d, int request, ...); //d:an open file descriptor. //request: a device-dependent request code
close()
//关闭fd,这样这个fd就可以重新用于连接其他文件,成功返回0,失败返回-1设errno #include int close(int fd); #include #include int res=close(fd); if(-1==res) perror("close"),exit(-1);
通过本文,我们了解了Linux文件I/O的基本原理和方法,它们可以满足我们对文件的各种操作需求。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如关闭不用的文件描述符,检查错误返回值,使用合适的缓冲区大小等。文件I/O是Linux程序设计中不可或缺的一部分,它可以实现数据的持久化和交换,也可以提升程序的功能和性能。希望本文能够对你有所帮助和启发。
위 내용은 Linux 파일 I/O: 원칙 및 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Centos와 Ubuntu의 주요 차이점은 다음과 같습니다. Origin (Centos는 Red Hat, Enterprise의 경우, Ubuntu는 Debian에서 시작하여 개인의 경우), 패키지 관리 (Centos는 안정성에 중점을 둡니다. Ubuntu는 APT를 사용하여 APT를 사용합니다), 지원주기 (Ubuntu는 5 년 동안 LTS 지원을 제공합니다), 커뮤니티에 중점을 둔다 (Centos Conciors on ubuntu). 튜토리얼 및 문서), 사용 (Centos는 서버에 편향되어 있으며 Ubuntu는 서버 및 데스크탑에 적합), 다른 차이점에는 설치 단순성 (Centos는 얇음)이 포함됩니다.

Centos는 중단되었으며 대안은 다음과 같습니다. 1. Rocky Linux (Best Compatibility); 2. Almalinux (Centos와 호환); 3. Ubuntu 서버 (구성 필수); 4. Red Hat Enterprise Linux (상업용 버전, 유료 라이센스); 5. Oracle Linux (Centos 및 Rhel과 호환). 마이그레이션시 고려 사항은 호환성, 가용성, 지원, 비용 및 커뮤니티 지원입니다.

CentOS 설치 단계 : ISO 이미지를 다운로드하고 부팅 가능한 미디어를 실행하십시오. 부팅하고 설치 소스를 선택하십시오. 언어 및 키보드 레이아웃을 선택하십시오. 네트워크 구성; 하드 디스크를 분할; 시스템 시계를 설정하십시오. 루트 사용자를 만듭니다. 소프트웨어 패키지를 선택하십시오. 설치를 시작하십시오. 설치가 완료된 후 하드 디스크에서 다시 시작하고 부팅하십시오.

Docker Desktop을 사용하는 방법? Docker Desktop은 로컬 머신에서 Docker 컨테이너를 실행하는 도구입니다. 사용 단계는 다음과 같습니다. 1. Docker Desktop 설치; 2. Docker Desktop을 시작하십시오. 3. Docker 이미지를 만듭니다 (Dockerfile 사용); 4. Docker Image 빌드 (Docker 빌드 사용); 5. 도커 컨테이너를 실행하십시오 (Docker Run 사용).

Docker는 Linux 커널 기능을 사용하여 효율적이고 고립 된 응용 프로그램 실행 환경을 제공합니다. 작동 원리는 다음과 같습니다. 1. 거울은 읽기 전용 템플릿으로 사용되며, 여기에는 응용 프로그램을 실행하는 데 필요한 모든 것을 포함합니다. 2. Union 파일 시스템 (Unionfs)은 여러 파일 시스템을 스택하고 차이점 만 저장하고 공간을 절약하고 속도를 높입니다. 3. 데몬은 거울과 컨테이너를 관리하고 클라이언트는 상호 작용을 위해 사용합니다. 4. 네임 스페이스 및 CGroup은 컨테이너 격리 및 자원 제한을 구현합니다. 5. 다중 네트워크 모드는 컨테이너 상호 연결을 지원합니다. 이러한 핵심 개념을 이해 함으로써만 Docker를 더 잘 활용할 수 있습니다.

도커 프로세스보기 방법 : 1. Docker CLI 명령 : Docker PS; 2. Systemd Cli 명령 : SystemCTL 상태 Docker; 3. Docker Compose CLI 명령 : Docker-Compose PS; 4. 프로세스 탐색기 (Windows); 5. /Proc Directory (Linux).

대 코드 시스템 요구 사항 : 운영 체제 : Windows 10 이상, MacOS 10.12 이상, Linux 배포 프로세서 : 최소 1.6GHz, 권장 2.0GHz 이상의 메모리 : 최소 512MB, 권장 4GB 이상의 저장 공간 : 최소 250MB, 권장 1GB 및 기타 요구 사항 : 안정 네트워크 연결, Xorg/Wayland (LINUX)

실패한 Docker 이미지 빌드에 대한 문제 해결 단계 : Dockerfile 구문 및 종속성 버전을 확인하십시오. 빌드 컨텍스트에 필요한 소스 코드 및 종속성이 포함되어 있는지 확인하십시오. 오류 세부 사항에 대한 빌드 로그를보십시오. -표적 옵션을 사용하여 계층 적 단계를 구축하여 실패 지점을 식별하십시오. 최신 버전의 Docker Engine을 사용하십시오. -t [image-name] : 디버그 모드로 이미지를 빌드하여 문제를 디버깅하십시오. 디스크 공간을 확인하고 충분한 지 확인하십시오. 빌드 프로세스에 대한 간섭을 방지하기 위해 Selinux를 비활성화하십시오. 커뮤니티 플랫폼에 도움을 요청하고 Dockerfiles를 제공하며보다 구체적인 제안을 위해 로그 설명을 구축하십시오.
