[리눅스 운영체제] 리눅스 시스템 프로그래밍에서 흔히 사용되는 file io의 기능을 설명하기 위한 예제

Linux 시스템 프로그래밍에서 파일 IO 작업은 매우 일반적이고 중요한 작업 중 하나입니다. 파일 IO 작업을 통해 파일을 열고 읽고 쓰고 닫을 수 있으며 파일 찾기, 복사, 삭제 및 이름 바꾸기와 같은 작업을 수행할 수 있습니다. 이 블로그에서는 일반적으로 사용되는 몇 가지 파일 IO 작업 기능을 소개합니다.
여기에 이미지 설명 삽입

1. 열기()

1.1 프로토타입, 매개변수 및 반환 값 설명

1.1.1 프로토타입:

open() 함수는 Linux 시스템 프로그래밍에서 일반적으로 사용되는 파일 IO 작업 함수 중 하나입니다. 파일을 열고 후속 파일 읽기 및 쓰기 작업을 위해 파일 설명자를 반환하는 데 사용됩니다.
open() 함수의 프로토타입은 다음과 같습니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags, mode_t mode);

1.1.2 매개변수 설명:

  1. pathname: 열려는 파일의 경로 이름입니다.
  2. flags: 파일을 열기 위한 모드 플래그이며 다음 모드의 조합일 수 있습니다.
    • O_RDONLY: 파일을 읽기 전용 모드로 엽니다.
    • O_WRONLY: 파일을 쓰기 전용 모드로 엽니다.
    • O_RDWR: 파일을 읽기-쓰기 모드로 엽니다.
    • O_CREAT: 파일이 없으면 생성합니다.
    • O_EXCL: O_CREAT와 함께 사용되며 파일이 이미 존재하는 경우 오류를 반환합니다.
    • O_TRUNC: 파일이 존재하고 쓰기 가능 모드로 열린 경우 파일을 0으로 자릅니다.
    • O_APPEND: 파일 끝에 데이터를 추가합니다.
  3. mode: 파일 생성 시 접근 권한으로, O_CREAT 사용 시에만 유효합니다.
    다음 매크로를 사용하여 권한을 설정할 수 있습니다.
    • S_IRUSR: 사용자 읽기 권한.
    • S_IWUSR: 사용자 쓰기 권한.
    • S_IXUSR: 사용자 실행 권한.
    • S_IRGRP: 그룹 읽기 권한.
    • S_IWGRP: 그룹 쓰기 권한.
    • S_IXGRP: 그룹 실행 권한.
    • S_IROTH: 다른 사용자의 읽기 권한.
    • S_IWOTH: 다른 사용자의 쓰기 권한.
    • S_IXOTH: 다른 사용자의 권한을 실행합니다.

1.1.3 반환 값:

  • 성공 : 후속 파일 IO 작업에 대해 음수가 아닌 정수 파일 설명자를 반환합니다.
  • 실패 : -1을 반환하고 오류 유형을 나타내도록 errno 변수를 설정합니다.

1.2 기능 예

다음은 open() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main() {
    
    
    int fd;

    // 打开文件
    fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 写入数据
    char buffer[] = "Hello, world!";
    ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);
    if (ret == -1) {
    
    
        perror("write");
        close(fd);
        return errno;
    }

    // 关闭文件
    close(fd);

    return 0;
}

1.3 코드 설명

위의 예에서는 먼저 open() 함수를 사용하여 쓰기 전용 모드에서 example.txt 파일을 엽니다. 파일이 없으면 파일을 만들고 사용자가 읽고 쓸 수 있도록 액세스 권한을 설정합니다. 파일 열기에 실패하면 perror() 함수를 사용하여 오류 메시지를 인쇄하고 errno 변수를 반환합니다.

다음으로 write() 함수를 사용하여 파일에 데이터를 씁니다. 이 예에서는 "Hello, world!" 문자열을 작성했습니다. 데이터 쓰기에 실패하면 파일을 닫기 전에 perror() 함수를 사용하여 오류 메시지를 출력하고 errno 변수를 반환합니다.

마지막으로 close() 함수를 사용하여 파일을 닫습니다.

2. 닫기()

2.1 프로토타입, 매개변수 및 반환 값 설명

2.1.1 프로토타입:

close() 함수는 리눅스 시스템 프로그래밍에서 파일을 닫기 위해 사용하는 함수이다. 파일 설명자를 인수로 사용하고 작업이 성공했는지 여부를 나타내는 정수 값을 반환합니다.

