进程与线程

进程的通信方式
  1. 管道

    管道的传输是单向的,如果需要传输数据需要创建两条管道。例如A进程给B进程传送数据。创建A管道像内核开辟的一块缓冲区发送数据。B进程创建管道联想缓冲区获取数据。

  2. 消息队列

    消息队列是存储在内存当中的消息链表。在发送数据的时候,会分成一个独立的数据单元(数据块),消息体是用户自订的数据类型,消息的发送方和接收方要约定好消息体的数据类型,所以每个消息都是固定大小的存储块,不像管道的无格式字节流数据。例如A进程给B进程发送消息。先创建消息队列。A进程像消息队列put消息。B进程从消息队列中get消息。get后的消息会被删除。

    消息队列不适合大数据传输,因为在每个内核中每个消息体都有一个最大长度的限制,同时所有队列所包含的全部消息信息体的长度也是有上限的。在Linux内核当中,会有两个宏定义MSGMAXMSGMNB,分别定义了一条信息的最大长度和一个队列的最大长度。

    消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。

//创建/打开消息队列
mq_open();
//关闭消息队列
mq_close();
//销毁消息队列
mq_unlink();
//获取队列属性
mq_getattr();
//设置队列属性
mq_setattr();
//发送消息
mq_send();//timeout 参数为NULL
mq_timedsend();//timeout 参数不为NULL
//接受消息
mq_receive();//timeout 参数为NULL
mq_timedreceive();//timeout 参数不为NULL
  1. 共享内存

    消息队列中的读取和写入的过程都会有用户态和内核态之间的信息拷贝过程。共享内存的机制就是拿出一块虚拟化的空间地址来映射到相同的物理内存中。进程写入东西的就是,另一个进程也可以看到。避免了拷贝的开销。

  2. 信号量

    如果使用了共享内存的通信方式就会出现同步和互斥的问题。如果多个进程同时写入有可能就会出现冲突。例如两个进程都同时写一个地址,那先写的那个进程会发现内容被别人覆盖了。

    为了防止多进程竞争资源出现问题,信号量作为一个保护机制保证在任意时刻只有一个进程可以访问该共享内存资源。

    控制信号量的两种操作

    • P操作:
      如果信号量 - 1 >= 0 代表还有资源可用,进程正常执行。相反信号量 - 1 < 0。阻塞。
    • V操作:
      如果信号量 + 1 <= 0,代表有阻塞的进程,唤醒其继续执行。信号量 + 1 > 0,表明无阻塞的进程。
Linux中的信号量函数
//有名信号量创建和初始化
sem_open();
//有名信号量删除
sem_close();
//无名信号量创建和初始化
sem_init();
//无名信号量删除
sem_destory();
//等待资源
sem_wait();//为0时睡眠
sem_trywait();//为0时报错
//唤醒其他睡眠
sem_post();
  1. 信号

    信号是进程间通信机制中的唯一的异步通信机制。信号产生会有如下操作:

    1. 执行默认的操作
    2. 捕捉信号
    3. 忽略信号
  2. socket

    管道、消息队列、共享内存、信号量和信号都是在同意主机上进行通信的,如果跨网络不同主机之间的进程通信就需要socket套接字。

线程的通信方式

线程之间的通信是用于保证线程的同步,而不是用于数据交换

  1. 锁机制(互斥锁、条件变量、读写锁)

    互斥锁防止数据结构被并发修改。
    读写锁允许多个线程同时读共享内存,但是写操作是互斥的。
    条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。

//互斥锁初始化
pthread_mutex_init();
//上锁
pthread_mutex_lock();//若已经上锁,挂起等待
pthread_mutex_trylock();//若已经上锁,返回错误
//解锁
pthread_mutex_unlock();
//销毁锁(当前锁为释放状态)
pthread_mutex_destroy();
  1. 信号量(同进程)
  2. 信号(同进程)
Linux中的线程函数
//线程创建
pthread_create();
//线程退出
pthread_exit();
//线程等待
pthread_join();
//线程脱离
pthread_detach();
//线程ID获取
pthread_self();
//线程ID比较
pthread_equal();
//向另一个线程发送取消请求
pthread_cancel();
为什么切换进程比切换线程慢?

这个问题首先要了解到物理内存和虚拟内存的关系。物理内存将进程的内存在磁盘划分的部分上面保存下来,然后用虚拟地址对这个物理地址进行映射。
然后映射就需要用到MMU(内存管理单元),他根据页表(存放物理地址和虚拟地址的映射,在内存里)来将虚拟地址翻译成物理地址。
每次访问内存,都需要进行虚拟地址到物理地址的转换,因此,每条指令进行一两次或更多地去访问页表是必要的,而页表又是存在于内存中的。
显然,访问页表(内存)次数太多导致其成为了操作系统地一个性能瓶颈,我们得想个法子解决它。

转换检测缓冲区(Translation Lookaside Buffer,TLB)应运而生,也称为快表。
TLB 通常内置在 CPU 的 MMU 中,这访问速度跟内存不是一个档次的。内存中的页表一般被称为慢表。
事实上,TLB 的出现是基于这样一种现象的:大多数程序总是对少量的页面进行多次的访问。因此,只有很少的页表项会被反复读取,而其他的页表项很少被访问。
TLB 中存放的就是那些会被反复读取的页表项。换句话说,TLB 中存放的就是页表中的一部分副本。
若 TLB 命中,就不需要再访问内存了;若 TLB 中没有目标页表项,则还需要去查询内存中的页表(慢表),从页表中得到物理页框地址,同时将页表中的该表项添加到 TLB 中。
由于进程切换会涉及到虚拟地址空间的切换,这就导致内存中的页表也需要进行切换,一个进程对应一个页表是不假,但是 CPU 中的 TLB 只有一个,页表切换后这个 TLB 就失效了。这样,TLB 在一段时间内肯定是无法被命中的,操作系统就必须去访问内存,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢。
而线程切换呢,由于不涉及虚拟地址空间的切换,也就不存在这个问题了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值