c++面经

1. 僵尸进程                

        僵尸进程(Zombie Process)在操作系统中指的是那些已经执行完毕,但其父进程尚未对其进行善后处理(例如读取子进程的状态信息或者执行回收资源的操作)的进程。在Unix和类Unix系统(比如Linux)中,当一个进程结束运行后,它的进程控制块(PCB,包含了进程的状态信息)仍然保留在系统中,直到父进程通过`wait()`系统调用对其进行处理。如果父进程没有调用`wait()`,那么结束的进程就会变成僵尸进程。

        僵尸进程是无害的,因为它们实际上并不占用除了进程表项之外的任何资源,也不会执行任何代码。然而,僵尸进程在进程表中占据了一个位置,而大量的僵尸进程可能会消耗所有的进程号,防止新的进程被创建。

解决僵尸进程的一般方法是:

1. 修改父进程代码,使其调用`wait()`或`waitpid()`来回收子进程的状态信息。
2. 如果僵尸进程的父进程已经结束,僵尸进程会被init进程(进程号为1的进程)收养,并由init进程负责调用`wait()`来处理它们。
3. 在一些情况下,可以发送一个信号给父进程,强制它去清理僵尸进程,例如使用`kill`命令发送`SIGCHLD`信号。

在编写多进程程序时,合理管理子进程的生命周期和状态是非常重要的,以避免产生僵尸进程。

2. 修改文件权限

        在Linux系统中,修改文件权限通常使用`chmod`(change mode)命令。这个命令可以通过字母表示法(u表示用户,g表示组,o表示其他人,a表示所有人)或者八进制数表示法来指定权限。

### 使用字母表示法

- `u` 表示文件的所有者(user)
- `g` 表示与文件的所有者在同一组的用户(group)
- `o` 表示其他用户(others)
- `a` 表示所有用户(all)

权限可以表示为:

- `r` 表示可读(read)
- `w` 表示可写(write)
- `x` 表示可执行(execute)

这是一些例子:

//给所有者增加执行权限:
  chmod u+x 文件名

//给组增加读写权限:
  chmod g+rw 文件名

//给其他用户去除所有权限:
  chmod o-rwx 文件名

//给所有用户设置读和执行权限:
  chmod a+rx 文件名

//只给所有者设置读写执行权限,而给组和其他用户设置读权限:
  chmod u=rwx,g=r,o=r 文件名

使用八进制数表示法

每个权限对应一个数字:

- `4` 表示可读(read)
- `2` 表示可写(write)
- `1` 表示可执行(execute)

权限数字组合起来就形成了三位数的八进制数:

- 第一位表示用户(u)的权限
- 第二位表示组(g)的权限
- 第三位表示其他用户(o)的权限

例如:

//设置所有者读写执行(7),组读执行(5),其他用户执行(1):
  chmod 751 文件名

//设置所有者读写(6),组读(4),其他用户无权限(0):
  chmod 640 文件名

//设置所有人都有读写执行权限:
  chmod 777 文件名

//设置所有人都有读写权限:
  chmod 666 文件名

        在使用`chmod`命令时,还可以加上`-R`选项递归地修改目录及其子目录中的所有文件和目录的权限:

chmod -R 755 目录名

        在修改文件权限之前,你需要确保有足够的权限去改变这些文件的权限,通常需要文件的所有者或者是root用户才能修改权限。

3. 修改用户组

        在Linux中,您可以使用各种命令来修改用户组。以下是一些常用的命令和步骤:

1. 更改用户的主要组:
   使用`usermod`命令加上`-g`选项来更改用户的主要组:

   sudo usermod -g 新主要组 用户名

   这将改变用户的主要组。请注意,这可能会影响用户对文件的默认访问权限,因为Linux系统中的文件通常会继承其所有者的主要组。

2.添加用户到附加组:
   使用`usermod`命令加上`-aG`选项来将用户添加到一个或多个附加组:

   sudo usermod -aG 组名1,组名2,... 用户名

   使用 `-a` 选项是非常重要的,因为它表示追加用户到新组而不会从用户目前所属的其他组中移除。

3. 创建新用户组:
   如果需要的话,可以先创建一个新的用户组:

   sudo groupadd 新组名

4.更改文件或目录的组:
   使用`chown`命令来更改文件或目录的组:

   sudo chown :新组名 文件或目录

   请注意,前面的冒号是必需的,这表明您正在更改组而不是所有者。

5. 更改用户的登录组:
   如果您需要更改用户登录时所属的组,可以使用`newgrp`命令:

   newgrp 新组名

   这个命令会启动一个新的shell会话,并且在这个会话中用户的主要组会被更改为指定的新组。

6. 查看用户所属的组:
   如果您想要确认用户的组修改操作是否成功,可以使用以下命令:

   groups 用户名

   或者:

   id 用户名

