Linux高并发服务器开发之第一章 Linux系统编程入门

第一章 Linux系统编程入门

1.1Linux开发环境搭建

环境:Ubuntu20.04

编译软件:VScode

编译:GCC

调试:GDB

1.2-1.3 GCC

  • 工作流程
    • Linux高并发服务器开发之第一章 Linux系统编程入门_第1张图片
    • 预处理
      • gcc -E test.c -o test.i
    • 编译
      • gcc -S test.i -o test.s
    • 汇编
      • gcc -c test.s -o test.o
    • 链接 
      • 可直接执行  ./xxx.o
  • gcc和g++的区别
    • 1、gcc、g++可以相互编译
    • 2、编译可以用gcc/g++,链接可以用g++/gcc -lstdc++
  •  gcc常用参数选项
    • Linux高并发服务器开发之第一章 Linux系统编程入门_第2张图片
    • Linux高并发服务器开发之第一章 Linux系统编程入门_第3张图片 

     

 1.4-1.5 静态库

  • 定义:在程序的链接阶段被复制到了程序中
  • 优缺点
    • 优:加载速度快、移植方便
    • 缺:消耗系统资源、浪费内存、更新、部署麻烦
  • 命名规则
    • libxxx.a
  • 制作
    • 1、gcc获得.o文件
    • 2、将.o 文件打包,使用ar工具
      ar rcs libxxx.a xxx.o xxx.o

 1.6 动态库

  • 定义:在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用
  • 优缺点
    • 优:实现进程间资源共享
    • 缺:加载速度慢、依赖提供的动态库
  • 命名规则
    • libxxx.so
  • 制作
    • 1、gcc得到.o文件,得到与位置无关的代码
      • gcc -c -fpic/-fPIC a.c b.c
    • 2、得到动态库
      • gcc -shared a.o b.o -o libcalc.so
         

1.7两者的工作原理

  • 静态库:gcc链接时,把静态库中的代码打包到可执行程序中Linux高并发服务器开发之第一章 Linux系统编程入门_第4张图片
  • 动态库: gcc链接时,不会把动态库中的代码打包到可执行程序中Linux高并发服务器开发之第一章 Linux系统编程入门_第5张图片
  • 定位共享库文件
    • 搜索DT_RPATH段
    • 环境变量LD_LIBRARY_PATH
    • /etc/ld.so.cache文件列表
    • /lib/,/usr/lib目录

1.10 Makefile

  • 功能:定义了一系列指定哪些文件需要先编译、哪些文件需要后编译、哪些需要重新编译。
    • 好处:自动化编译
  • 命名规则
    • 文件名
      • makefile/Makefile
    • 规则Linux高并发服务器开发之第一章 Linux系统编程入门_第6张图片
    • 其他规则一般都是为第一条规则服务的
  • 变量Linux高并发服务器开发之第一章 Linux系统编程入门_第7张图片

     

  • 函数

    • wildcard(查找函数)Linux高并发服务器开发之第一章 Linux系统编程入门_第8张图片

      注释:寻找当前目录和./sub/目录下为.c的文件 

    • patsubst(替换函数)Linux高并发服务器开发之第一章 Linux系统编程入门_第9张图片

      注释:把.c的文件替换为.o文件 

1.13GDB调试

  • 功能
    • 断点调试
  • 准备工作
    • gcc -g -Wall xxxx.c -o xxxx

         注:-g是在可执行文件中加入源代码的信息,并不是把整个源文件嵌入到可执行文件中

  • GDB的常用命令Linux高并发服务器开发之第一章 Linux系统编程入门_第10张图片

Linux高并发服务器开发之第一章 Linux系统编程入门_第11张图片

Linux高并发服务器开发之第一章 Linux系统编程入门_第12张图片

以下部分开始讨论UNIX系统, 

1.19文件描述符

  • 对内核而言,所有打开的文件都通过文件描述符引用
  • 分类
    • 0:进程的标准输入
    • 1:标准输出
    • 2:标准错误

1.20 open函数

  • 定义
  • 返回值
    • 若成功,返回文件描述符fd
    • 失败,返回-1
  • 使用事项
    • 三个头文件
    • 参数1:要打开的文件名
    • 参数2:函数的多个选项(进行“或”运算构成oflag参数)
      • 必选:O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)、
      • 可选:O_APPEND(追加到文件的尾端)、O_CREAT(修改文件的访问权限)....
  • 举例
    #include
    #include
    #include
    #include
    #include 
    
    int main()
    {
        int fd =open("a.txt",O_RDONLY);
    
        if(fd == -1)
        {
            perror("open");
        }
    
        close(fd);
    
        return 0;
    }

