Linux高并发服务器之第一章

目录

目录

1.1 Linux 开发环境搭建

必备软件:

1.2  GCC

1.2.1 GCC简介

1.2.2 vim命令

1.2.3 gcc工作原理

1.2.4 gcc和g++的区别

1.2.5 GCC常用选项

1.3 静态库的制作

1.3.1什么是库

1.3.2 静态库命名规则: 

1.3.3 制作静态库 

1.4动态库的制作和使用

 1.5静态库与动态库的对比

1.6 Makefile

 1.6.2  变量和模式匹配​编辑

1.7GDB调试

 1.8文件IO

1.8.1标准C库IO函数

 1.8.2标准C库IO和Linux系统IO的关系​编辑

 1.8.3虚拟地址空间

​ 1.8.4文件描述符

 1.8.5 Linux系统IO函数​编辑


1.1 Linux 开发环境搭建

必备软件:

Ubuntu 18.04

XShell-用于远程登录,使用SSH协议,TCP连接,端口号22

XFtp,本次实验中尚未用到

Visual studio code,安装扩展包:

Linux安装

sudo apt install openssh-server

VS code软件安装Remote Development插件,用于建立远程连接管理

 同样使用SSH建立连接

使用ctrl+L能够使得SSH软件清屏

为了便于后期操作,windows端与ubuntu端使用相同的公钥

ssh-keygen -t rsa

此处也可以记录一下原本的过程:

(1)远程主机收到用户的登录请求,把自己的公钥发给用户。

(2)用户使用这个公钥,将登录密码加密后,发送回来。

