【Linux学习】线程详解

目录

十八.多线程

        18.1 线程与进程

        18.2 内核视角看待创建线程与进程

        18.3 线程优缺点总结

        线程的优点:

        线程的缺点:

        线程的用途:

        18.4 线程与进程的联系

十九.线程控制

        19.1 POSIX线程库

        19.2 线程创建

        19.3 线程等待

        19.4 线程终止

        19.5 线程分离

        19.6 线程ID及进程地址空间布局


十八.多线程

        18.1 线程与进程

在Linux系统中,线程(Thread)和进程(Process)是两种不同的执行单元,它们之间有几个重要的区别:

  1. 资源共享:线程是属于同一进程的执行单元,它们共享同一进程的资源,如内存空间、文件描述符等。而进程是独立的执行单元,拥有独立的内存空间和资源,进程之间的通信通常需要通过特定的IPC(进程间通信)机制。

  2. 调度线程是由内核进行调度的最小执行单元,因此线程的切换开销通常比进程小。进程的调度由操作系统负责,而线程的调度可以在用户空间内完成,因此线程的切换速度通常更快。

  3. 创建和销毁开销:创建线程比创建进程要快,因为线程共享了父进程的地址空间和其他资源。销毁线程的开销也比销毁进程小。

  4. 独立性:进程是独立的执行环境,一个进程的崩溃通常不会影响其他进程;而线程是共享相同地址空间的执行单元,一个线程的崩溃可能会影响同一进程内的其他线程。

  5. 通信:进程之间的通信需要使用IPC机制,如管道、消息队列、共享内存等;而线程之间可以直接共享全局变量等数据,也可以使用线程间同步机制来进行通信和协调。

总的来说,线程是轻量级的执行单元,更适合用于并发编程和任务并行,可以更高效地利用系统资源;而进程是独立的执行环境,更适合用于资源隔离和单独执行任务。

        18.2 内核视角看待创建线程与进程

之前表示进程所用的结构图:

一个进程的创建实际上伴随着其进程控制块(task_struct)、进程地址空间(mm_struct)以及页表的创建,虚拟地址和物理地址就是通过页表建立映射的

但如果我们在创建“进程”时,只创建task_struct,并要求创建出来的task_struct和父task_struct共享进程地址空间和页表,那么创建的结果就是下面这样的:

此时我们创建的实际上就是四个线程:

  • 其中每一个线程都是当前进程里面的一个执行流,也就是我们常说的“线程是进程内部的一个执行分支”。
  • 同时我们也可以看出,线程在进程内部运行,本质就是线程在进程地址空间内运行,也就是说曾经这个进程申请的所有资源,几乎都是被所有线程共享的。

  如何理解之前的进程?

从内核角度来看:

  • 进程:它是承担分配系统资源的基本实体。
  • 线程:它是CPU调度的基本单位,承担进程资源的一部分的基本实体。

         因此,我们从现在理解进程就不能说一个task_struct结构了,一个进程它包含了进程地址空间、文件相关的属性、各种信号、页表等

        换言之,当我们创建进程时是创建一个task_struct、创建地址空间、维护页表,然后在物理内存当中开辟空间、构建映射,打开进程默认打开的相关文件、注册信号对应的处理方案等等。而我们之前接触到的进程都只有一个task_struct,也就是该进程内部只有一个执行流,即单执行流进程,反之,内部有多个执行流的进程叫做多执行流进程。

CPU如何看待task_struct?

CPU不管有多少条执行流,只看task_struct,你task_struct有1条执行流就是单执行流的task_struct,有多执行流,你就是多执行流的task_struct。如下图: 

单执行流进程被调度:

多执行流进程被调度:

因此,CPU看到的虽说还是task_struct,但已经比传统的进程要更轻量化了。 

Linux下并不存在真正的线程,而是用进程模拟的?

确实,在 Linux 系统中,并没有严格意义上的线程实体,而是使用进程模拟了线程的行为。在 Linux 内核中,线程被实现为轻量级进程(Lightweight Process,LWP),这些轻量级进程与父进程共享了相同的地址空间和其他资源,因此在用户空间看起来就像是在同一个进程中创建了多个线程一样。