1.22 read、wirte函数

  • read
    • 定义
    • 返回值
      • 读到的字节数,若已到文件尾,返回0
      • 出错,返回-1
    • 参数1:源文件的文件描述符
    • 参数2:读取数据存放的地方,一般为一个数组
    • 参数3:数组的大小,使用sizeof函数
      • 注意头文件#include
  • wirte
    • 定义
    • 返回值
      • 若成功,返回已写的字节数
      • 若出错,返回-1
    • 参数1:目标文件的文件描述符
    • 参数2:读取数据存放的地方
    • 参数3:要写的数据实际大小
  • 举例
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
        //1.通过open打开english.txt文件
        int srcfd = open("english.txt",O_RDONLY);
        if(srcfd == -1)
        {
            perror("open");
            return -1;
    
        }
        //2.创建一个新的文件
        int destfd = open("copy.txt",O_WRONLY | O_CREAT,0664);
        if(destfd == -1)
        {
            perror("open");
            return -1;
    
        }
        //3.频繁的读写操作
        char buf[1024]={0};
        int len =0;
        while((len = read(srcfd,buf,sizeof(buf))) >0 )
        {
            write(destfd, buf, len);
    
        }
        //4.关闭文件
        close(srcfd);
        close(destfd);
    
    
    }
    

1.23 lseek函数

  • 定义:对文件指针进行操作
  • 返回值
    • 若成功,返回新的文件偏移量
    • 出错,返回-1
  • 参数2offset与参数3 whence有关
    • whence为 SEEK_SET, 将文件的偏移量设置为距文件开始处offset个字节
    • whence为 SEEK_CUR, 将文件的偏移量设置为其当前值加offset字节,可正可负
    • whence为 SEEK_END, 将文件的偏移量设置为文件长度加offset字节,可正可负
  • 作用
    • 1、移动文件指针到文件头
    • 2、获取当前文件指针的位置
    • 3、获取文件的长度
    • 4、拓展文件的长度
  • 举例
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int fd = open("hello.txt",O_RDWR);
        if(fd == -1)
        {
            perror("open");
            return -1;
        }
    
        //扩展文件的长度
        int ret =lseek(fd, 100 ,SEEK_END);
        if(ret == -1)
        {
            perror("lseek");
            return -1;
        }
        //写入一个空数据
        write(fd, " ", 1);
    
        //关闭文件
        close(fd);
    
    }

  • 1.24stat函数

    • 定义:获取具体文件的信息Linux高并发服务器开发之第一章 Linux系统编程入门_第13张图片

    • 返回值

      • 若成功,返回0;

      • 出错,返回-1

    • 参数1:要操作的文件名路径

    • 参数2:结构体变量,包括文件的相关信息

      • mode_t:文件的类型和存取的权限

      • st_size:文件字节数

    • 举例(获取该文件的大小)

      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main()
      {
          struct stat statbuf;
      
          int ret = stat("a.txt", &statbuf);
      
          if(ret == -1)
          {
              perror("stat");
              return -1;
          }
      
          printf("size: %ld\n", statbuf.st_size);
      
          return 0;
      
      
      
      }

 1.25使用stat函数获取文件的信息(实现ls -l的功能)

  • 代码
    //模拟实现ls -l的指令
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    int main(int argc, char * argv[])
    {
        //判断输入的参数是否正确
    
        if(argc < 2)
        {
            printf("%s filename\n",argv[0]);
            return -1;
    
        }
    
        //通过stat函数获取用户传入的文件信息
        struct stat st;
        int ret = stat(argv[1], &st);
        if(ret == -1)
        {
            perror("stat");
            return -1;
        }
    
        //获取文件类型和文件权限
        char perms[11]={0};
    
        switch (st.st_mode & S_IFMT) //与掩码进行或运算
        {
            case S_IFSOCK:
                perms[0]='s';
                break;
            case S_IFLNK:
                perms[0]='l';
                break;
            case S_IFREG:
                perms[0]='-';
                break;
            case S_IFBLK:
                perms[0]='b';
                break;    
            case S_IFDIR:
                perms[0]='d';
                break;
            case S_IFCHR:
                perms[0]='c';
                break;
            case S_IFIFO:
                perms[0]='p';
                break;
            default:
                perms[0]='?';
                break;
        }
    
        //判断文件的访问权限
    
        //文件所有者
        perms[1] = (st.st_mode & S_IRUSR)?'r':'-';
        perms[2] = (st.st_mode & S_IWUSR)?'w':'-';
        perms[3] = (st.st_mode & S_IXUSR)?'x':'-';
    
        //文件所在组
        perms[4] = (st.st_mode & S_IRGRP)?'r':'-';
        perms[5] = (st.st_mode & S_IWGRP)?'w':'-';
        perms[6] = (st.st_mode & S_IXGRP)?'x':'-';
    
        //其他人
        perms[7] = (st.st_mode & S_IROTH)?'r':'-';
        perms[8] = (st.st_mode & S_IWOTH)?'w':'-';
        perms[9] = (st.st_mode & S_IXOTH)?'x':'-';
    
        //获取硬连接数
        int linuNum = st.st_nlink;
    
        //文件所有者
        char * fileUser =getpwuid(st.st_uid)->pw_name;
    
        //文件所在组
        char * fileGrp = getgrgid(st.st_gid)->gr_name;
    
        //文件大小
        long int fileSize = st.st_size;
    
        //获取修改时间
        char * time =ctime(&st.st_mtime);
        char mtime[512]={0};
        strncpy(mtime,time,strlen(time)-1);
    
        char buf[1024];
        sprintf(buf, "%s %d %s %s %ld %s %s",perms,linuNum,fileUser,fileGrp ,fileSize,mtime,argv[1]);
    
        printf("%s\n",buf);
    
        return 0;
    
    }

