目录
一、并发与并行
1、并发
对于单个CPU,在一个时刻只有一个进程在运行,但是线程的切换时间则减少到纳米数量级,多个任务不停的来回快速切换。
2、并行
对于多个CPU,多个进程同时运行。
3、区别
通俗的讲,他们虽然都说是“多个进程同时运行”,但是他们的“同时”不是一个概念。并行的“同时”是同一时刻可以多个任务运行,并发的“同时”是经过不同线程快速切换,使得看上去多个任务同时都在运行的现象。
二、进程、线程、协程
1、进程
程序是指令、数据及其组织的描述,而进程则是程序的运行实例,包括程序计数器、寄存器和变量的当前值
2、线程
微进程,一个进程里更小的执行单元。一个进程里包含多个线程并发的执行任务。
3、协程
协程是微线程,在子程序内部执行,可在子程序内部中断,转而执行别的子程序,在适当的时候再返回来接着执行
4、区别
(1)线程与进程的区别
(a)一个线程属于一个进程;一个进程可以包含多个线程;
(b)一个线程挂掉,对应的进程挂掉;但一个进程挂掉,不会影响其他进程;
(c)进程是系统资源调度的最小单位;线程是CPU调度的最小单位;
(d)进程系统开销显著大于线程开销;线程需要的系统资源更小;
(e)进程在执行时拥有独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组;
(f)通信方式不一样。
(2)线程与协程的区别
(a)协程执行效率极高,协程没有内核切换的开销,上下文切换非常快,切换的开销比线程更小;
(b)协程不需要多线程的锁机制,因为多个协程从属于一个线程,不存在同时写变量冲突,效率比线程高;
(c)一个线程可以有多个协程。
三、Linux中如何创建子进程
fork函数用来创建一个子进程。对于父进程来说,fork函数返回新创建的子进程的PID,对于子进程来说,fork函数调用成功会返回0,创建错误会返回-1.
fork函数创建一个新进程后,会为这个新进程分配进程空间,将父进程的进程空间中的内容复制到子进程的进程空间中,包括父进程的数据段和堆栈段,并且和父进程共享代码段。这时,子进程和父进程一摸一样,都接受系统的调度。因为两个进程都停留在fork函数中,最后fork函数会返回两次,一次在父进程中返回,一次在子进程中返回,两次返回值不一样。
四、孤儿进程、僵尸进程、如何解决僵尸进程
1、孤儿进程
当一个父进程退出后,而他的一个或者多个子进程没有退出,那么这些子进程将会变成孤儿进程。
孤儿进程会被init进程(进程号为1)所收养,并且由init进程对他们完整的状态进程收集。
2、僵尸进程
一个进程使用fork函数创建子进程,如果子进程退出,而父进程没有调用wait()或者waitpid()系统调用取得子进程的终止状态,那么子进程的进程描述符仍然要保留在系统中,占用系统资源,这种进程叫僵尸进程。
3、如何解决僵尸进程
(1)使用wait或者waitpid系统调用
一般为了防止产生僵尸进程,在fork子进程之后我们都要及时使用wait系统调用;同时,当子进程退出的时候,内核一般都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait(或者waitpid),就可以清理退出的子进程以防达到防止僵尸进程的目的。
(2)使用kill命令
打开终端并输入以下命令:
ps aux | grep z
这样就会列出进程表中所有僵尸进程的详细内容
然后输入命令:
kill -s SIGCHLD pid(父进程pid)
五、进程间通信的方式
1、管道
(1)无名管道:半双工,只能用于具有亲缘关系的进程直接的通信(父子或者兄弟),可以看作是一种特殊的文件;
看下面这篇文章,写的非常详细
(2)有名管道:允许没有亲缘关系间的进程进行通信
看下面这篇文章,写的非常详细
2、系统IPC
(1)消息队列:消息的连接表,放在内核中。消息队列独立于发送与接受进程,进程终止时,消息队列及其内容并不会被删除;消息队列可以实现消息的随机查询,可以按照消息的类型读取;
(2)信号量semaphore:计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步。
(3)信号:用于通知接收进程某个事件的发生
(4)内存共享:使多个进程访问一块内存空间
3、套接字socket
用于不同的主机之间
六、线程间通信
1、临界区
每个线程中访问临界资源的那段代码称为临界区,一次仅允许一个线程使用的共享资源,每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源还是软件临界资源,多个线程必须互斥的对他进行访问。
2、互斥量
采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,可以保证公共资源不会被多个线程同时访问。
3、信号量
计数器,允许多个线程同时访问同一个资源
4、条件变量
通过条件变量通知操作的方式来保持多线程同步。
5、读写锁
读写锁与互斥量类似。但互斥锁要么是锁住状态,要么是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样的效率比互斥锁更高。