Linux 内核提供了一些系统调用(如 clone()),允许创建这样的轻量级进程。这些轻量级进程可以与父进程共享资源,包括地址空间、文件描述符等。由于轻量级进程的实现方式与进程类似,因此内核中并不需要专门的线程管理模块,所有的线程操作都可以通过进程相关的系统调用来完成。

总的来说,Linux 中的线程实际上就是轻量级进程,是通过进程模拟实现的。尽管在用户空间中看起来像是在操作线程,但在内核层面实际上是在操作轻量级进程。因此,将 Linux 中的线程概念加上引号以示区分,是合适的。

        18.3 线程优缺点总结

线程的优点:

  1. 低开销创建和切换: 创建一个新线程的开销比创建一个新进程小得多,线程之间的切换也相对较快。

  2. 资源消耗较少: 线程相比进程占用的资源要少很多,因为线程共享了相同进程的资源,如地址空间、文件等。

  3. 并发性能提升: 能够充分利用多处理器系统的并行性,提高程序的并发性能。

  4. IO操作重叠: 在IO密集型应用中,线程可以同时等待不同的IO操作,从而提高了IO操作的效率。

  5. 任务并行处理: 能够将任务分解成多个线程来同时执行,提高了计算密集型应用的执行效率。

线程的缺点:

  1. 性能损失: 当计算密集型线程的数量超过可用处理器的数量时,可能会造成性能损失,因为额外的同步和调度开销会增加。

  2. 健壮性降低: 编写多线程程序需要更加全面和深入的考虑,线程之间的同步和数据共享容易引发健壮性问题。

  3. 缺乏访问控制: 线程是进程内的执行分支,因此在多线程程序中,访问控制变得更加困难,可能会影响进程内部的资源访问。

  4. 编程难度提高: 编写和调试多线程程序比单线程程序更加困难,因为需要考虑线程同步、数据共享等问题。

线程的用途:

  1. 提高CPU密集型程序的执行效率: 合理利用多线程技术,将任务分解成多个线程来并行执行,提高计算密集型应用的执行效率。

  2. 提高IO密集型程序的用户体验: 在IO密集型应用中,使用多线程可以同时进行多个IO操作,提高了程序的响应速度和用户体验。

总的来说,线程是一种强大的并发编程工具,能够提高程序的并发性能和响应速度。但是在使用线程时,需要注意合理设计线程数量和任务分配,避免因过多线程导致的性能损失和健壮性问题。

        18.4 线程与进程的联系

进程是资源分配的基本单位;线程是调度的基本单位

线程共享进程数据,但也拥有自己的一部分数据:

  • 线程ID
  • 一组寄存器(存储自己的上下文信息)
  • 栈(每个线程都有临时数据,都需要压栈出栈,各自独立)
  • errno
  • 信号屏蔽字
  • 调度优先级

多线程共享

共享同一地址空间,因此代码段(Text Segment)、数据段(Data Segment)都是共享的:

  • 如果定义一个函数,在各线程中都可以调用;
  • 如果定义一个全局变量,在各线程中都可以访问到;

除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

十九.线程控制

        19.1 POSIX线程库

应用层的原生线程库:

  • pthread 线程库是应用层的原生线程库,意味着它并非由操作系统内核直接提供,而是由第三方实现并被大部分操作系统默认包含。

函数系列:

  • 与线程相关的函数构成了一个完整的系列,大部分函数的名称都以 "pthread_" 打头,例如 pthread_create、pthread_join、pthread_mutex_init 等。

使用方法:

  • 要使用 pthread 线程库,需要包含头文件 <pthread.h>
  • 在链接时,需要使用编译器的 -lpthread 选项来链接 pthread 库。

错误检查:

  • 传统的一些函数在出错时会设置全局变量 errno,并返回 -1 来表示错误,例如 read、write 等。
  • pthread 函数在出错时不会设置全局变量 errno,而是通过返回值来表示错误。通常,成功返回 0,失败返回非零值。
  • pthread 同样提供了线程内的 errno 变量,以支持其他使用 errno 的代码,但是建议优先使用返回值来判断错误,因为读取返回值的开销更小。

        19.2 线程创建