(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

中文软件包

大体本节实验就结束了
 

1.2  GCC

1.2.1 GCC简介

GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器。GNU还有其他语言,例如Java、Go的编译器套件,也包括了这些语言的库

GCC不仅支持C,还可以区分不同的C语言标准,支持用命令行选项来控制编译器在编译源代码时应该遵循哪个C标准。

查看标准可以使用如下命令

gcc/g++ -v/–version

1.2.2 vim命令

1.打开命令: vim a.log   (注意后缀名要加上)

2.退出命令:按ESC键 跳到命令模式,然后输入   :q (不保存退出)    或者    :wq (保存退出)。

3.注意   以  :  和  /  开头的命令都有历史纪录,可以首先键入  :  或  /  然后按上下箭头来选择某个历史命令。

:w 保存文件但不退出vi   
:w file 将修改另外保存到file中,不退出vi   
:w! 强制保存,不推出vi   
:wq 保存文件并退出vi   
:wq! 强制保存文件,并退出vi   
:q 不保存文件,退出vi   
:q! 不保存文件,强制退出vi   
:e! 放弃所有修改,从上次保存文件开始再编辑命令历史

编译文件的命令格式如下:

gcc 文件名.扩展名 -o 输出文件名

运行文件

 ./输出文件名 

1.2.3 gcc工作原理

预处理:指令-E,举例如下:

gcc test.c -E -o test.i

编译:指令-S

gcc test.i -S -o test.s

汇编:指令-c

gcc test.s -s -o test.o

.o文件已是可执行文件

rm--删除文件

1.2.4 gcc和g++的区别

第四个内容有关gcc和g++的区别
首先声明gcc和g++都是GNU(组织)的一个编译器

误区1:gcc只能用于编译c代码,g++只能编译c++代码。但实际上两者都可以

后缀为.c的gcc把它当做c程序,而g++当做是c++程序

后缀为.cpp的,两者都会认为是c++程序,c++的语法规则更加严谨一些

编译阶段,g++会调用gcc,对于c++代码,两者等价,但因为gcc命令不能自动和c++程序使用的库链接,所以通常用g++完成链接。

为了统一,索性直接用g++完成编译、链接,但实际上,gcc也可以完成.cpp程序的编译

误区2:gcc不会定义__cplusplus宏,而g++会

实际上,该宏只是标志着编译器会把代码按照c还是c++语法来解释
如果后缀为.c,并且采用gcc编译器,那么宏就是未定义的;使用g++就是已定义
误区3:编译只能用gcc,链接只能用g++

严格来说,这句话不错,但是混淆了概念,应该说编译二者都可以用,而链接则可以用g++或者gcc -lstdc++

gcc命令不能自动和C++程序使用的库链接,所以通常用g++来完成链接,但在编译阶段,二者等价
 

1.2.5 GCC常用选项

 

1.3 静态库的制作

1.3.1什么是库

1.3.2 静态库命名规则: 

 

1.3.3 制作静态库 

 

 意思是:把calc目录下的libcalc.a文件复制到当前目录的lib/文件夹中

 编译main.c时出现问题,此时是因为include目录下的head.h没有引用到

 出现未定义库问题时,指定库

使用静态库的完整步骤

gcc -c add.c sub.c mult.c div.c -I ../include/    //首先通过gcc把.c文件编译成.o文件

ar rcs libsuanshu.a add.o div.o mult.o sub.o      //把.o文件打包成libsuanshu.a文件

mv libsuanshu.a  ../lib/                          //把libsuanshu.a移动到lib/文件夹中

gcc main.c -o app -I ./include/ -l suanshu -L ./lib //main.c引用到suanshu静态库生成app文件
 

 

1.4动态库的制作和使用

 编译过程中出现问题:

解决方法:

如上

出现问题2

 问题:动态库文件寻找不到

解决方法

方法一:在终端配置(暂时性作用),当终端结束后,作用消失

寻找到动态库的地址

 把地址添加到LD_LIBRARY_PATH

 方法二:永久配置

方法2.1  修改.bashrc文件

vim .bashrc 打开文件

shift+g 然后按o向下插入一行

 然后

source .bashrc 

 方法2.2

 1.5静态库与动态库的对比

 

 

 

 

 

1.6 Makefile

1.6.1Makefile文件命名和规则

 1.6.2  变量和模式匹配

 

 

 

 

make clean 执行清除文件操作

1.7GDB调试

 

打断点 

 

查看断点信息

 

 1.8文件IO

1.8.1标准C库IO函数

 1.8.2标准C库IO和Linux系统IO的关系

 1.8.3虚拟地址空间

 1.8.4文件描述符

一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个IO设备。内核返回一个小的非负整数,叫做描述符.它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需要记住这个描述符。

 1.8.5 Linux系统IO函数

open函数打开文件

man 2 open

看open的函数

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

    // 打开一个已经存在的文件
    int open(const char *pathname, int flags);
        参数:
            - pathname:要打开的文件路径
            - flags:对文件的操作权限设置还有其他的设置
              O_RDONLY,  O_WRONLY,  O_RDWR  这三个设置是互斥的
        返回值:返回一个新的文件描述符,如果调用失败,返回-1

    errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。

    #include <stdio.h>
    void perror(const char *s);作用:打印errno对应的错误描述
        s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误描述)
    

    // 创建一个新的文件
    int open(const char *pathname, int flags, mode_t mode);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {

    // 打开一个文件
    int fd = open("a.txt", O_RDONLY);

    if(fd == -1) {
        perror("open");
    }
    // 读写操作

    // 关闭
    close(fd);

    return 0;
}

 open创建新文件



 /*
          int open(const char *pathname, int flags, mode_t mode);
          参数:
             -pathname:要创建的文件的路径
             -flags:对文件的操作权限和其他的设置
                 --必选项:O_RDONLY,O_WRONLY,O_RDWR,这三个是互斥的
                 --可选项: O_CREAT文件不存在,创建新文件
             -mode:八进制的数,表示用户对创建出的新文件的操作权限 比如:0775  rwx
             最终的权限是:mode & ~umask
             0777  ->111111111
             &0775 ->111111101 =111111101=0775
             --------------------------
         umask的作用就是抹去某些权限

         flags参数是一个int类型的数据,占4个字节,32位
         flags 32个位,每一位就是一个标志位

          mode_t umask(mode_t mask);

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

int main()
{
    //创建一个新文件
   int fd = open("create.txt",O_RDWR|O_CREAT,0777);
   if(fd==-1)
   {
    perror("open");
   }

   //关闭
   close(fd);
   
    return 0;
}

read and write函数

/*
  #include <unistd.h>
 ssize_t read(int fd, void *buf, size_t count);
     参数:
     - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
     - buf:需要读取数据存放的地方,数组的地址(传出参数)
     -count:指定的数组的大小

     返回值:
       -成功:
       >0:返回实际的读取到的字节数
       =0:文件已经读完
       -1:文件读取失败,并且设置为errno

       #include <unistd.h>
 ssize_t write(int fd, const void *buf, size_t count);
     参数:
     -fd:文件描述符,open得到的,通过这个文件描述符
     -buf:要往磁盘写入的数据,数据
     -count:要写的数据的实际的大小
     返回值:
     成功:实际写入的字节数
     失败:返回-1,并写入errno

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


 int main()
 {
    //通过open打开englist.txt
     int srcfd =  open("englist.txt",O_RDONLY);
     if(srcfd ==-1)
     {
       perror("open");
       return -1;
     }
    //创建一个新的文件
     int destfd = open("cpy.txt",O_WRONLY|O_CREAT,0664);
     if(destfd==-1)
     {
      perror("open");
      return -1;
     }
    //频繁的读写操作
    char buf[1024] ={0};
     int len =0;
     len = read(srcfd,buf,sizeof(buf));
   
    while(len>0)
    {
      write(destfd,buf,len);
    }

    //关闭文件
    close(destfd);
    close(srcfd);

    return 0;
 } 

lseek函数

/*
  标准C库函数
       #include <stdio.h>
       int fseek(FILE *stream, long offset, int whence);
       


       #include <sys/types.h>
       #include <unistd.h>
       off_t lseek(int fd, off_t offset, int whence);
      参数:
      -fd:文件描述符,通过open得到的,通过这个fd操作某个文件
      -offset:偏移量
      -whence:
        SEEK_SET
           设置文件指针的偏移量
        SEEK_CUR
           设置偏移量:当前位置+第二个参数offset的值
        SEEK_END
           设置偏移量:文件大小+第二个参数offset的值
    
      返回值:返回文件指针的位置
    作用:
    1.移动文件指针到文件头
      lseek(fd,0,SEEK_SET);
    2.获取当前文件指针的位置
      lseek(fd,0,SEEK_CUR);
    3.获取文件长度
      lseek(fd,0,SEEK_END);
    4.拓展文件的长度,当前文件10b,100b,增长了100个字节
      lseek(fd,100,SEEK_END)


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

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

  //写入一个空数据
write(fd," ",1);

//拓展文件长度
int ret =lseek(fd,100,SEEK_END);
if(ret==-1)
{
  perror("lseek");
  return -1;
}


  close(fd);

  return 0;
}

stat函数

在Linux系统中,stat()是一个C语言函数,用于获取文件的详细信息。它可以用于检索文件的权限、所有者、大小、创建时间和修改时间等信息。

 

/*

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

       int stat(const char *pathname, struct stat *statbuf);
        作用:获取一个文件相关的一些信息
        参数:
            -pathname:操作的文件的路径
            -statbuf:结构体变量,传出参数,用于保存获取到的信息 

            返回值:
               成功:返回0
               失败:返回-1,设置errno
       int fstat(int fd, struct stat *statbuf);
       int lstat(const char *pathname, struct stat *statbuf);
*/
      #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>
       #include<stdio.h>

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;
}

