Linux下进程和线程

一、基本定义

进程:

  1. 进程是系统分配资源的基本单位。每个进程都拥有独立的地址空间、内存、数据栈等系统资源

  2. 进程之间通过IPC(进程间通信)机制进行通信和数据共享

  3. 进程是程序的一次执行过程,它包含了程序代码、数据和系统资源(如打开的文件、网络连接等)

  4. 在Linux中,进程由进程ID(PID)唯一标识

线程:

  1. 线程是CPU调度的基本单位。线程共享进程的资源,包括代码段、数据段、堆等,但每个线程拥有自己的栈和程序计数器

  2. 线程之间共享进程的资源,这使得线程间的通信和数据共享变得相对简单

  3. 线程的主要优点是并发执行和共享资源,可以充分利用多核CPU的并行处理能力

  4. 在Linux中,线程通常是通过pthread库来创建和管理的

线程与进程的区别:

  1. 资源占用:进程拥有独立的资源空间,而线程则共享进程的资源。因此,创建线程通常比创建进程更加轻量级,所需的资源更少

  2. 并发性:多个线程可以在同一个进程中并发执行,而多个进程则需要通过IPC进行通信和协调。

  3. 独立性:进程之间是完全独立的,一个进程的崩溃不会影响其他进程。而线程共享进程的资源,一个线程的崩溃可能导致整个进程的崩溃。

  4. 通信方式:进程间通信通常需要使用系统调用或网络等机制,而线程间通信则可以通过共享内存或全局变量等方式实现

二、Linux下进程创建

在 Linux 系统中,进程通过非常简单的方式来创建,fork 系统调用会创建一个源进程的拷贝(副本)。调用 fork 函数的进程被称为 父进程(parent process),使用 fork 函数创建出来的进程被称为 子进程(child process)。父进程和子进程都有自己的内存映像。如果在子进程创建出来后,父进程修改了一些变量等,那么子进程是看不到这些变化的,也就是 fork 后,父进程和子进程相互独立。

虽然父进程和子进程保持相互独立,但是它们却能够共享相同的文件,如果在 fork 之前,父进程已经打开了某个文件,那么 fork 后,父进程和子进程仍然共享这个打开的文件。对共享文件的修改会对父进程和子进程同时可见。

那么该如何区分父进程和子进程呢?子进程只是父进程的拷贝,所以它们几乎所有的情况都一样,包括内存映像、变量、寄存器等。区分的关键在于 fork  函数调用后的返回值,如果 fork 后返回一个非零值,这个非零值即是子进程的 进程标识符(Process Identiier, PID),而会给子进程返回一个零值,可以用下面代码来进行表示

pid = fork();    // 调用 fork 函数创建进程
if(pid < 0){
  error()				 // pid < 0,创建失败
}
else if(pid > 0){
  parent_handle() // 父进程代码
}
else {
  child_handle()  // 子进程代码
}

父进程在 fork 后会得到子进程的 PID,这个 PID 即能代表这个子进程的唯一标识符也就是 PID。如果子进程想要知道自己的 PID,可以调用 getpid 方法。当子进程结束运行时,父进程会得到子进程的 PID,因为一个进程会 fork 很多子进程,子进程也会 fork 子进程,所以 PID 是非常重要的。我们把第一次调用 fork 后的进程称为 原始进程,一个原始进程可以生成一颗继承树

三、Linux下进程间通信

Linux 进程间的通信机制通常被称为 Internel-Process communication,IPCLinux 进程间通信的机制,大致来说,Linux 进程间的通信机制可以分为 6 种

  1. 信号 signal

信号是 UNIX 系统最先开始使用的进程间通信机制,因为 Linux 是继承于 UNIX 的,所以 Linux 也支持信号机制,通过向一个或多个进程发送异步事件信号来实现,信号可以从键盘或者访问不存在的位置等地方产生;信号通过 shell 将任务发送给子进程。