4. deb编包机制           linux学习系列——编包_传统的launchpad,是把代码下载到本地,然后执行debuild -s -sa在本地生成一个 包-CSDN博客

5. cmake作用

        CMake是一个跨平台的自动化构建系统,主要用于管理软件构建的过程,它使用平台和编译器独立的配置文件来生成标准的构建文件,使得你可以在多种系统上以统一的方式构建你的项目。

CMake的主要作用和特点包括:

1. 生成构建文件:
   CMake可以生成各种构建系统的文件,如Unix的Makefiles或Windows上的Visual Studio工程文件。你只需要为你的项目编写一次CMake配置文件(`CMakeLists.txt`),然后CMake可以基于这个配置文件生成适合不同平台的构建文件。

2. 跨平台:
   由于CMake的配置文件是独立于平台的,所以你可以使用相同的构建系统设置在不同的操作系统上构建你的软件,包括Linux、macOS和Windows。

3. 便于管理大型项目:
   对于大型项目,CMake通过允许你创建多个`CMakeLists.txt`文件来组织你的构建设置,可以很容易地管理复杂的构建过程。你可以在顶层目录有一个主`CMakeLists.txt`文件,并为项目的不同部分创建附属的`CMakeLists.txt`文件。

4. 查找库和程序:
   CMake有能力自动发现系统上的库和程序。使用`find_package`、`find_library`、`find_file`等命令,CMake可以定位依赖库的头文件和二进制文件,并将它们包含在构建过程中。

5. 测试和打包:
   CMake还包括测试和打包软件的能力。CTest可以用来定义和运行测试,而CPack可以用来生成各种分发格式的安装包,如`.deb`、`.rpm`、NSIS安装程序等。

6. 自定义构建选项:
   CMake允许用户通过命令行或图形界面(如cmake-gui)来配置构建选项,比如选择构建类型(Debug、Release等)、启用或禁用项目的特定部分等。

7. 可扩展性:
   CMake支持自定义模块和宏,你可以编写自己的CMake模块来扩展CMake的功能或简化常见的任务。

8. 现代语法:
   CMake的语言设计有现代化的特点,支持条件判断、循环、函数和宏等构造。

简而言之,CMake是一个强大的工具,可以简化跨平台项目的构建管理,使得开发者能够专注于代码本身而不是构建过程的细节。

6. makefile作用

        `Makefile` 是一个特殊的文件,用于存储编译和链接程序的命令。这个文件被 `make` 工具读取和解析,用于自动化构建过程。`make` 工具检查文件的依赖关系,并执行 `Makefile` 中定义的命令来编译源代码并构建目标程序或库。

`Makefile` 的主要作用和特点包含:

1. 自动化构建:
   `Makefile` 定义了如何从源代码编译出最终的可执行文件或库,自动化了编译过程,使得开发者不必手动输入复杂的编译指令。

2. 依赖性管理:
   `Makefile` 中可以定义文件间的依赖关系,使得只有当源代码文件或依赖发生变化时,`make` 命令才会重新编译相关的文件,这样可以节省编译时间。

3. 规则定义:
   开发者可以定义具体的规则,指定如何生成特定的目标文件。规则中包括目标、依赖和命令。当依赖的文件更新时,相关的命令将被执行来更新目标文件。

4. 参数化构建:
   `Makefile` 允许定义变量,这使得构建过程可以根据需要轻易地被自定义和调整,例如可以定义编译器选项、编译器路径等。

5. 不同的构建目标:
   你可以在一个 `Makefile` 中定义多个构建目标,包括编译整个项目、编译单个文件、清理构建结果、执行测试等。

6. 可重用性:
   一个良好编写的 `Makefile` 可以在不同的项目之间共享和重用,特别是当多个项目有类似的构建过程时。

7. 编译优化:
   `Makefile` 可以用来优化编译过程,如并行编译源文件来加快构建速度。

8. 跨平台构建:
   虽然 `Makefile` 通常与 Unix-like 系统相关联,但是通过编写具有适当条件判断的 `Makefile`,也可以支持跨平台构建。

        总而言之,`Makefile` 提供了一种灵活、强大的方式来管理和控制编译过程。通过简单的 `make` 命令,开发者可以执行复杂的构建任务,而无需记住繁琐的编译指令和链接参数。

二者区别(自己加的,面试没问)

CMake 和 Makefile 是两个相关但有区别的构建工具,它们在软件构建过程中扮演不同的角色:

1. 层级和抽象级别:
   - CMake 是一个高级构建系统配置工具。它使用平台和编译器独立的配置文件(通常是 `CMakeLists.txt`)来生成标准的构建文件,如 Makefiles 或其他构建脚本。
   - Makefile 是一个由 `make` 工具使用的低级构建脚本,包含了直接用于构建软件的规则和命令。