close() 함수의 프로토타입은 다음과 같습니다.

#include <unistd.h>

int close(int fd);

2.1.2 매개변수:

  • fd: 닫을 파일 설명자입니다.

2.1.3 반환 값:

  • 성공 : 0을 반환합니다.
  • 실패 : -1을 반환하고errno오류 유형을 나타내는 변수를 설정합니다.

2.2 기능 예

다음은 close() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main() {
    
    
    int fd;

    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 关闭文件
    int ret = close(fd);
    if (ret == -1) {
    
    
        perror("close");
        return errno;
    }

    return 0;
}

2.3 코드 설명

위의 예에서 먼저 open()함수를 사용하여 파일을 읽기 전용 모드로 열고 example.txt반환된 파일 설명자를 변수에 저장합니다 fd. 파일 열기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

다음으로 함수를 사용하여 close()파일을 닫습니다. 파일 닫기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

파일을 닫은 후에는 파일 설명자에 대해 어떤 작업도 수행해서는 안 됩니다.

3. 읽기()

3.1 프로토타입, 매개변수 및 반환 값 설명

3.1.1 프로토타입:

read() 함수는 Linux 시스템 프로그래밍에서 파일에서 데이터를 읽는 데 사용되는 함수입니다. 파일 설명자, 버퍼 주소 및 읽을 최대 바이트 수를 매개 변수로 사용하고 실제 읽은 바이트 수를 반환합니다.

read() 함수의 프로토타입은 다음과 같습니다.

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

3.1.2 매개변수:

  • fd: 읽을 파일의 파일 설명자입니다.
  • buf: 읽은 데이터를 저장하기 위해 사용되는 버퍼의 주소.
  • count: 읽을 최대 바이트 수입니다.

3.1.3 반환 값:

  • 성공 : 실제로 읽은 바이트 수를 반환합니다.
  • 실패 :오류 유형을 나타내는 변수를 설정하여-1반환errno

3.2 기능 예시

다음은 read() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main() {
    
    
    int fd;

    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 读取数据
    char buffer[100];
    ssize_t ret = read(fd, buffer, sizeof(buffer));
    if (ret == -1) {
    
    
        perror("read");
        close(fd);
        return errno;
    }

    // 输出读取的数据
    printf("Read %ld bytes: %s\n", ret, buffer);

    // 关闭文件
    close(fd);

    return 0;
}

3.3 코드 설명

위의 예에서 먼저 open()함수를 사용하여 파일을 읽기 전용 모드로 열고 example.txt반환된 파일 설명자를 변수에 저장합니다 fd. 파일 열기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

다음으로 함수를 사용하여 read()파일에서 데이터를 읽습니다. 길이가 100인 버퍼를 정의하고 함수 buffer의 인수로 전달합니다 . 함수는 데이터 바이트를 읽고 버퍼에 저장하려고 시도합니다 . 데이터 읽기에 실패하면 함수를 사용하여 오류 메시지를 인쇄하고 파일을 닫기 전에 변수를 반환합니다 .read()read()countperror()errno

printf()마지막으로 읽은 데이터를 출력하는 함수와 close()파일을 닫는 함수를 사용합니다 .

read() 함수는 차단 함수이므로 파일에서 읽을 데이터가 충분하지 않으면 읽을 데이터가 충분하거나 오류가 발생할 때까지 대기합니다. 비차단 데이터를 읽어야 하는 경우 fcntl() 함수를 사용하여 파일 설명자를 비차단 모드로 설정할 수 있습니다.

4. 쓰기()

4.1 프로토타입, 매개변수 및 반환 값 설명

4.1.1 프로토타입:

write() 함수는 Linux 시스템 프로그래밍에서 파일에 데이터를 쓰는 데 사용되는 함수입니다. 파일 디스크립터, 데이터 버퍼 주소, 쓸 바이트 수를 매개변수로 받고 실제 쓴 바이트 수를 반환합니다.

write() 함수의 프로토타입은 다음과 같습니다.

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

4.1.2 매개변수:

  • fd: 쓸 파일의 파일 설명자입니다.
  • buf: 쓸 데이터의 버퍼 주소.
  • count: 쓸 바이트 수입니다.

4.1.3 반환 값:

  • 성공 : 실제로 쓴 바이트 수를 반환합니다.
  • 실패 :오류 유형을 나타내는 변수를 설정하여-1반환errno