1.26文件属性操作函数

  • access:判断文件是否存在,是否具有某些权限
    • 定义
    • 返回值
      • 若成功,返回0
      • 出错,返回-1
    • 参数1:所要操作的文件名
    • 参数2:测试信息mode
      • R_OK:测试读权限
      • W_OK:测试写权限
      • X_OK:测试执行权限
    • 举例
      #include 
      #include 
      
      int main()
      {
          int ret = access("a.txt",F_OK);
          if(ret == -1 )
          {
              perror("access");
      
          }
      
          printf("文件存在!!\n");
      
          return 0;
      }

  • chmod:修改文件信息
    • 定义
    • 返回值
      • 若成功,返回0;
      • 出错,返回-1
    • 参数1:所要修改的文件名
    • 参数2:常量的按位或,如0775
    • 举例
      #include 
      #include 
      
      int main()
      {
          int ret = chmod("a.txt",0775);
      
          if(ret == -1 )
          {
              perror("chmod");
              return -1;
          }
      
          return 0;
      
      }

  • chown:修改文件的用户ID和组ID
  • truncate:缩减或扩展文件的尺寸至指定大小
    • 定义
    • 返回值
      • 若成功,返回0;
      • 出错,返回-1
    • 参数1:要修改的文件名
    • 参数2:所要调整的文件大小
    • 举例 
      #include 
      #include 
      #include 
      
      int main()
      {
      
          int ret = truncate("a.txt",20);
      
          if(ret == -1)
          {
              perror("truncate");
          
          }
          return 0;
      }

1.27 目录操作函数

  • rename:更改目录名
    • 定义
    • 返回值
      • 若成功,返回0;
      • 出错,返回-1
    • 参数1:原来的目录名
    • 参数2:新的目录名
    • 举例
      #include
      
      int main()
      {
          int ret = rename("aaa","bbb");
      
          if(ret == -1)
          {
              perror("rename");
              return -1;
          }
          return 0;
      
      
      }

  • chdir:修改工作目录
    • 定义
    • 返回值
      • 若成功,返回0;
      • 出错,返回-1
    • 参数1:要修改的工作目录
  • getcwd:获取当前的工作路径
    • 定义
      • ​​​​​​​
    • 返回值
      • 若成功,返回buf
      • 出错,返回NULL
    • 参数1:缓冲区地址
    • 参数2:缓冲区的长度
    • 举例
      #include 
      #include 
      #include 
      #include 
      #include 
      
      
      int main()
      {
          //1、获取当前的工作目录
          char buf[128];
          getcwd(buf,sizeof(buf));
          printf("当前的工作目录为:%s\n",buf);
      
          //2、修改工作目录
      
          int ret = chdir("/home/metrorise/linux/lesson1-26");
          if(ret == -1)
          {
              perror("chdir");
              return -1;
          }
      
          //3、创建新的文件
          int fd = open("chdir.txt",O_CREAT | O_RDWR,0664);
          if(fd == -1)
          {
              perror("open");
              return -1;
          }
          
          //4、修改后的工作目录
          char buf1[128];
          getcwd(buf1,sizeof(buf));
          printf("当前的工作目录为:%s\n",buf1);  
      
          //5、关闭
          close(fd);
      
      
      
      
      }

  • mkdir:创建目录
  • rmdir:删除目录