2. 跨平台:
   - CMake 设计为跨平台支持,能够为多种不同的编译环境生成相应的构建文件。这意味着,无论你在哪个平台上,只需要维护一套 CMake 配置文件即可。
   - Makefile 通常是平台特定的,并且需要为不同的操作系统编写不同的 Makefile 或在其中加入特定的条件判断,以支持跨平台构建。

3. 用户接口:
   - CMake 提供了基于文本的和图形的配置接口(如 `ccmake` 或 `cmake-gui`),使得用户可以在生成构建文件之前配置项目。
   - Makefile 通常不提供图形接口,而是通过编辑 Makefile 文件本身来修改构建参数。

4. 功能和复杂性:
   - CMake 内置了许多高级功能,如依赖性管理、编译器检测、测试支持等。
   - Makefile 通常需要用户手动编写所有规则和依赖关系,这可能会导致 Makefile 变得复杂且难以维护,特别是在大型项目中。

5. 使用场景:
   - CMake更适合于需要跨平台构建和需要复杂构建过程管理的项目。
   - Makefile 可以用于简单或者特定平台的项目,并且在某些情况下,开发者可能偏好直接编写 Makefile 来得到更好的控制。

        简单来说,CMake 是一个构建系统生成器,它可以根据你的项目配置生成 Makefile 或其他构建系统文件;而 Makefile 是一个构建脚本,包含了具体的构建命令,由 `make` 工具直接执行。CMake 的抽象级别更高,更易于跨平台使用和维护,而 Makefile 给予开发者更底层的控制,但可能需要针对不同平台进行手动调整。

7. 软连接硬链接

        在Unix-like操作系统中,软链接(Symbolic Link)和硬链接(Hard Link)是两种不同类型的文件系统链接:

硬链接(Hard Link): ln <源文件> <新创建的链接文件>

1. 硬链接是一个指向文件系统中文件数据的直接指针,它与原始文件有相同的inode(文件系统中的索引节点)。
2. 硬链接与文件名无关,你可以删除原始文件,硬链接仍然可以访问文件的内容,因为硬链接就像是文件的另一个名字。
3. 不能跨文件系统创建硬链接,硬链接必须在同一个文件系统中。
4. 不能为目录创建硬链接,只能为文件创建。
5. 当最后一个到文件的硬链接被删除时,文件的空间才会被释放。

软链接(Symbolic Link,也称为符号链接或软连接): ln -s <源文件> <新创建的链接文件>

1. 软链接相当于是一个快捷方式,它是一个特殊类型的文件,包含了另一个文件的路径信息。
2. 软链接可以跨文件系统,因为它仅仅存储了指向目标文件或目录的路径。
3. 当你删除或者移动了原始文件,软链接将会失效(变成死链接),因为它的目标路径已经不存在了。
4. 可以为目录创建软链接。
5. 软链接文件和原始文件有不同的inode。

        总结一下,硬链接像是一个文件的额外别名,它和原始文件在文件系统中完全平等,不存在主次之分。而软链接更像是一个指向另一个文件或目录的指针,它依赖于原始文件或目录的路径。硬链接通常用于保证文件即使被删除也能被访问,而软链接用于创建实际文件位置的引用,更加灵活,但当原始文件被移动或删除时,链接将会失效。

8. 类模板和函数模板

类模板和函数模板-CSDN博客

9. 多态

虚函数的疑问-CSDN博客

10. static成员函数变量作用,为什么定义为static 

c++中static的作用-CSDN博客

11. 深拷贝浅拷贝、代码

        在 C++ 中,深拷贝和浅拷贝是在进行对象复制时的两种不同方式。深拷贝会复制对象的所有成员,包括指向的动态内存,而浅拷贝只是简单地复制对象的成员变量值,而不会复制指向的动态内存。下面是一个简单的示例代码,演示深拷贝和浅拷贝的区别:


#include 
#include  // 用于字符串操作

class DeepCopyExample {
public:
    char* data; // 使用指针来模拟动态内存

    // 构造函数
    DeepCopyExample(const char* str) {
        data = new char[strlen(str) + 1]; // 为 data 分配内存
        strcpy(data, str); // 复制字符串内容
    }

    // 深拷贝构造函数
    DeepCopyExample(const DeepCopyExample& other) {
        data = new char[strlen(other.data) + 1]; // 为 data 分配内存
        strcpy(data, other.data); // 复制字符串内容
    }

    // 析构函数
    ~DeepCopyExample() {
        delete[] data; // 释放动态内存
    }
};

