设为首页 收藏本站
查看: 965|回复: 0

[经验分享] Linux中的进程

[复制链接]

尚未签到

发表于 2018-5-19 07:22:40 | 显示全部楼层 |阅读模式
  说进程之前要先说一下程序,相信大家都知道什么是程序,程序就是一个可执行文件,是一堆指令的集合。相对而言程序是静态的。而运行起来的程序就是进程,是动态的,是程序执行的过程。程序可以运行多次比如QQ可以启动多个,但是每一个都会在内存中有独立的隔离空间用于装载程序代码和数据。
  

  我们通过ps命令可以查看Linux系统中当前运行的进程
ps -aux   DSC0000.jpg
  具体字段请看 Linux系统的监控命令
  这里面每一行都是一个进程,PID是进程号,后面的COMMAND这是具体进程或者说是程序名称。第一行永远都是PID 1的系统init进程,这个是开机内核创建的,它会一直运行直到关机,在它产生的初期是在内核态运行,然后通过系统调用,执行/sbin/init程序,从内核态切换到用户态。以后所有的用户进程都是由这个进程派生出来,它是所有用户进程的父进程,也就是说Linux内核并不直接建立用户进程。
  那用户运行程序是怎么变成进程的呢?其实都是init进程通过调用fork函数复制一个自己,然后去exec最终要执行的程序。
  

top #默认按照进程来查看
DSC0001.jpg

top -H  #按照线程来查看   DSC0002.jpg
top -H -p PID  #查看进程中跑了多少线程,其实也就是这个进程派生出来的线程   DSC0003.jpg
  

  关于fork:
  fork的作用就是复制一个与自己一样的进程,新进程的变量、参数等都和原来的一样,但是它是一个全新的进程,并且作为原来进程的子进程。
  可是通常情况下复制了自己以后就马上执行exec函数集合,调用可执行程序来取代调用该执行程序的进程内容,这就会导致原来复制的数据都白费了。所以在fork复制的时候采用了写时复制技术,也就是新的子进程和父进程都有独立的虚拟内存地址,不过这个虚拟地址都指向相同的物理地址,这就保证了它们有独立的逻辑空间,如果子进程需要修改数据的时候,在为其分配独立的物理空间,然后在把数据复制过去。但是要注意它并不是所有的东西都是用到才复制,比如虚拟地址空间结构(mm结构)、父进程的页表信息都是实实在在复制过去的,因为任何进程都是task_struct结构的实例。
  所以fork就是复制一个和自己一样的进程并作为自己的子进程存在,然后由子进程去调用真正需要执行的程序。这就要用到exec函数集合。
  还有一个叫做vfork,只要是跟内存有关的东西都不复制了,父子进程内存完全共享。为了避免共同操作同一个栈,当子进程生成以后,父进程就被挂起,直到子进程调用了exec函数并有了自己独立的空间或者子进程退出。如果使用vfork然后马上执行exec的话效率会比用fork要高。和fork相比,vfork节省的最大开销就是对页表的拷贝。
  说明:进程都是task_struct这个数据结构的实例,这个也被称为进程描述符它记录了进程的上下文,其中有一个叫做内存描述符的数据结构(mm_struct)它描述了进程地址空间的所有信息,它包括代码段、数据段等。每个进程都有自己独立的mm_struct。即使是fork一个进程,子进程也有独立的task_struct并且有独立的mm_struct,只是它的虚拟地址空间映射到了父进程的物理地址空间而已,如果是vfork则完全不用,父子使用相同的虚拟地址空间,当然也就共享同样的物理地址空间。
  

  fork()函数的返回值说明:

  
返回值
说明
PID值
说明当前在父进程中
0
说明当前在子进程中
负值
出现错误
  

  关于exec函数集合:
  exec函数集合的作用就是根据指定的文件名找到可执行文件(使用系统环境变量或者接收一个传递过来的环境变量),并用这个可执行文件取代调用进程(调用进程就是调用该exec函数集的进程,理解为上面提到的子进程)的内容。
  exec函数执行成功后不会返回信息,因为调用进程(子进程)的实体包括代码、数据和堆栈都是存在的,只是被新的可执行程序所取代,只有进程ID还是和原来一样(子进程和父进程的ID可是不同的,fork一个进程就会为其分配一个独立的ID号),你可以理解为把一个程序装入到子进程中,所以不能算是全新的创建;但是如果执行失败会返回-1.
  

  
  exec函数集合,如下:
  arg为列表参数
  int execl(const char *path, const char *arg, ...);    path是可执行程序的完整路径
  int execlp(const char *file, const char *arg, ...);   file是可执行程序的文件名,无论是path还是file都会自动使用系统当前环境变量
  int execle(const char *path, const char *arg, ..., char * const envp[]); envp是接收自定义的环境变量来找可执行文件
  arg为数组参数
  int execv(const char *path, char *const argv[]);
  int execvp(const char *file, char *const argv[]);
  int execve(const char *path, char *const argv[], char *const envp[]);  envp是接收自定义的环境变量来找可执行文件
  上面只有execve是真正会被执行的,其他都是经过了不同封装因为传递的参数会有不同,但是他们最终也是要执行execve的。


  再说回进程,当子进程退出时,会通知父进程,让父进程来清理自己的内存空间,并在内核里留下自己的退出信息,如果退出正常完成该code就是0,如果不正常就是大于0的整数。父进程调用wait函数清理子进程使用的内存空间,并且可以获取到子进程的退出信息。如果子进程没有退出,而父进程退出了呢?那么子进程就会被init进程所接管,成为子进程的新父进程,而之前退出的父进程是init进程的子进程,所以init就会调用wait函数去清理其使用的内存然后获取退出信息。
   DSC0004.jpg
  从上图我们可以看到进程的树形结构init在最顶层。
  

  Linux中到底有没有线程概念呢?如果你理解的线程是Windows平台下的那种线程的话,那在Linux中则没有。确切的说Linux有的就是进程,而没有真正的线程,它的线程其实就是一通特殊的进程而已。fork出来的进程你也可以说它是线程,不过跟父进程比,它是更加轻量级的。那Linux中的线程库是干嘛的呢?那个只是用来模拟线程操作而已。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-461849-1-1.html 上篇帖子: linux tomcat service 下篇帖子: GNU和Linux的关系
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表