1.28 目录遍历函数 

  • opendir:打开一个目录
    • 定义
    • 返回值
      • 若成功,返回DIR *类型,目录流
      • 出错,返回NULL
    • 参数1:需要打开的目录名称
  • readdir:读取目录中的数据
    • 定义
      • ​​​​​​​
    • 返回值
      • 若成功,返回读取到的文件信息
      • 出错,返回NULL
    • 参数:dirp是opendir返回的结果
  • closedir:关闭目录
    • 举例
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int getFileNum(const char * path);//函数的声明
      
      // 读取某个目录下所有的普通文件的个数
      int main(int argc, char * argv[]) 
      {
      
          if(argc < 2) {
              printf("%s path\n", argv[0]);   //输入格式:./xx  目标目录
              return -1;
          }
      
          int num = getFileNum(argv[1]);
      
          printf("普通文件的个数为:%d\n", num);
      
          return 0;
      }
      
      // 用于获取目录下所有普通文件的个数
      int getFileNum(const char * path) {
      
          // 1.打开目录
          DIR * dir = opendir(path);
      
          if(dir == NULL) {
              perror("opendir");
              exit(0);
          }
      
          struct dirent *ptr;
      
          // 记录普通文件的个数
          int total = 0;
      
          //循环读目录文件
          while((ptr = readdir(dir)) != NULL) {
      
              // 获取名称——判断是. 还是..文件,将其忽略
              char * dname = ptr->d_name;
      
              // 忽略掉. 和..     strcmp比较字符串
              if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
                  continue;
              }
      
              // 判断是否是普通文件还是目录
              if(ptr->d_type == DT_DIR) {
                  // 目录,需要继续读取这个目录
                  char newpath[256];
                  sprintf(newpath, "%s/%s", path, dname);
                  total += getFileNum(newpath);
              }
      
              if(ptr->d_type == DT_REG) {
                  // 普通文件
                  total++;
              }
      
      
          }
      
          // 关闭目录
          closedir(dir);
      
          return total;
      }

 1.29 dup、dup2函数

  • dup:复制一个现有的文件描述符
  • dup2:重定向文件描述符

     

    • 定义
      • ​​​​​​​
    • 返回值
      • 若成功,返回新的文件描述符
      • 出错,返回-1
    • 参数1:旧的文件描述符
    • 参数2:新的文件描述符
    • 举例
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main() {
      
          int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
          if(fd == -1) {
              perror("open");
              return -1;
          }
      
          int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
          if(fd1 == -1) {
              perror("open");
              return -1;
          }
      
          printf("fd : %d, fd1 : %d\n", fd, fd1);
      
          int fd2 = dup2(fd, fd1);
          if(fd2 == -1) {
              perror("dup2");
              return -1;
          }
      
          // 通过fd1去写数据,实际操作的是1.txt,而不是2.txt
          char * str = "hello, dup2";
          int len = write(fd1, str, strlen(str));
      
          if(len == -1) {
              perror("write");
              return -1;
          }
      
          printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);
      
          close(fd);
          close(fd1);
      
          return 0;
      }

1.30 fcntl函数 

  • 主要功能
    • 1、复制文件描述符
    • 2、设置/获取文件的状态标志
  • 定义
    • ​​​​​​​
  • 返回值
    • 若成功,依赖于cmd
    • 出错,返回-1
  • 参数2:命令
    • F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
    • F_GETFL : 获取指定的文件描述符文件状态flag
    • F_SETFL : 设置文件描述符文件状态flag
  • 代码
    #include 
    #include 
    #include 
    #include 
    
    int main() {
    
        // 1.复制文件描述符
        // int fd = open("1.txt", O_RDONLY);
        // int ret = fcntl(fd, F_DUPFD);
    
        // 2.修改或者获取文件状态flag
        int fd = open("1.txt", O_RDWR);
        if(fd == -1) {
            perror("open");
            return -1;
        }
    
        // 获取文件描述符状态flag
        int flag = fcntl(fd, F_GETFL);
        if(flag == -1) {
            perror("fcntl");
            return -1;
        }
        flag |= O_APPEND;   // flag = flag | O_APPEND
    
        // 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
        int ret = fcntl(fd, F_SETFL, flag);
        if(ret == -1) {
            perror("fcntl");
            return -1;
        }
    
        char * str = "nihao";
        write(fd, str, strlen(str));
    
        close(fd);
    
        return 0;
    }

 

你可能感兴趣的:(#,linux,运维,服务器)