4.2 기능 예

다음은 write() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main() {
    
    
    int fd;

    // 打开文件
    fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 写入数据
    char buffer[] = "Hello, World!";
    ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);
    if (ret == -1) {
    
    
        perror("write");
        close(fd);
        return errno;
    }

    // 关闭文件
    close(fd);

    return 0;
}

4.3 코드 설명

위의 예에서 먼저 open()함수를 사용하여 쓰기 전용 모드로 파일을 열고 example.txt반환된 파일 설명자를 변수에 저장합니다 fd. 파일 열기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

다음으로 함수를 사용하여 write()데이터를 파일에 씁니다. 문자열을 정의 하고 함수의 buffer인수로 전달합니다 . 함수는 파일에 데이터 바이트 쓰기를 시도합니다 . 데이터 쓰기에 실패하면 함수를 사용하여 오류 메시지를 인쇄하고 파일을 닫기 전에 변수를 반환합니다 .write()write()countperror()errno

마지막으로 함수를 사용하여 close()파일을 닫습니다.

write() 함수는 차단 함수이며, 파일이 기록된 데이터를 즉시 받아들일 수 없는 경우(예: 디스크 공간 부족) 데이터를 기록할 수 있거나 오류가 발생할 때까지 기다립니다. 비차단 데이터를 작성해야 하는 경우 fcntl() 함수를 사용하여 파일 설명자를 비차단 모드로 설정할 수 있습니다.

5. lseek()

5.1 프로토타입, 매개변수 및 반환 값 설명

5.1.1 프로토타입:

lseek() 함수는 Linux 시스템 프로그래밍에서 파일에서 읽기 및 쓰기 위치를 찾는 데 사용되는 함수입니다. 파일 설명자, 오프셋 및 시작 위치를 매개 변수로 사용하고 새로운 읽기 및 쓰기 위치를 반환합니다.

lseek() 함수의 프로토타입은 다음과 같습니다.

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

5.1.2 매개변수:

  • fd: 찾을 파일의 파일 설명자입니다.
  • offset: 오프셋, 양수, 음수 또는 0일 수 있습니다.
  • whence: 다음 세 가지 값을 사용할 수 있는 시작 위치입니다.
    • SEEK_SET: 파일 시작 부분에서 오프셋을 계산합니다.
    • SEEK_CUR: 현재 읽기/쓰기 위치에서 오프셋을 계산합니다.
    • SEEK_END: 파일 끝에서 오프셋을 계산합니다.

5.1.3 반환 값:

  • SUCCESS : 새로운 읽기 및 쓰기 위치를 반환합니다.
  • 실패 :오류 유형을 나타내는 변수를 설정하여-1반환errno

5.2 기능 예시

다음은 lseek() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main() {
    
    
    int fd;

    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 定位读写位置
    off_t ret = lseek(fd, 5, SEEK_SET);
    if (ret == -1) {
    
    
        perror("lseek");
        close(fd);
        return errno;
    }

    // 读取数据
    char buffer[10];
    ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
    if (n == -1) {
    
    
        perror("read");
        close(fd);
        return errno;
    }
    buffer[n] = '\0';
    printf("Data: %s\n", buffer);

    // 关闭文件
    close(fd);

    return 0;
}

5.3 코드 설명

위의 예에서 먼저 open()함수를 사용하여 파일을 읽기 전용 모드로 열고 example.txt반환된 파일 설명자를 변수에 저장합니다 fd. 파일 열기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

다음으로 함수를 사용하여 lseek()파일 시작 후 5번째 바이트에 읽기 및 쓰기 위치를 찾습니다. 파일 설명자, 오프셋 및 시작 위치를 매개 변수로 lseek()함수에 전달합니다. 읽기/쓰기 위치 찾기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno파일을 닫기 전에 변수를 반환합니다.

그런 다음 함수를 사용하여 read()파일에서 데이터를 읽습니다. 버퍼를 정의 하고 함수 buffer의 인수로 전달합니다 . 이 함수는 데이터 바이트를 버퍼로 읽으려고 시도합니다 . 데이터 읽기에 실패하면 함수를 사용하여 오류 메시지를 인쇄하고 파일을 닫기 전에 변수를 반환합니다 . 마지막으로 버퍼 끝에 null 문자를 추가하고 함수를 사용하여 읽은 데이터를 인쇄합니다.read()read()countperror()errnoprintf()