注:进程可以选择忽略发送过来的信号,但是有两个是不能忽略的:SIGSTOP 和  SIGKILL 信号。SIGSTOP 信号会通知当前正在运行的进程执行关闭操作,SIGKILL 信号会通知当前进程应该被杀死。

2.管道 pipe

在两个进程之间,可以建立一个通道,一个进程向这个通道里写入字节流,另一个进程从这个管道中读取字节流。管道是同步的,当进程尝试从空管道读取数据时,该进程会被阻塞,直到有可用数据为止。shell 中的管线就是用管道实现的

sort <f | head

如上,它会创建两个进程,一个是 sort,一个是 head,sort,会在这两个应用程序之间建立一个管道使得 sort 进程的标准输出作为 head 程序的标准输入。sort 进程产生的输出就不用写到文件中了,如果管道满了系统会停止 sort 以等待 head 读出数据

3.共享内存 shared memory

两个进程之间还可以通过共享内存进行进程间通信,其中两个或者多个进程可以访问公共内存空间。两个进程的共享工作是通过共享内存完成的,一个进程所作的修改可以对另一个进程可见(很像线程间的通信)。

4.先入先出队列 FIFO

先入先出队列 FIFO 通常被称为命名管道,命名管道的工作方式与常规管道非常相似,但是确实有一些明显的区别。未命名的管道没有备份文件:操作系统负责维护内存中的缓冲区,用来将字节从写入器传输到读取器。一旦写入或者输出终止的话,缓冲区将被回收,传输的数据会丢失。相比之下,命名管道具有支持文件和独特 API ,命名管道在文件系统中作为设备的专用文件存在。当所有的进程通信完成后,命名管道将保留在文件系统中以备后用。命名管道具有严格的 FIFO 行为。

5.消息队列 Message Queue

一听到消息队列这个名词你可能不知道是什么意思,消息队列是用来描述内核寻址空间内的内部链接列表。可以按几种不同的方式将消息按顺序发送到队列并从队列中检索消息。每个消息队列由 IPC 标识符唯一标识。消息队列有两种模式,一种是严格模式, 严格模式就像是 FIFO 先入先出队列似的,消息顺序发送,顺序读取。还有一种模式是 非严格模式,消息的顺序性不是非常重要。

6.套接字 Socket

管理两个进程间通信的是使用 socket,socket 提供端到端的双相通信。一个套接字可以与一个或多个进程关联。就像管道有命令管道和未命名管道一样,套接字也有两种模式,套接字一般用于两个进程之间的网络通信。

四、线程的分类

1.用户级线程

用户级线程避免使用内核,通常,每个线程会显示调用开关,发送信号或者执行某种切换操作来放弃 CPU,同样,计时器可以强制进行开关,用户线程的切换速度通常比内核线程快很多。在用户级别实现线程会有一个问题,即单个线程可能会垄断 CPU 时间片,导致其他线程无法执行从而饿死。如果执行一个 I/O 操作,那么 I/O 会阻塞,其他线程也无法运行

2.内核级线程

内核级线程通常使用几个进程表在内核中实现,每个任务都会对应一个进程表。在这种情况下,内核会在每个进程的时间片内调度每个线程

所有能够阻塞的调用都会通过系统调用的方式来实现,当一个线程阻塞时,内核可以进行选择,是运行在同一个进程中的另一个线程(如果有就绪线程的话)还是运行一个另一个进程中的线程。

3.混合实现

结合用户空间和内核空间的优点,采用了一种内核级线程的方式,然后将用户级线程与某些或者全部内核线程多路复用起来

注:在Linux中,线程实际上是共享同一地址空间的轻量级进程。因此,线程和进程在本质上都是进程,只是线程是某个进程的子任务

五.Linux启动

在Linux下,进程和线程都是实现并发执行的重要手段。进程拥有独立的资源空间,适合处理需要大量资源和独立性的任务;而线程则共享进程的资源,适合处理需要频繁通信和共享数据的任务。根据具体的应用场景和需求,可以选择使用进程或线程来实现并发执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值