创建线程的函数叫做pthread_create,函数原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • 参数说明:

    • thread:获取创建成功的线程ID,是一个输出型参数。
    • attr:用户设置线程的属性,传入 NULL 表示默认属性。
    • start_routine:表示线程的入口函数,即线程启动后要执行的函数。
    • arg:传给线程入口函数的参数。
  • 返回值说明:

    • 线程创建成功返回 0,失败返回相应的错误码。 

让主线程创建一个新线程 

  • 当一个程序启动时,操作系统会创建一个进程,同时也会创建一个线程,这个线程称为主线程。

  • 主线程通常是产生其他子线程的线程,在启动其他线程后,主线程可以继续执行其他操作,也可以等待子线程执行完毕后再继续执行。

  • 主线程通常负责完成一些必要的执行动作,比如初始化操作、资源的释放等。

下面是一个示例代码,展示了如何在主线程中创建一个新线程:

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void* thread_run(void* args)                                                                                                                             
{      const char* id = (const char*)args;      while(1){      printf("I am %s thread, %d\n", id, getpid());      sleep(1);      }      
}      int main()      
{      pthread_t tid; pthread_create(&tid, NULL, thread_run, (void*)"thread 1");      while(1){      printf("I am mian thread, %d\n",getpid());      sleep(1);      }      return 0;      
}

注意:你需要在编译时添加 -pthread 选项来链接 pthread 库

运行结果:

此时使用ps axj的命令查看进程信息: 虽然此时该进程中有两个线程,但是我们看到的进程只有一个,因为这两个线程都是属于同一个进程的。 

使用ps -aL命令,可以显示当前的轻量级进程。

  • 不带-L,看到是就一个个的进程
  • 带-L,看到的是每个进程内的多个轻量级进程

其中,LWP(light weight process)就是轻量级进程的ID,可以看到显示的两个轻量级进程的PID是相同的,因为它们属于同一个进程。

注意:在Linux中,应用层的线程与内核的LWP是一一对应的,实际上操作系统调度的时候采用的是LWP,而并非PID,只不过我们之前接触到的都是单线程进程,其PID和LWP是相等的,所以对于单线程进程来说,调度时采用PID和LWP是一样的。

获取线程ID

在提取线程 ID 的过程中,常见的两种方式如下:

  • 创建线程时通过输出型参数获得。
  • 通过调用pthread_self函数获得。
pthread_t pthread_self(void);

调用pthread_self函数即可获得当前线程的ID,类似于调用getpid函数获取当前进程的ID。