마지막으로 함수를 사용하여 close()파일을 닫습니다.

lseek() 함수를 사용하여 읽기 및 쓰기 위치를 찾을 수 있지만 파일 크기는 변경되지 않습니다. 파일 크기를 변경해야 하는 경우 truncate() 함수 또는 ftruncate() 함수를 사용할 수 있습니다.

6. 통계()

6.1 프로토타입, 매개변수 및 반환 값 설명

6.1.1 프로토타입:

stat() 함수는 리눅스 시스템 프로그래밍에서 파일 정보를 얻기 위해 사용하는 함수이다. 파일 경로를 인수로 사용하고 파일 정보를 포함하는 구조를 반환합니다.

stat() 함수의 프로토타입은 다음과 같습니다.

#include <sys/types.h>
#include <sys/stat.h>

int stat(const char *pathname, struct stat *buf);

6.1.2 매개변수:

  • pathname: 정보를 가져올 파일 경로입니다.
  • buf: 파일 정보를 저장하는 데 사용되는 구조에 대한 포인터입니다.

6.1.3 반환 값:

  • 성공 : 0을 반환합니다.
  • 실패 :오류 유형을 나타내는 변수를 설정하여-1반환errno

struct stat구조에는 파일 유형, 권한, 크기, 생성 시간, 수정 시간 등 파일에 대한 다양한 정보가 포함됩니다.

다음은 struct stat 구조의 정의입니다.

struct stat {
    
    
    dev_t     st_dev;         // 文件所在设备的ID
    ino_t     st_ino;         // 文件的inode号
    mode_t    st_mode;        // 文件的类型和权限
    nlink_t   st_nlink;       // 文件的硬链接数
    uid_t     st_uid;         // 文件的所有者ID
    gid_t     st_gid;         // 文件的所有者组ID
    dev_t     st_rdev;        // 如果文件是设备文件,则为设备的ID
    off_t     st_size;        // 文件的大小(字节)
    blksize_t st_blksize;     // 文件系统的块大小
    blkcnt_t  st_blocks;      // 分配给文件的块数
    time_t    st_atime;       // 文件的最后访问时间
    time_t    st_mtime;       // 文件的最后修改时间
    time_t    st_ctime;       // 文件的最后状态改变时间
};

6.2 기능 예시

다음은 stat() 함수를 사용하는 예입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>

int main() {
    
    
    const char *pathname = "example.txt";
    struct stat file_info;

    // 获取文件信息
    int ret = stat(pathname, &file_info);
    if (ret == -1) {
    
    
        perror("stat");
        return errno;
    }

    // 打印文件信息
    printf("File Size: %ld bytes\n", file_info.st_size);
    printf("File Permissions: %o\n", file_info.st_mode & 0777);
    printf("File Owner UID: %d\n", file_info.st_uid);
    printf("File Owner GID: %d\n", file_info.st_gid);

    return 0;
}

6.3 코드 설명

위의 예제에서는 먼저 획득한 파일 정보를 저장할 파일 경로 pathnamestruct stat구조를 정의합니다.file_info

그런 다음 함수를 사용하여 stat()파일 정보를 file_info구조에 저장합니다. file_info파일 경로와 구조 포인터를 stat()함수 의 매개변수로 전달 합니다 . 파일 정보 가져오기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

마지막으로 이 기능을 사용하여 파일 크기, 파일 권한 및 파일 소유자 합계 등을 printf()포함하여 얻은 파일 정보를 인쇄합니다.UIDGID

stat() 함수는 파일의 정보를 얻을 수만 있고 파일의 정보를 수정할 수는 없다는 점에 유의해야 합니다. 파일의 정보를 수정해야 하는 경우 chmod() 함수를 사용하여 파일의 권한을 수정할 수 있습니다.

7. fcntl()

7.1 프로토타입, 매개변수 및 반환 값 설명

7.1.1 프로토타입:

fcntl() 함수는 Linux 시스템 프로그래밍에서 파일 디스크립터를 제어하고 작동하는 데 사용되는 함수입니다. 파일 상태 플래그 설정, 파일 상태 플래그 가져오기, 파일 잠금 설정 등에 사용할 수 있습니다.

fcntl() 함수의 프로토타입은 다음과 같습니다.

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