模拟实现ls-l命令

//模拟ls -l命令
//-rw-rw-r-- 1 su su 11 Feb 17 22:47 a.txt
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
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_IFLNK:
        perms[0] ='l';
        break;
        case __S_IFDIR: //目录
        perms[0] ='d';
        break;
        case __S_IFREG: //普通文件
        perms[0] ='-';
        break;
        case __S_IFBLK: //块设备
        perms[0] ='b';
        break;
        case __S_IFCHR: //字符设备
        perms[0] ='c';
        break;
        case __S_IFSOCK: //套接字
        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 linkNum = 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, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);

    printf("%s\n", buf);
    return 0;
}

文件属性操作函数

在Linux中,access()函数用于检查当前进程是否可以访问一个给定的文件路径。

access()函数需要两个参数:文件路径名和一个表示请求访问的方式的整数值。其中,0表示检查文件是否存在,也就是判断当前进程是否能够打开这个文件。

/*
       #include <unistd.h>
       int access(const char *pathname, int mode);
         作用:判断某个文件是否有某个权限,或者判断文件是否存在
         参数:
            -pathname:判断文件路径
            -mode:
                R_OK:判断是否有读权限
                W_OK:判断是否有写权限
       
*/
#include <unistd.h>
#include<stdio.h>

int main()
{
    int ret=access("a.txt",F_OK);
    if(ret==-1)
    {
        perror("access");
    }
    printf("文件存在\n");
    return 0;
}

chmod()函数

在Linux中,chmod()函数用于修改文件或目录的权限模式。它的原型定义在sys/stat.h头文件中,函数的参数包括要修改权限的文件或目录的路径名,以及用来设置新的权限模式的一个八进制数值。

/*
       #include <sys/stat.h>

       int chmod(const char *pathname, mode_t mode);
           修改文件的权限
           参数:
           -pathname:修改文件路径
           -mode:需要修改权限值,八进制的数
          返回值:成功返回0,失败返回-1
       int fchmod(int fd, mode_t mode);
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{  
    int ret=chmod("a.txt",0775);
    if(ret==-1)
    {
        perror("chmod");
        return -1;
    }
    
    return 0;
}

chown()函数

在Linux中,chown()函数用于修改文件或目录的所有者和所属组。它的原型定义在unistd.h头文件中,函数的参数包括要修改所有者和所属组的文件或目录的路径名,以及新的所有者和所属组的用户ID和组ID。

/*

       #include <unistd.h>

       int chown(const char *pathname, uid_t owner, gid_t group);

       查看ownerid指令 :vim /etc/passwd

       查看groupid指令 :vim /etc/group

*/

 truncate()函数