例如下面的代码,我们通过主线程创建一个新线程,主线程不断的打印新线程的ID,新线程去执行回调函数,打印出自己的ID:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* thread_run(void* args) {while(1) { //新线程打印自己的IDprintf("我是新线程[%s],我线程ID是:%lu\n", (const char*)args, pthread_self());sleep(1);}
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_run, (void*)"new thread");while(1) { //主线程先是创建新线程并打印新线程的IDprintf("我是主线程,我创建的线程ID是:%lu, 我的线程ID是:%lu\n", tid, pthread_self());sleep(1);}
}

运行代码,可以看到这两种方式获取到的线程的ID是一样的。

        19.3 线程等待

线程等待是指一个线程等待另一个线程执行完毕后再继续执行的过程。在 POSIX 线程中,常用的线程等待函数是 pthread_join。一个线程被创建出来,就如同进程一般,也是需要被等待的。如果主线程不对新线程进行等待,那么这个新线程的资源也是不会被回收的。线程需要被等待,如果不等待会产生类似于“僵尸进程”的问题,也就是内存泄漏。

线程等待的函数: pthread_join()

int pthread_join(pthread_t thread, void **retval);

参数说明:

  • thread:被等待线程的ID
  • retval:它是一个输出型参数,用来获取新线程退出的时候,函数的返回值;新线程函数的返回值是void*,所以要获取一级指针的值,就需要二级指针,也就是void**;

返回值

  • 成功返回0
  • 失败返回错误码

使用说明:

  • 调用该函数的线程将挂起等待,直到被指定的线程结束。
  • 被等待的线程以不同的方式终止,根据终止方式,retval 所指向的单元里存放的值也会不同:
    • 如果被等待的线程通过 return 返回,retval 所指向的单元里存放的是被等待线程函数的返回值。
    • 如果被等待的线程被其他线程调用 pthread_cancel 异常终止,retval 所指向的单元里存放的是常数 PTHREAD_CANCELED
    • 如果被等待的线程是自己调用 pthread_exit 终止的,retval 所指向的单元存放的是传给 pthread_exit 的参数。
    • 如果对被等待线程的终止状态不感兴趣,可以传入 NULLretval 参数。

 为什么线程异常终止会导致整个进程崩溃?

  1. 进程异常处理: 当一个进程中的某个子进程异常终止时,父进程可以通过 wait()waitpid() 函数获取到子进程的退出状态,包括退出码、终止信号等信息。这是因为子进程和父进程是相互独立的,一个子进程的异常终止不会影响到父进程的执行。

  2. 线程异常处理: 在一个进程中,所有线程共享同一地址空间和资源,它们是相互依赖的。如果一个线程异常终止,整个进程都会受到影响,因为线程之间是共享资源的。当一个线程异常终止时,整个进程的执行状态就会变得不确定,甚至可能导致进程崩溃。因此,pthread_join() 函数只能获取到线程正常退出时的退出码,而不能获取到线程异常退出的信息。

  • 一个线程的错误可能会影响到其他线程以及整个进程的执行状态。如果一个线程访问了无效的内存地址,或者发生了其他类似的错误,可能会导致整个进程崩溃。
  • 因此,当一个线程异常终止时,整个进程的执行状态就变得不确定。此时,父线程(或者主线程)可能无法正常地调用 pthread_join() 函数来等待子线程的结束,因为整个进程可能已经处于异常状态,无法继续执行。

模拟野指针问题 

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* thread_run(void* args) {int num = *(int*)args;while(1) {printf("I am new thread [%d], my thread ID is: %lu\n", num, pthread_self());sleep(1);// 模拟异常情况if(num == 3) {printf("Thread number: %d quit due to an error.\n", num);int* p = NULL;*p = 100;}}
}int main() {pthread_t tid[5];for(int i = 0; i < 5; i++) {pthread_create(tid + i, NULL, thread_run, (void*)&i);}// 等待线程结束for(int i = 0; i < 5; i++) {void* status = NULL;pthread_join(tid[i], &status);printf("Thread [%d] exit with status: %d\n", i, (int)status);}return 0;
}

        19.4 线程终止

线程的终止是线程生命周期的一个重要部分。在多线程编程中,正确地管理线程的终止是确保程序稳定性和可靠性的关键。本节将介绍线程终止的几种常见方式以及如何安全地终止线程。

1. 正常退出

线程可以通过以下方式正常退出:

  • 返回语句: 线程函数可以通过简单地返回来正常退出。在这种情况下,线程函数中的 return 语句将会返回一个值,并且该值将会成为线程的退出状态,可以由其他线程通过 pthread_join 获取到。
void* thread_function(void* arg) {// 线程执行的代码return (void*)42; // 返回退出状态
}
  •  调用 pthread_exit 函数: 可以在线程函数内部显式地调用 pthread_exit 函数来退出线程。与返回语句相比,使用 pthread_exit 可以更明确地指定线程的退出状态。
void pthread_exit(void *retval);
void* thread_function(void* arg) {// 线程执行的代码pthread_exit((void*)42); // 退出线程并指定退出状态
}

2. 异常退出

线程可能会因为各种异常情况而提前退出,这时候需要考虑如何优雅地处理线程的终止。

  • 取消线程: 可以使用 pthread_cancel 函数取消线程的执行。被取消的线程会在接收到取消请求后立即退出,但是需要确保线程的资源得到正确释放,避免资源泄漏。
pthread_cancel(thread_id); // 取消线程的执行

异常处理: 在线程函数内部进行异常处理,确保线程在出现异常时能够安全地退出,释放资源。

        19.5 线程分离

线程分离是多线程编程中的重要概念,它涉及到线程的生命周期管理和资源释放。本节将介绍线程分离的概念、作用以及如何在编程中正确地使用线程分离。

1. 线程分离的概念

线程分离是指将一个线程从其创建者(通常是主线程)中分离出来,使得该线程在终止时能够自行释放资源,而无需其他线程显式地调用 pthread_join 函数等待其结束。分离线程后,不需要调用 pthread_join 函数来等待线程的结束,线程结束时系统会自动释放其资源。

2. 线程分离的作用

  • 资源释放: 分离线程可以确保线程在结束时自动释放其占用的资源,避免资源泄漏。
  • 避免僵尸线程: 分离线程可以避免产生僵尸线程,提高程序的稳定性和可靠性。
  • 简化代码: 分离线程可以简化代码逻辑,不需要显式地等待线程结束。

3. 使用 pthread_detach 函数分离线程

可以使用 pthread_detach 函数将线程分离,使得该线程在结束时自动释放资源。

pthread_detach(thread_id); // 将线程分离

4. 示例代码

下面是一个示例代码,演示了如何创建线程并将其分离:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* thread_function(void* arg) {// 线程执行的代码printf("Thread is running...\n");sleep(3); // 模拟线程执行一段时间printf("Thread finished.\n");pthread_exit(NULL); // 结束线程
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_function, NULL); // 创建线程pthread_detach(tid); // 分离线程printf("Main thread finished.\n");return 0;
}

在这个示例中,主线程创建了一个新线程,并立即将其分离。因此,主线程不需要显式地等待新线程结束,而是可以立即继续执行后续代码。当新线程执行完毕后,系统会自动回收其资源。

5. 注意事项

  • 分离已结束的线程: 要确保在调用 pthread_detach 函数之前,线程尚未结束。如果尝试分离已经结束的线程,可能会导致不确定的行为。
  • 资源释放: 分离线程仅仅负责释放线程占用的资源,而不负责资源的释放。因此,在线程中分配的内存等资源需要在线程结束时手动释放,以避免资源泄漏。

线程分离是多线程编程中的一项重要技术,能够简化代码逻辑并提高程序的稳定性和可维护性。正确地使用线程分离可以有效地管理线程的生命周期,避免资源泄漏和僵尸线程的产生。

        19.6 线程ID及进程地址空间布局

  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和内核中的LWP不是一回事。
  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供的pthread_self函数,获取的线程ID和pthread_create函数第一个参数获取的线程ID是一样的。

pthread_t到底是什么类型呢?

  • 在Linux系统中,pthread_t 是一个不透明的数据类型,用于表示线程的标识符。它实际上是一个指向线程控制块(TCB)的指针,TCB 包含了线程的各种属性和状态信息,如线程ID、栈信息、调度优先级等。
  • 当调用 pthread_create 函数创建新线程时,它会返回一个 pthread_t 类型的变量,这个变量实际上是一个对应于新创建线程的唯一标识符。该标识符由 NPTL 线程库使用,用于在用户空间管理线程的状态和属性,并与内核级的 LWP(轻量级进程)关联起来。
  • 线程库通过 pthread_t 变量来管理每个线程的状态和属性。线程库会根据 pthread_t 变量找到相应的线程控制块,然后根据需要对线程进行操作.pthread_t 类型取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。
  • 总的来说,pthread_t 是线程库提供的一种抽象类型,用于标识和管理线程,由线程库内部使用,开发者只能通过 pthread_create 等接口来创建和操作线程。

通过ldd命令可以看到,我们采用的线程库实际上是一个动态库。

  • 在Linux系统中,动态库加载后需要将动态库本身的全部信息映射到主线程的堆、栈之间的共享区域。动态库除了包含代码之外,还需要维护线程创建的数据结构,这些数据结构通常存储在共享区域中,供所有线程访问和修改。
  • 每个线程运行时都需要有自己的临时数据,因此需要有私有的栈结构。在地址空间中,通常只有一个栈,这个栈是用来给主线程使用的,而其他线程会使用动态库中维护的栈结构。这样就可以确保每个线程都有自己的私有栈,不会与其他线程共享栈空间。
  • 动态库本身还负责线程的组织和管理工作,每个线程在地址空间中都会有一个 struct pthread(线程结构体)以及线程局部存储和线程栈。这些信息由动态库来维护,实现了“先描述、再组织”的方式。每个新线程在共享区域都有一块描述其信息的区域,通过这个区域的起始地址可以获取到线程的各种信息。
  • 在内核中,LWP(轻量级进程)和 struct pthread(线程结构体)是一一对应的关系。在用户层,如果存在多个线程结构体,为了和内核的 LWP 一一对应,这些线程结构体中一定会包含对应的 LWP。这样就可以确保在用户空间中的线程结构体和内核中的 LWP 是一一对应的关系,方便线程的管理和调度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/256261.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT高效提问—prompt实践(生成VBA)

ChatGPT高效提问—prompt实践&#xff08;生成VBA&#xff09; 2. 生成VBA函数操作Excel ​ 当前Excel表格数据无背景颜色&#xff0c;区分不明显。假如我们想美化数据展示效果&#xff0c;把标题行设置为浅蓝色&#xff0c;其余奇数行设置为橙色&#xff0c;该怎么操作呢&am…

开启Android学习之旅-1

最近在学习《第一行代码 Android》&#xff0c;两天看书把所有代码都敲了一遍。由于之前没有接触过 Kotlin&#xff0c;导致了囫囵吞枣&#xff0c;跟着书会敲&#xff0c;离开就忘了。Android 大佬开发的各种框架、控件好像大部分都用了 Kotlin。看他们的源码&#xff0c;理解…

AI 消灭软件工程师?| 新程序员

【导读】“AI 是否会取代软件工程师”是自大模型爆火以来程序员们最为关心的一大话题&#xff0c;事关编程的未来与我们每一位程序员。本文作者 Babel CEO、多年的资深程序员张海龙深入技术本质&#xff0c;为我们进行了答疑解惑。 本文精选自《新程序员 007&#xff1a;大模型…

书生谱语-大语言模型测试demo

课程内容简介 通用环境配置 开发机 InterStudio 配置公钥 在本地机器上打开 Power Shell 终端。在终端中&#xff0c;运行以下命令来生成 SSH 密钥对&#xff1a; ssh-keygen -t rsa您将被提示选择密钥文件的保存位置&#xff0c;默认情况下是在 ~/.ssh/ 目录中。按 Enter …

【HTTP】localhost和127.0.0.1的区别是什么?

目录 localhost是什么呢&#xff1f; 从域名到程序 localhost和127.0.0.1的区别是什么&#xff1f; 域名的等级划分 多网站共用一个IP和端口 私有IP地址 IPv6 今天在网上逛的时候看到一个问题&#xff0c;没想到大家讨论的很热烈&#xff0c;就是标题中这个&#xff1a; …

如何把手机平板变为电脑的屏幕

文章目录 安装软件运行效果结尾 本文首发地址 https://h89.cn/archives/181.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 闲置的手机平板、触屏音箱等&#xff0c;均可作为电脑的扩展屏&#xff0c;为电脑增加一块显示屏&#xff0c;本文介绍如何使用免费的软件s…

Linux运用fork函数创建进程

fork函数&#xff1a; 函数原型&#xff1a; pid_t fork(void); 父进程调用fork函数创建一个子进程&#xff0c;子进程的用户区父进程的用户区完全一样&#xff0c;但是内核区不完全一样&#xff1b;如父进程的PID和子进程的PID不一样。 返回值&#xff1a; RETURN VALUEO…

Linux操作系统基础(十三):Linux安装、卸载MySQL

文章目录 Linux安装、卸载MySQL 一、卸载系统自带的mariadb-lib 二、上传安装包并解压 三、按顺序安装 错误1: 错误2: 错误3: 错误4: 四、初始化数据库 五、目录授权&#xff0c;否则启动失败 六、启动msyql服务 七、查看msyql服务的状态 八、在/var/log/mysqld.l…

CTFshow web(php命令执行 55-59)

web55 <?php /* # -*- coding: utf-8 -*- # Author: Lazzaro # Date: 2020-09-05 20:49:30 # Last Modified by: h1xa # Last Modified time: 2020-09-07 20:03:51 # email: h1xactfer.com # link: https://ctfer.com */ // 你们在炫技吗&#xff1f; if(isset($_GET[…

CVE-2018-19518 漏洞复现

CVE-2018-19518 漏洞介绍 IMAP协议&#xff08;因特网消息访问协议&#xff09;它的主要作用是邮件客户端可以通过这种协议从邮件服务器上获取邮件的信息&#xff0c;下载邮件等。它运行在TCP/IP协议之上&#xff0c;使用的端口是143。在php中调用的是imap_open函数。 PHP 的…

C++三剑客之std::optional(一) : 使用详解

相关文章系列 C三剑客之std::optional(一) : 使用详解 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)&#xff1a;深入剖析 目录 1.概述 2.构建方式 2.1.默认构造 2.2.移动构造 2.3.拷贝构…

