> 시스템 튜토리얼 > 리눅스 > Linux 파일 I/O: 원칙 및 방법

Linux 파일 I/O: 원칙 및 방법

PHPz
풀어 주다: 2024-02-09 18:27:27
앞으로
878명이 탐색했습니다.

파일은 Linux 시스템에서 가장 기본적이고 일반적으로 사용되는 데이터 저장 방법으로 텍스트 파일, 바이너리 파일, 장치 파일, 디렉터리 파일 등이 될 수 있습니다. 파일을 읽고 쓰는 것은 Linux 프로그래밍에서 가장 중요한 작업 중 하나입니다. 여기에는 파일 설명자, 버퍼, 시스템 호출 및 라이브러리 기능과 같은 개념이 포함됩니다. 이 기사에서는 열기, 닫기, 읽기, 쓰기, 위치 지정, 자르기, 동기화 및 기타 작업을 포함하여 Linux 파일 I/O의 기본 원리와 방법을 소개하고 예제를 통해 사용법과 주의 사항을 설명합니다.

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 구조이지만 본질적인 차이점은 없습니다. 파일을 읽을 때 파일 시스템 Linux 파일 I/O: 원칙 및 방법
Linux 파일 I/O: 원칙 및 방법

파일 설명자 플래그

close-on-exec현재 시스템에는 하나의 파일 설명자 플래그

만 있는데, 이는 프로세스가 하위 프로세스를 포크할 때 하위 프로세스에서 exec 함수가 호출될 때 사용됩니다. 의미는 exec를 실행하기 전에 이 파일 설명자를 닫을지 여부입니다. 🎜
  • 일반적으로 다른 프로그램을 실행하기 위해 exec를 호출하게 되는데 이때 자식 프로세스의 텍스트, 데이터, 힙, 스택은 새로운 프로그램으로 대체됩니다. 물론 이때는 파일 디스크립터를 담고 있는 변수가 더 이상 존재하지 않으며, 쓸모없는 파일 디스크립터를 닫을 수도 없습니다. 따라서 일반적으로 우리는 하위 프로세스를 포크하고 하위 프로세스에서 직접 닫기를 실행하여 쓸모 없는 파일 설명자를 끄고 exec를 실행합니다. 그러나 복잡한 시스템에서는 하위 프로세스를 포크할 때 얼마나 많은 파일 설명자(소켓 핸들 등 포함)가 열려 있는지 더 이상 알 수 없습니다. 이때 하나씩 정리하는 것은 정말 어렵습니다. 우리가 기대하는 것은 자식 프로세스를 포크하기 전에 파일 핸들을 열 때 이를 지정하는 것입니다. 자식 프로세스를 포크한 후 exec를 실행할 때 이 핸들을 닫을 것입니다." 그래서 close-on-exec
  • 이 있습니다.
  • 각 파일 설명자에는 close-on-exec 플래그가 있습니다. 시스템 기본적으로 이 플래그의 마지막 비트는 0으로 설정됩니다. 이 플래그는 꺼져 있습니다. 그런 다음 하위 프로세스가 exec 함수를 호출하면 하위 프로세스는 파일 설명자를 닫지 않습니다. 이때 상위 프로세스와 하위 프로세스는 동일한 파일 테이블 항목, 동일한 파일 오프셋 등을 가지고 파일을 공유합니다.
  • fcntl()FD_CLOEXECopen()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);
로그인 후 복사
Linux 파일 I/O: 원칙 및 방법

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);
로그인 후 복사

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:lxlinux.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