int main() {
    DeepCopyExample obj1("Hello");
    DeepCopyExample obj2 = obj1; // 调用深拷贝构造函数

    std::cout << "obj1 data: " << obj1.data << std::endl;
    std::cout << "obj2 data: " << obj2.data << std::endl;

    // 修改 obj2 的 data
    obj2.data[0] = 'C';

    std::cout << "After modifying obj2 data:" << std::endl;
    std::cout << "obj1 data: " << obj1.data << std::endl;
    std::cout << "obj2 data: " << obj2.data << std::endl;

    return 0;
}

        在这个示例中,`DeepCopyExample` 类包含一个 `char*` 类型的成员变量 `data`,用于模拟动态分配的内存。在构造函数中,我们为 `data` 分配内存并复制字符串内容。在深拷贝构造函数中,我们为新对象的 `data` 再次分配内存并复制内容,从而实现深拷贝。

        在 `main` 函数中,我们创建了两个对象 `obj1` 和 `obj2`,并通过深拷贝构造函数将 `obj1` 的内容复制给 `obj2`。然后我们修改了 `obj2` 的 `data`,但不会影响到 `obj1` 的 `data`,因为它们分别拥有独立的内存空间。

12. 智能指针,智能指针怎么释放的资源

        智能指针就是利用了 RAII 技术来实现自动管理资源的机制。通过智能指针,我们可以更方便地管理动态分配的内存、文件流、数据库连接等资源,避免内存泄漏和资源泄露。

        当使用智能指针时,智能指针对象会在它们超出作用域时自动调用析构函数,从而释放所管理的资源。这种自动释放资源的机制大大简化了资源管理的工作,减少了程序出错的可能性。

        智能指针的不同类型(如 `std::unique_ptr`、`std::shared_ptr`、`std::weak_ptr`)会根据自身的特性来管理资源的生命周期,例如 `std::unique_ptr` 独占所有权、`std::shared_ptr` 共享所有权等。但它们都依赖于 RAII 技术来实现资源的自动释放。

        自动释放资源的机制是通过 RAII(资源获取即初始化)技术来实现的。RAII 是 C++ 中的一种编程范式,它利用对象的生命周期与作用域的关系,来确保在对象生命周期结束时自动执行资源的获取和释放操作。

        在 C++ 中,当一个对象在其作用域结束时,其析构函数会被自动调用,从而实现资源的自动释放。这种特性被广泛运用在智能指针、文件流、数据库连接等资源管理场景中,帮助开发者避免内存泄漏和资源泄露的问题。下面是一个简单的示例来说明 RAII 的自动释放资源机制:


#include 

class Resource {
public:
    Resource() {
        std::cout << "Resource allocated" << std::endl;
    }
    
    ~Resource() {
        std::cout << "Resource released" << std::endl;
    }

    void doSomething() {
        std::cout << "Resource being used" << std::endl;
    }
};

int main() {
    {
        Resource res; // 在作用域内创建 Resource 对象

        // 使用 Resource 对象
        res.doSomething();
    } // Resource 对象超出作用域,自动调用析构函数释放资源

    return 0;
}

        在上面的示例中,当 `Resource` 对象 `res` 超出作用域时,它的析构函数会被自动调用,输出 `"Resource released"`,从而释放资源。这种自动释放资源的机制使得我们不再需要手动管理资源的释放,减少了出错的可能性。

13. 动态库 静态库

        动态库和静态库是在软件开发中常用的两种库文件形式,它们都是用来存放可重用的代码和函数的。下面我将简单介绍一下动态库和静态库的概念以及它们之间的区别:

静态库(Static Library):
- 静态库是在编译时将库的代码直接链接到可执行文件中的库文件,链接后生成独立的可执行文件。
- 静态库的文件格式一般为`.lib`(Windows)或`.a`(Unix/Linux)。
- 静态库的优点是使用简单,不需要依赖其他库文件,一旦链接到可执行文件中,程序的运行不依赖于库的存在。
- 缺点是静态库会使得可执行文件的体积变大,因为库中的代码被完整地复制到可执行文件中。

动态库(Dynamic Library):
- 动态库是在运行时被动态加载到内存中的库文件,程序在运行时才能够调用库中的函数。
- 动态库的文件格式一般为`.dll`(Windows)或`.so`(Unix/Linux)。
- 动态库的优点是可以减小可执行文件的体积,多个程序可以共享动态库,节省内存。
- 缺点是程序在运行时需要动态加载库,如果动态库不存在或者版本不兼容,程序可能无法运行。

 区别:
- 静态库在编译时被链接到可执行文件中,程序运行时不再依赖库文件;而动态库在运行时被加载到内存中,程序运行时依赖库文件。
- 静态库的代码复制到可执行文件中,体积较大,但运行时不需要依赖库文件;动态库体积较小,但程序运行时需要依赖库文件。

14. 查看进程id 

        ps命令

15. 如何关注进程状态

        ps aux

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值