Docker容器监控-CIG

目录 一、CIG说明 1. CAdvisor 2. InfluxDB 3. Grafana 二、环境搭建 1. 创建目录 2. 编写 docker-compose.yml 3. 检查并运行容器 三、进行测试 1. 查看 influxdb 存储服务 是否能正常访问 2. 查看 cAdvisor 收集服务能否正常访问 3. 查看 grafana 展现服务&#…

智能家居中可自行收集能量的无电池的无线设备

此图片来源于网络 1、背景 ZigBee是一种基于IEEE 802.15.4标准的低速短距离无线通信技术&#xff0c;用于创建个人区域网络。其名称来源于蜜蜂的八字舞&#xff0c;因为蜜蜂通过这种舞蹈来与同伴传递花粉的所在方位信息&#xff0c;从而构成了群体中的通信网络。ZigBee技术具…

AcWing 802. 区间和 离散化

文章目录 题目链接题目描述解题思路代码实现总结 题目链接 链接: AcWing 802. 区间和 题目描述 解题思路 离散化是一种常用的技巧&#xff0c;它能够将原始的连续数值转换为一组离散的值&#xff0c;从而简化问题的处理。在这段代码中&#xff0c;离散化的过程主要分为三个步…

[C/C++] -- CMake使用

CMake&#xff08;Cross-platform Make&#xff09;是一个开源的跨平台构建工具&#xff0c;用于自动生成用于不同操作系统和编译器的构建脚本。它可以简化项目的构建过程&#xff0c;使得开发人员能够更方便地管理代码、依赖项和构建设置。 CMake 使用一个名为 CMakeLists.tx…

