找回密码
 立即注册
首页 业界区 业界 Linux系统编程入门(下)

Linux系统编程入门(下)

卜笑 前天 17:44
Makefile


  • 文件命名及规则
    1.png

  1. app:sub.c add.c mult.c div.c main.c
  2.     gcc sub.c add.c mult.c div.c main.c -o app
复制代码

  • 检查更新
    2.png

  1. app:sub.o add.o mult.o div.o main.o
  2.     gcc sub.o add.o mult.o div.o main.o -o app
  3. sub.o:sub.c
  4.     gcc -c sub.c -o sub.o
  5. add.o:add.c
  6.     gcc -c add.c -o add.o
  7. mult.o:mult.c
  8.     gcc -c mult.c -o mult.o
  9. div.o:div.c
  10.     gcc -c div.c -o div.o
  11.    
  12. main.o:main.c
  13.     gcc -c main.c -o main.o
复制代码
3.png


  • 变量
  1. #定义变量
  2. src=sub.o add.o mult.o div.o main.o
  3. target=app
  4. $(target):$(src)
  5.     $(CC) $(src) -o $(target)
  6.    
  7. sub.o:sub.c
  8.     gcc -c sub.c -o sub.o
  9. add.o:add.c
  10.     gcc -c add.c -o add.o
  11. mult.o:mult.c
  12.     gcc -c mult.c -o mult.o
  13. div.o:div.c
  14.     gcc -c div.c -o div.o
  15.    
  16. main.o:main.c
  17.     gcc -c main.c -o main.o
复制代码

  • 模式匹配
    4.png

  1. #定义变量
  2. src=sub.o add.o mult.o div.o main.o
  3. target=app
  4. $(target):$(src)
  5. $(CC) $(src) -o $(target)
  6. %.o:%.c
  7. $(CC) -c $< -o $@