在Linux中,truncate()函数用于截断文件的大小。它的原型定义在unistd.h头文件中,函数的参数包括要截断大小的文件路径名,以及截断后的大小。

/*
       #include <unistd.h>
       #include <sys/types.h>

       int truncate(const char *path, off_t length);
       作用:缩减或者拓展文件的尺寸至指定大小
       参数:
            -path:需要修改文件的路径
            -length:需要最终文件变成的大小
       返回值:成功返回0,失败返回-1
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
   int ret=truncate("a.txt",20);
   if(ret==-1)
   {
       perror("truncat");
       return -1;
   }
   return 0;
}

 mkdir()函数

创建目录,/path/to/newdir表示要创建的目录路径,0755表示新目录的权限模式。其中,第一个数字0表示这是一个八进制数值,755表示所有者、组和其他用户都具有读、写和执行权限。

/*

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

       int mkdir(const char *pathname, mode_t mode);
       作用:创建一个目录
       参数:
           pathname:创建目录的路径
           mode:权限,八进制的数
       返回值:成功返回0,失败返回1
        
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
       int main()
       {

        int ret =mkdir("aaa",777);

        if(ret==-1)
        {
            perror("mkdir");
            return -1;
        }
        return 0;
       }

rename函数

/*
   #include <stdio.h>

       int rename(const char *oldpath, const char *newpath);
*/

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

int main()
{
    int ret =rename("aaa","bbb");
    if(ret==-1)
    {
        perror("rename");
        return -1;
    }
    return 0;
}

chdir函数

/*

    #include <unistd.h>
    int chdir(const char *path);
        作用:修改进程的工作目录
            比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
        参数:
            path : 需要修改的工作目录

    #include <unistd.h>
    char *getcwd(char *buf, size_t size);
        作用:获取当前工作目录
        参数:
            - buf : 存储的路径,指向的是一个数组(传出参数)
            - size: 数组的大小
        返回值:
            返回的指向的一块内存,这个数据就是第一个参数

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

int main() {

    // 获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf);

    // 修改工作目录
    int ret = chdir("/home/nowcoder/Linux/lesson13");
    if(ret == -1) {
        perror("chdir");
        return -1;
    } 

    // 创建一个新的文件
    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
    if(fd == -1) {
        perror("open");
        return -1;
    }

    close(fd);

    // 获取当前的工作目录
    char buf1[128];
    getcwd(buf1, sizeof(buf1));
    printf("当前的工作目录是:%s\n", buf1);
    
    return 0;
}

opendir()函数

在Linux中,opendir()函数用于打开一个目录,并返回指向目录流的指针。它的原型定义在dirent.h头文件中,函数的参数包括要打开的目录路径名。

readdir()

在Linux中,readdir()函数用于读取一个目录中的内容。它的原型定义在dirent.h头文件中,函数的参数包括要读取的目录流指针。

closedir()函数

在Linux中,closedir()函数用于关闭由opendir()函数打开的目录流。它的原型定义在dirent.h头文件中,函数的参数包括要关闭的目录流的指针。

统计目录下普通文件的个数

/*
       //打开一个目录
       #include <sys/types.h>
       #include <dirent.h>
       DIR *opendir(const char *name);
       参数:
        -name:需要打开的目录的名称
        返回值:
          DIR*类型,理解为目录流
      
      //读取目录中的数据
       #include <dirent.h>
       struct dirent *readdir(DIR *dirp);
       -参数:dirp是opendir返回的结果
       -返回值:
             struct dirent,代表读取到的文件信息
             读取到了末尾或者失败了,返回NULL

      //关闭目录 
      #include <sys/types.h>
      #include <dirent.h>
      int closedir(DIR *dirp);
*/
#include <sys/types.h>

#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 int getFileNum(const char*path);
       
       //读取到某个目录下所有的普通文件的个数
       int main(int argc,char*argv[])
       //argc是一个整数类型的参数,表示命令行参数的数量,包括程序本身
       //argv是一个指向字符指针数组的指针,每个指针指向一个命令行参数的字符串。
       {
        if(argc<2)
        {
            printf("%s path\n",argv[0]);
            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 ;
         //readdir(dir)==NULL时说明已读到文件结尾

        //记录普通文件的个数
            int total =0;
         while(ptr =readdir(dir)!=NULL)
         {
            //获取名称
            char * dname=ptr->d_name;



            //忽略掉.和..
            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;
       }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值