sheng的学习笔记-docker部署数据库oracle,mysql

部署目录&#xff1a;sheng的学习笔记-部署-目录-CSDN博客 docker基础知识可参考 sheng的学习笔记-docker部署&#xff0c;原理图&#xff0c;命令&#xff0c;用idea设置docker docker安装数据库 mac版本 安装oracle 下载oracle镜像 打开终端&#xff0c;输入 docker s…

【开源】JAVA+Vue.js实现高校学院网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学院院系模块2.2 竞赛报名模块2.3 教育教学模块2.4 招生就业模块2.5 实时信息模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学院院系表3.2.2 竞赛报名表3.2.3 教育教学表3.2.4 招生就业表3.2.5 实时信息表 四、系…

计算机网络——06分组延时、丢失和吞吐量

分组延时、丢失和吞吐量 分组丢失和延时是怎样发生的 在路由器缓冲区的分组队列 分组到达链路的速率超过了链路输出的能力分组等待排到队头、被传输 延时原因&#xff1a; 当当前链路有别的分组进行传输&#xff0c;分组没有到达队首&#xff0c;就会进行排队&#xff0c;从…

Java String源码剖析+面试题整理

由于字符串操作是计算机程序中最常见的操作之一&#xff0c;在面试中也是经常出现。本文从基本用法出发逐步深入剖析String的结构和性质&#xff0c;并结合面试题来帮助理解。 String基本用法 在Java中String的创建可以直接像基本类型一样定义&#xff0c;也可以new一个 Str…

LeetCode、198. 打家劫舍【中等,一维线性DP】

文章目录 前言LeetCode、198. 打家劫舍【中等&#xff0c;一维线性DP】题目及分类思路线性DP&#xff08;一维&#xff09; 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注…