复制代码

  • wildcard函数
    5.png

    获取目标目录下的.c文件
  1. src=$(wildcard ./*.c)
复制代码

  • patsubst函数
    6.png

在目标文本text里找到符合模式pattern的文本,如果匹配的话使用replacement来替换,返回被替换后的字符串
  1. #定义变量
  2. src=$(wildcard ./*.c)
  3. #src=sub.o add.o mult.o div.o main.o
  4. #在src这个文本字符串里将.c后缀,修改为.o后缀
  5. objs=$(patsubst %.c, %.o, $(src))
  6. target=app
  7. $(target):$(objs)
  8.     $(CC) $(objs) -o $(target)
  9. %.o:%.c
  10.     $(CC) -c $< -o $@
  11.   
复制代码

  • clean
    规则定义:在Makefile中添加clean规则用于删除编译生成的中间文件(.o文件),其基本语法为:
    参数说明:-f参数表示强制删除,避免因文件不存在而报错
    clean文件冲突问题: 当目录中存在名为clean的文件时,执行make clean会提示"clean已是最新"而不执行删除操作,
    解决方法是在Makefile中添加.PHONY: clean声明clean为伪目标,伪目标不会生成实际文件,因此不会与同名文件冲突。
  1. src=$(wildcard ./*.c)
  2. objs=$(patsubst %.c, %.o, $(src))
  3. target=app
  4. $(target):$(objs)
  5.     $(CC) $(objs) -o $(target)
  6. %.o:%.c
  7.     $(CC) -c $< -o $@
  8. #定义clean是一个尾目标。表示clean不需要生成文件
  9. .PHONY:clean
  10. clean:
  11.     rm $(objs) -f
复制代码
GDB

7.png

8.png

9.png

Linux系统IO函数

IO函数

10.png

open


  • 打开文件
  1. /*
  2.     #include <sys/types.h>
  3.     #include <sys/stat.h>
  4.     #include <fcntl.h>
  5.     //打开一个已经存在的文件
  6.     int open(const char *pathname, int flags);
  7.         参数:
  8.             - pathname:要打开文件的路径
  9.             - flags:对文件的操作权限设置还有其他的设置
  10.             O_RDONLY, O_WRONLY, or O_RDWR 这三个只能选一个
  11.         返回值:返回一个新的文件描述符,如果调用失败,返回-1
  12.     errno: 属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
  13.     #include <stdio.h>
  14.     void perror(const char *s);
  15.         s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误信息)
  16.     作用:打印errno对应的错误描述
  17.     //创建一个新的文件
  18.     int open(const char *pathname, int flags, mode_t mode);
  19. */
复制代码

  • 示例
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. int main() {
  7.     int fd = open("a.txt", O_RDONLY);
  8.     if(fd == -1) {
  9.         perror("open");
  10.     }
  11.    
  12.     //关闭
  13.     close(fd);
  14.     return 0;
  15. }
复制代码

  • 创建新文件
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. int open(const char *pathname, int flags, mode_t mode);
  5.     参数:
  6.         - pathname: 要创建的文件的路径
  7.         - flags: 对文件操作的权限和其他的设置
  8.             - 必选项: O_RDONLY, O_WRONLY, or O_RDWR 互斥的
  9.             - 可选项: O_CREAT 文件不存在,创建新文件
  10.             - mode: 八进制的数,表示创建出的新的文件的操作权限,比如: 0775 (0代表八进制,7代表用户权限,7代表用户所在组的权限,5代表其他组的用户权限)
  11.             最终的权限是: mode & ~umask
  12.                 umask的作用是抹去某些权限
  13.                 umask 与 当前用户有关
  14.                 ~umask = 0777-umask = 0777-0002 = 0775
  15.                 最终的权限是: 0777 & 0775 = 0775
复制代码

  • 示例
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. int main() {
  7.     //创建一个新的文件
  8.     int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
  9.     if(fd == -1) {
  10.         perror("open");
  11.     }
  12.     //关闭
  13.     close(fd);
  14.     return 0;
  15. }
复制代码
close
  1. #include <unistd.h>
  2. int close(int fd);
复制代码
read与write

man 2 read
man 2 write


  • read
  1. #include <unistd.h>
  2. ssize_t read(int fd, void *buf, size_t count);
  3.         参数:
  4.             - fd: 文件描述符, open得到的,通过文件描述符来操作某个文件
  5.             - buf: 需要读取数据存放的地方,数组的地址(传出参数)
  6.             - count: 指定的数组的大小
  7.         返回值:
  8.             - 成功:
  9.                 >0 : 返回实际的读取到的字节数
  10.                 =0 : 文件已经读取完了
  11.             - 失败:
  12.                 -1 : 读取失败并设置errorno
复制代码

  • write
  1. #include <unistd.h>
  2. ssize_t write(int fd, const void *buf, size_t count);
  3.         参数:
  4.             - fd: 文件描述符, open得到的,通过文件描述符来操作某个文件
  5.             - buf: 要往磁盘写入的数据
  6.             - count: 要写的的数据的实际大小
  7.         返回值:
  8.             成功:实际写入的字节数
  9.             失败: -1, 写入失败并设置errorno
复制代码

  • 实现拷贝文件
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. int main() {
  7.     // 1.通过open, 只读O_RDONLY打开english.txt文件
  8.     int srcfd = open("english.txt", O_RDONLY);
  9.     if(srcfd == -1) {
  10.         perror("open");
  11.     }
  12.     // 2.创建一个新的文件(拷贝文件)
  13.     int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
  14.     if(destfd == -1) {
  15.         perror("open");
  16.     }
  17.     // 3.频繁的读写操作
  18.     char buf[1024] = {0};
  19.     // int len = read(srcfd, buf, sizeof(buf));
  20.     // printf("%s", buf);
  21.     int len = 0;
  22.     while((len = read(srcfd, buf, sizeof(buf))) > 0) {
  23.         write(destfd, buf, len);
  24.     }
  25.     // 4.关闭文件
  26.     close(srcfd);
  27.     close(destfd);
  28.     return 0;
  29. }
复制代码
lseek
  1. 标准C库的函数 man 3 fseek
  2. #include <stdio.h>
  3. int fseek(FILE *stream, long offset, int whence);
  4. Linux系统函数 man 2 lseek
  5. #include <sys/types.h>
  6. #include <unistd.h>
  7. off_t lseek(int fd, off_t offset, int whence);
  8.     参数:
  9.         - fd: 文件描述符,通过open得到的,通过这个fd操作某个文件
  10.         - offset: 偏移量
  11.         - whence:
  12.             SEEK_SET
  13.                 设置文件指针的偏移量
  14.             SEEK_CUR
  15.                 设置偏移量:当前位置 + 第二个参数offset的值
  16.             SEEK_END
  17.                 设置偏移量:文件大小 + 第二个参数offset的值
  18.     返回值:返回文件指针的位置
复制代码

  • 作用
    1.移动文件指针到头文件
    lseek(fd, 0, SEEK_SET)
    2.用来获取当前文件指针
    lseek(fd, 0, SEEK_CUR)
    3.获取文件长度
    lseek(fd, 0, SEEK_END)
    4.拓展文件的长度: eg.当前文件10b, 110b, 增加了100个字节
    lseek(fd, 100, SEEK_END)
    注意要写入一次数据才生效
  • 实现扩展文件大小
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. int main() {
  7.    int fd = open("hello.txt", O_RDWR);
  8.    if(fd == -1) {
  9.        perror("open");
  10.    }
  11.    
  12.    //扩展文件长度
  13.    int ret = lseek(fd, 100, SEEK_END);
  14.    if(ret == -1) {
  15.        perror("lseek");
  16.    }
  17.    //11+100+1=112
  18.    write(fd, " ", 1);
  19.    //关闭文件
  20.    close(fd);
  21.    return 0;
  22. }
复制代码
stat与lstat

man 2 stat


  • stat作用
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <unistd.h>
  4. int stat(const char *pathname, struct stat *statbuf);
  5.     作用: 获取一个文件相关的信息
  6.     参数:
  7.         - pathname: 文件路径
  8.         - statbuf: 结构体变量,传出参数,用于保存获取到的文件信息
  9.     返回值:
  10.         成功:返回0
  11.         失败:返回-1 设置errno
  12. int lstat(const char *pathname, struct stat *statbuf);
  13.     作用: 获取一个文件相关的信息
  14.     参数:
  15.         - pathname: 文件路径
  16.         - statbuf: 结构体变量,传出参数,用于保存获取到的文件信息
  17.     返回值:
  18.         成功:返回0
  19.         失败:返回-1 设置errno
复制代码

  • st_mode变量
    11.png

  • stat示例
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. int main() {
  6.     struct stat statbuf;
  7.     int ret = stat("a.txt", &statbuf);
  8.     if(ret == -1) {
  9.         perror("stat");
  10.     }
  11.     printf("size: %ld\n", statbuf.st_size);
  12.     return 0;
  13. }
复制代码

  • 软链接:
    ln -s a.txt b.txt
    12.png

  • stat与lstat的区别
  1. 当文件是一个符号链接时,
  2. lstat返回的是该符号链接本身的信息;
  3. 而stat返回的是该链接指向的文件的信息。
复制代码
实现ls-l命令
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <unistd.h>
  5. #include <pwd.h>
  6. #include <grp.h>
  7. #include <time.h>
  8. #include <string.h>
  9. //模拟实现 ls -l命令
  10. //-rw-rw-r-- 1 chan chan    11  9月  6 17:51 a.txt
  11. int main(int argc, char *argv[]) {
  12.     //判断输入的参数是否正确
  13.     if(argc < 2) {
  14.         printf("%s filename\n", argv[0]);
  15.         return 0;
  16.     }
  17.     //通过用户传入的文件名,调用stat获取文件信息
  18.     struct stat statbuf;
  19.     int ret = stat(argv[1], &statbuf);
  20.     if(ret == -1) {
  21.         perror("stat");
  22.         return 0;
  23.     }
  24.     //1. 获取文件类型与文件权限
  25.     char perms[11] = {0}; //用于保存文件类型与文件权限
  26.     switch (statbuf.st_mode & S_IFMT) {
  27.         case S_IFLNK:
  28.             perms[0] = 'l';
  29.             break;
  30.         case S_IFREG:
  31.             perms[0] = '-';
  32.             break;
  33.         case S_IFBLK:
  34.             perms[0] = 'b';
  35.             break;
  36.         case S_IFDIR:
  37.             perms[0] = 'd';
  38.             break;
  39.         case S_IFCHR:
  40.             perms[0] = 'c';
  41.             break;
  42.         case S_IFIFO:
  43.             perms[0] = 'p';
  44.             break;
  45.         case S_IFSOCK:
  46.             perms[0] = 's';
  47.             break;
  48.         default:
  49.             perms[0] = '?';
  50.             break;
  51.     }
  52.    
  53.     // 判断文件的访问权限
  54.     perms[1] = (statbuf.st_mode & S_IRUSR) ? 'r':'-';
  55.     perms[2] = (statbuf.st_mode & S_IWUSR) ? 'w':'-';
  56.     perms[3] = (statbuf.st_mode & S_IXUSR) ? 'x':'-';
  57.     perms[4] = (statbuf.st_mode & S_IRGRP) ? 'r':'-';
  58.     perms[5] = (statbuf.st_mode & S_IWGRP) ? 'w':'-';
  59.     perms[6] = (statbuf.st_mode & S_IXGRP) ? 'x':'-';
  60.     perms[7] = (statbuf.st_mode & S_IROTH) ? 'r':'-';
  61.     perms[8] = (statbuf.st_mode & S_IWOTH) ? 'w':'-';
  62.     perms[9] = (statbuf.st_mode & S_IXOTH) ? 'x':'-';
  63.     // 2.硬连接数
  64.     int linknum = statbuf.st_nlink;
  65.     // 3.文件所有者: 可以通过statbuf.st_uid得到uid, 然后通过getpwuid来获取用户名; man 3 getpwuid可查看
  66.     char * fileUser = getpwuid(statbuf.st_uid)->pw_name;
  67.     // 4.文件所在组: 可以通过statbuf.st_gid得到gid, 然后通过getgrgid来获取用户名; man 3 getpwgid可查看
  68.     char * fileGrp = getgrgid(statbuf.st_gid)->gr_name;
  69.     // 5.获取大小
  70.     long int fileSize = statbuf.st_size;
  71.     // 6.获取修改时间  statbuf.st_mtime:从1980年到修改时间的一个秒数,使用ctime转换,以换行结尾
  72.     char * time = ctime(&statbuf.st_mtime);
  73.     char mtime[512] = {0};
  74.     strncpy(mtime, time, strlen(time)-1);
  75.     char buf[1024];
  76.     sprintf(buf, "%s %d %s %s %ld %s %s", perms, linknum, fileUser, fileGrp, fileSize, mtime, argv[1]);
  77.     printf("%s\n", buf);
  78.     return 0;
  79. }
复制代码
文件操作函数

文件属性操作函数

access

man 2 access
  1. #include <unistd.h>
  2. int access(const char *pathname, int mode);
  3.     作用:判断某个文件是否有某个权限(R_OK,W_OK,X_OK),或者判断文件是否存在(F_OK)
  4.     参数:
  5.         - pathname: 判断的文件路径
  6.         - mode:
  7.             R_OK,W_OK,X_OK,F_OK 读,写,执行,文件存在
  8.     返回值:成功返回0,失败返回1
复制代码

  • 示例代码
  1. #include <unistd.h>
  2. #include <stdio.h>
  3. int main() {
  4.     int ret = access("a.txt", F_OK);
  5.     if(ret == -1) {
  6.         perror("access");
  7.         return -1;
  8.     }
  9.     printf("文件存在!!!\n");
  10.     return 0;
  11. }
复制代码
chmod
  1. #include <sys/stat.h>
  2. int chmod(const char *pathname, mode_t mode);
  3.     作用:修改文件的权限
  4.     参数:
  5.         - pathname: 修改的文件的路径
  6.         - mode: 需要修改的权限值,八进制数
  7.     返回值:
  8.         On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
  9.           
复制代码

  • 示例代码
  1. #include <sys/stat.h>
  2. #include <stdio.h>
  3. int main() {
  4.     int ret = chmod("a.txt", 0775);
  5.     if(ret == -1) {
  6.         perror("chmod");
  7.         return -1;
  8.     }
  9.     printf("修改成功!\n");
  10.     return 0;
  11. }
复制代码
chown

  1. 改变用户所属组
  2. sudo useradd 用户名 添加用户
  3. /vim/passwd 可查看所有用户
  4. /vim/group 可查看每个用户的组情况
  5. id 用户名 查看用户所属组信息
复制代码
  1. #include <unistd.h>
  2. int chown(const char *pathname, uid_t owner, gid_t group);
  3. 作用:让目标用户,uid 归属于gid下
复制代码
truncate
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. int truncate(const char *path, off_t length);
  4.     作用:缩减或者扩展文件的尺寸至指定的大小
  5.     参数:
  6.         - path: 需要修改的文件的路径
  7.         - length: 最终文件需要变成的大小
  8.     返回值:
  9.         On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
复制代码

  • 示例代码
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <stdio.h>
  4. int main() {
  5.     int ret = truncate("b.txt", 5);
  6.     if(ret == -1) {
  7.         perror("truncate");
  8.         return -1;
  9.     }
  10.     printf("修改文件大小成功\n");
  11.     return 0;
  12. }
复制代码
目录操作函数

mkdir

创建文件夹
  1. #include <sys/stat.h>
  2. #include <sys/types.h>
  3. int mkdir(const char *pathname, mode_t mode);
  4.     作用:创建一个目录
  5.     参数:
  6.         pathname: 创建的目录的路径
  7.         mode: 权限,八进制的数
  8.     返回值:
  9.         return zero on success, or -1 if an error occurred (in which case, errno is set appropriately).
复制代码

  • 示例代码
  1. #include <sys/stat.h>
  2. #include <sys/types.h>
  3. #include <stdio.h>
  4. int main() {
  5.     int ret = mkdir("a", 0777);
  6.     if(ret == -1) {
  7.         perror("mkdir");
  8.         return -1;
  9.     }
  10.     printf("创建文件夹成功\n");
  11.     return 0;
  12. }
复制代码
rmdir

删除目录
rename

修改名字
  1. #include <stdio.h>
  2. int rename(const char *oldpath, const char *newpath);
  3. 返回值
  4. On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
复制代码

  • 示例代码
  1. #include <stdio.h>
  2. int main() {
  3.     int ret = rename("a","b");
  4.     if(ret == -1) {
  5.         perror("rename");
  6.         return -1;
  7.     }
  8.     printf("修改成功\n");
  9.     return 0;
  10. }
复制代码
chdir与getcwd
  1. #include <unistd.h>
  2. int chdir(const char *path);
  3.     作用:修改进程的工作目录
  4.         比如在/home/chan 启动了一个可执行的程序a.out,进程的工作目录 /home/chan
  5.     参数:
  6.         path: 需要修改的工作目录
  7.     返回值:
  8.         On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
  9. #include <unistd.h>
  10. char *getcwd(char *buf, size_t size);
  11.     作用:获取当前工作目录
  12.     参数:
  13.         - buf: 存储的路径,指向一个数组(传出参数)
  14.         - size: 数组大小
  15.     返回值:
  16.         返回指向的一块内存,这个数据就是第一个参数
复制代码

  • 示例代码
  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. int main() {
  7.     char buf[128];
  8.     getcwd(buf, sizeof(buf));
  9.     printf("当前的工作目录是:%s\n", buf);
  10.     //修改工作目录
  11.     int ret = chdir("b");
  12.     if(ret == -1) {
  13.         perror("chdir");
  14.         return -1;
  15.     }
  16.     printf("修改后的工作目录是:b\n");
  17.     //创建一个新文件
  18.     int fd = open("chdir.txt", O_WRONLY|O_CREAT, 0664);
  19.     if(fd == -1) {
  20.         perror("open");
  21.         return -1;
  22.     }
  23.     close(fd);
  24.     //获取当前的工作路径
  25.     char buff[128];
  26.     getcwd(buff, sizeof(buff));
  27.     printf("当前的工作目录是:%s\n", buff);
  28.     return 0;
  29. }
复制代码
目录遍历函数

opendir

man 3 opendir
  1. // 打开一个目录
  2. #include <sys/types.h>
  3. #include <dirent.h>
  4. DIR *opendir(const char *name);
  5.     参数:
  6.         - name: 打开的目录名
  7.     返回:
  8.         DIR * 类型, 一个目录流
  9.         失败返回NULL
复制代码
readdir

man 3 readdir
  1. // 读取目录的数据
  2. #include <dirent.h>
  3. struct dirent *readdir(DIR *dirp);
  4.     - 参数: dirp是opendir返回的目录流
  5.     - 返回:
  6.         dirent结构体, 代表读取到的文件信息
  7.         读取到了末尾或者失败了返回NULL
  8.         struct dirent {
  9.             ino_t          d_ino;      
  10.             off_t          d_off;      
  11.             unsigned short d_reclen;   
  12.             unsigned char  d_type;      
  13.             char           d_name[256];
  14.         };
复制代码
closedir

man 3 closedir
  1. // 关闭目录
  2. #include <sys/types.h>
  3. #include <dirent.h>
  4. int closedir(DIR *dirp);
复制代码
递归获取目录下的普通文件个数
  1. #include <sys/types.h>
  2. #include <dirent.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. //读取某个目录下的所有普通文件的个数
  7. int FileNum(const char *path);
  8. int main(int argc, char *argv[]) {
  9.     if(argc < 2) {
  10.         printf("%s path\n", argv[0]);
  11.         return -1;
  12.     }
  13.     printf("普通文件个数为%d\n", FileNum(argv[1]));
  14.     return 0;
  15. }
  16. // 用于获取目录下所有所有普通文件的个数
  17. int FileNum(const char *path) {
  18.     int cnt = 0;
  19.     // 1.打开目录
  20.     DIR *dir = opendir(path);
  21.     if(dir == NULL) {
  22.         perror(NULL);
  23.         exit(0);
  24.     }
  25.     // 2.循环读取dir
  26.     struct dirent *ptr;
  27.     while((ptr=readdir(dir)) != NULL) {
  28.         // 获取名称 (目的是过滤掉 ./ 和 ../)
  29.         char *dname = ptr->d_name;
  30.         if(strcmp(dname,".") == 0||strcmp(dname,"..") == 0) {
  31.             continue;
  32.         }
  33.         // 判断是普通文件还是目录
  34.         // 如果是目录
  35.         if(ptr->d_type == DT_DIR) {
  36.             // 递归读取这个目录, 需要在path后加上/dname
  37.             char newpath[256];
  38.             sprintf(newpath, "%s/%s", path, dname);
  39.             cnt += FileNum(newpath);
  40.         }
  41.         // 如果是普通文件
  42.         if(ptr->d_type == DT_REG) {
  43.             cnt ++;
  44.         }
  45.     }
  46.     closedir(dir);
  47.     return cnt;
  48. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册