Linux 基础 基础概念进程与线程

进程

传统的程序本身是一组指令的集合,是一个静态实体,无法描述程序在内存中的执行情况,也就不能如实反映程序并发执行过程的特征。

进程是操作系统进行资源分配和高度的独立单位,是程序在计算机上的一次执行过程。进程是动态概念,它可以申请和拥有系统资源,也是活动的实体。
进程由程序段、数据段和进程控制块组成。
进程控制块:描述和控制程序运行的一个数据结构,也称为PCB
程序段和数据段:分别存放进程运行的程序和需要的数据
进程特征:并发性、动态性、异步性、独立性
进行执行时间间断性,运行中的进程可能有三种状态:就绪状态、执行状态和阻塞状态。三种基本状态转换方式:阻塞->就绪,就绪->执行,执行->就绪,执行->阻塞
程序是静态,进程是动态。程序作为一种资料长期永久存在,进程是有生命周期,是暂时存在的。同一程序可以对应多个进程。程序并能独立运行,作为资源分配和独立运行的基本单元都是进程。

Linux进程用一个task_struct数据结构来表示,它就是Linux进程的PCB。

进程控制块(PCB):进程状态、进程调度信息、进程标识符、进程通信信息、进程链接信息、进程相关的文件信息、进程时间信息、进程虚拟内存信息等。
Linux进程在内存中由三个部分组成:堆栈段、代码段和数据段
Linux调度器将进程分为三个不同的类型:交互进程、批处理进程、监控进程(守护进程)。

fork进程

进程是操作系统进行资源分配和高度的独立单位,是程序在计算机上的一次执行过程。进程是动态概念,它可以申请和拥有系统资源,也是活动的实体。

fork创建进程

函数原型如下

1
2
3
#include <unistd.h>// 必须引入头文件,使用fork函数的时候,必须包含这个头文件,否则,系统找不到fork函数

pid_t fork(void); //void代表没有任何形式参数

父进程与子进程

  1. 掌握概念,什么是父进程,什么是子进程
    除了0号进程(系统创建的)之外,linux系统中都是由其他进程创建的。创建新进程的进程,即调用fork函数的进程为父进程,新建的进程为子进程。
  2. fork函数不需要任何参数,对于返回值有三种情况
    • 对于父进程,fork函数返回新建子进程的pid;
    • 对于子进程,fork函数返回 0;
    • 如果出错, fork 函数返回 -1。

父子进程共享资源

1.父子进程共享代码段(可读的)
父进程在创建子进程的时候,子进程会把父进程的地址空间里的数据段。和栈堆进行复制,但是没有复制代码段。

fork函数出错的情况

  1. fork函数返回值为-1即创建失败,有两种情况可能会导致fork 函数出错;
  2. 系统中已经有太多的进程存在;
  3. 调用fork函数的用户的进程太多。

进程间通信

  1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
  2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期 信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上, 该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,sigaction函数重新实现了signal函数);
  3. 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  4. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针其他通信机制运行效率较低设计的。往往与其它通信机制,如信号量结合使用, 来达到进程间的同步及互斥。
  5. 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
  6. 套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字。

线程

线程通信方式

  1. 锁机制:包括互斥锁、条件变量、读写锁和自旋锁。
    互斥锁确保同一时间只能有一个线程访问共享资源。当锁被占用时试图对其加锁的线程都进入阻塞状态(释放CPU资源使其由运行状态进入等待状态)。当锁释放时哪个等待线程能获得该锁取决于内核的调度。
    读写锁当以写模式加锁而处于写状态时任何试图加锁的线程(不论是读或写)都阻塞,当以读状态模式加锁而处于读状态时“读”线程不阻塞,“写”线程阻塞。读模式共享,写模式互斥.
    条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
    自旋锁上锁受阻时线程不阻塞而是在循环中轮询查看能否获得该锁,没有线程的切换因而没有切换开销,不过对CPU的霸占会导致CPU资源的浪费。 所以自旋锁适用于并行结构(多个处理器)或者适用于锁被持有时间短而不希望在线程切换产生开销的情况。
  2. 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
  3. 信号机制(Signal):类似进程间的信号处理
  4. 屏障

线程间的通信开发中常用的方式

  1. 全局变量方式 对于标准类型的全局变量,我们建议使用volatile修饰符,它告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变
  2. 参数传递方式 主线程在创建子线程时,可以通过传给线程函数的参数和其通信
  3. 消息传递方式
  4. 线程同步法 synchronize

Java主要线程通信方式

  1. 共享变量来做控制,synchronized加锁的线程的Object类的wait()/notify()/notifyAll()
  2. 共享变量来做控制,ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()
  3. 共享变量来做控制,volatile使用,AtomicInteger,
  4. 通过IO管道进行线程间通信:1)字节流;2)字符流
  5. while轮询的方式
  6. CyclicBarrier,BlockingQueue等

参考