序言
很久没写技术的话题了,所以写写进程与线程,我一直在想,在什么层面来看进程与线程,感觉很底层,感觉毫无价值。。。
进程,线程傻傻分不清楚,其实。。。也是因为没有存在感吧。
进程与线程
1、 进程与线程的定义
为什么要有进程?随着计算能力的增加,原来都是单一的进程运行在CPU上,而现在,需要各种进程运行在CPU上,所谓的多道程序设计,也就是多人多任务的操作系统,进程,只是CPU的一种抽象;进程,是程序的运行时的形态;进程,是用来分配资源的最小单位。
为什么需要线程?在程序运行的时候,启动一个进程,但是对于进程来说,总是会发生相关的IO操作的,从而总是会出现阻塞的情况,那么必然会进行进程的切换,从而要耗费大量的CPU时间,为了更好的提高性能,从而有了线程,可能在一个进程中运行几个线程,一个线程读取用户的输入,一个线程用来读取磁盘,从而能大大提升性能,但是,如果是单纯的计算,那么性能不能增加,相反还会降低。线程是执行单位,也就是真正的一线工作者。
以为有了线程就能提高性能?幼稚。。。线程需要进行上下文切换吗?线程,所谓的轻量级的线程,在使用线程的时候,主要是因为一个进程的时间片没有用完,从而进行线程切换,但是不像进程是重型的切换,只需要保存少量的堆栈信息,而不需要刷新TLB高速缓存,不需要保存各种内存镜像,毕竟。。。线程之间很多的信息总是共享的。
2、 查看进程与线程
在使用线程与进程的时候,使用一段小的例子来演示:
例子的主要目的就是在进程中,一个主线程用来等待两个子线程,啥事都不干,就在那睡觉。。。
查看线程:
可以看到,在查看线程的时候,发现这个程序都是一样的,在多线程中,执行的都是同一片代码,从而执行的命令都是相同的(LWP表示轻量级进程的pid,而NLWP表示的是线程数量有几个)。
查看进程和线程的数量有什么用?
在很多时候,无论是CPU繁忙,表象为load值高,还是io等待高,还是内存使用量多,其实都是和进程和线程脱离不了关系,从而在进行查看这类问题的时候,总是要进行统计这种相关的数据的变化率,例如每隔5秒统计一次线程的数量和进程的数量。
还有一种情况就是,在我们统计进程或者线程的时候,我们应该关注什么样的运行状态的进程,在进程或者线程的运行态中,分为三种,一种是就绪,一种是阻塞,一种是运行,主要关注的指标就是运行的进程数量和阻塞的进程数量,从而在统计的时候,需要用如下的指令。
查看进程:ps h -eo pid,state,cmd,%cpu,%mem |awk '{if($2=="R"||$2=="D") print$0}'|sort |uniq -c |sort -rnk 1
查看线程:ps h -eLo pid,state,cmd,%cpu,%mem |awk '{if($2=="R"||$2=="D") print$0}'|sort |uniq -c |sort -rnk 1
主要表示的意思就是:参数L主要是用来区分线程和进程的统计,h表示不显示header便于统计,-e表示显示所有的进程或线程,-o表示自定义选项,在自定义的时候,psr表示cpu的核心数,这个也是很有意思的,从而可以看到程序正在运行在哪个cpu上,从而可以看看是不是真的多线程,例如python就是假的,同一时刻,有GIL的存在。。。哼,骗子。。
在统计数量的时候,主要关注的事运行态和阻塞态,对于D状态的太多来说,会造成系统的负载一样很高很高。ps -ef可能用了很多年,而并不知道参数的意思,其实和ps aux一样一样的。。。所谓的变化率,其实就看前后的一种对比,可能第一次运行有50个进程或者线程,然后看看cpu,内存,第二次又51个,可以看到哪些是不断增加的趋势,从而可以判断哪个进程导致了系统的负载升高,内存oom等问题。。。
3、 进程的树形结构
进程的组织方式是一棵树,子子孙孙无穷尽也。。不可能的,一个系统中运行的进程数量是指定的。
为什么要看进程树?在查看进程树的时候,我们总是为了追查父进程,例如有一个僵尸进程,那么如何追查?
僵尸进程的产生,是因为父进程没有等待子进程(可能在睡眠状态),从而导致了子进程变成了僵尸,等父进程醒了,那么就可以回收子进程使用的资源了。
以上代码在一个容器里面运行,从而产生一个僵尸进程,如何追查?
在使用命令的时候,使用pstree也是可以的,但是,不好追查相关的进程属于哪个容器。。。一个容器有各种进程。。。
如果僵尸进程越来越多,会让操作系统不堪重负,最终不能启动任何新的进程。。。
4、 查看进程使用的文件
在查看进程的时候,也可以看到进程打开了哪些文件,使用了哪些文件描述符,那么,看这个又有什么意思呢 ?
在很多时候,有的日志被删除,然后要重启进程,是不是很熟悉,所以呢,这种情况下可以找到被删除的日志。
在这个里面可以看到删除的文件的大小,从而可以计算出消失的磁盘空间。。。
5、 杂乱的命令
在查看运行的队列和阻塞队列的时候,使用命令vmstat:
在查看进程状态的时候,也可以使用top,但是top显示的睡眠总数,其实我们要关注的是叫不醒的进程,叫醒了也没用。。。忙等待。。
查看进程使用的IO,可以使用iotop:
竞争条件
其实,以上的命令都很无趣,最有压力的就是竞态条件,所谓的race condition,所谓的内核线程,所谓的用户空间的线程。
内核线程和用户线程都是可以的,一个是内核控制,一个是用户控制,当然,有的是两者的结合,从而有了多路复用,那么怎么看是内核线程,哪个是用户线程呢?
在上面的结果中,这种带有中括号的,就是内核线程。。。
竞态条件,就是所谓的多个进程或者多个线程一起访问同一个临界区,那么并发控制怎么办?你要写入,我也要写入。。。冲突。。。哼,所谓的竞争,简直是FUCK。。。
这就是所谓的锁的由来,在同一时刻,只让一个进程或者线程进入操作。。。也可以用硬件来实现,屏蔽中断?不可能的,只能屏蔽一颗CPU,多颗CPU怎么办?
所谓的并发编程中锁,互斥量,信号量,所谓的TSL,XCHG,管城,所谓的消息传递,都是为了解决这种问题。。。其实这类问题,还是主要是因为产生了多个进程或者线程使用同一个变量或者数据结构,从而导致冲突的产生。。。为何要共享?
共享,居然是为了更好的协作,也就是多个进程在不同的时刻做不同的事儿,从而提高性能。。。
个人还是比较喜欢用户级线程,这样在访问临界区的时候,只要将控制权让出就好了,所谓的pthread_yield。。。自甘奉献的精神?