7.1.2 매개변수:

  • fd: 열려 있는 파일의 파일 설명자 또는 소켓의 파일 설명자일 수 있는 파일 설명자입니다.
  • cmd: 수행할 작업을 지정하는 데 사용되는 제어 명령입니다.
  • arg: 특정 작업에 대한 매개변수를 전달하는 데 사용되는 선택적 매개변수입니다.

7.1.3 반환 값:

  • 성공 : 다른 작업 명령에 따라 다른 값을 반환합니다. 일반적으로 0 또는 양의 정수입니다.
  • 실패 :오류 유형을 나타내는 변수를 설정하여-1반환errno

다음은 fcntl() 함수에서 일반적으로 사용되는 몇 가지 명령입니다.

  • F_DUPFD: 파일 설명자를 복제하여 원본과 동일한 열린 파일을 가리키는 새 파일 설명자를 만듭니다.
  • F_GETFD: 파일 설명자에 대한 파일 상태 플래그를 가져옵니다.
  • F_SETFD: 파일 설명자에 대한 파일 상태 플래그를 설정합니다.
  • F_GETFL: 파일의 열기 모드 및 상태 플래그를 가져옵니다.
  • F_SETFL: 파일 열기 방식과 상태 플래그를 설정합니다.
  • F_GETLK: 파일 잠금 정보를 가져옵니다.
  • F_SETLK: 파일 잠금을 설정합니다.
  • F_SETLKW: 파일 잠금을 설정하고, 잠금을 획득할 수 없으면 차단하고 대기합니다.

7.2 기능 예시

다음은 fcntl() 함수를 사용하는 예입니다.

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main() {
    
    
    int fd = open("example.txt", O_RDWR);
    if (fd == -1) {
    
    
        perror("open");
        return errno;
    }

    // 获取文件的打开方式和状态标志
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
    
    
        perror("fcntl");
        return errno;
    }

    // 设置文件的状态标志为非阻塞
    flags |= O_NONBLOCK;
    int ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1) {
    
    
        perror("fcntl");
        return errno;
    }

    // 关闭文件描述符
    close(fd);

    return 0;
}

7.3 코드 설명

위의 예에서 먼저 open()함수를 사용하여 파일을 열고 반환된 파일 설명자를 변수 fd에 저장합니다. 파일 열기에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

그런 다음 fcntl()함수를 사용하여 파일의 열기 모드 및 상태 플래그를 가져옵니다. 파일 설명자와 F_GETFL명령을 매개 fcntl()변수로 함수에 전달하고 얻은 파일 상태 플래그를 변수에 저장합니다 flags. 파일 상태 플래그를 가져오는 데 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

다음으로 O_NONBLOCK플래그 비트 flags그런 다음 fcntl()함수를 사용하여 수정된 상태 플래그를 다시 파일 설명자로 설정합니다. 파일 설명자, F_SETFL명령 및 수정된 상태 플래그를 인수로 함수에 전달합니다 fcntl(). 파일 상태 플래그 설정에 실패하면 perror()함수를 사용하여 오류 메시지를 인쇄하고 errno변수를 반환합니다.

마지막으로 이 함수를 사용하여 close()파일 설명자를 닫고 리소스를 해제합니다.

fcntl() 함수의 사용은 매우 유연하며 필요에 따라 파일 설명자 복사, 파일 잠금 획득 등과 같은 다양한 작업을 수행할 수 있습니다. fcntl() 함수를 사용할 때 오류 처리에 주의하고 파일 디스크립터의 유효성을 확인해야 합니다. 동시에 파일 디스크립터는 리소스를 해제하고 리소스 누수를 방지하기 위해 더 이상 필요하지 않을 때 닫혀야 합니다.

요약하다

파일 IO 작업은 Linux 시스템 프로그래밍의 중요한 부분입니다. open(), close(), 등의 기능을 통해 파일을 열고 읽고 쓸 수 있습니다 read(). write()함수를 사용하면 lseek()파일 내에서 탐색할 수 있습니다. stat(), 등의 함수를 통해 파일의 상태 정보를 얻고, 파일 디스크립터에 대한 제어 작업을 수행하고, 파일 디스크립터를 복사하는 등의 작업을 수행할 수 있습니다 fcntl().dup()

위의 기능 외에도 mkdir()디렉토리 생성, rmdir()디렉토리 삭제, unlink()파일 삭제, rename()파일 이름 변경 등과 같은 파일 IO 작업에 사용할 수 있는 다른 많은 기능이 있습니다.

추천

출처blog.csdn.net/Goforyouqp/article/details/132211498