本文转载自微信公众号《CS攻略》,作者大白。转载本文请联系CS攻略公众号。大家好,我是大白!准备了好久,我的Linux系列文章终于和大家见面了。最近为了准备linux的文章,没时间写程序员视角的城市系列。最近不仅有读者在催我,一些喜欢我的城市系列文章的号主也在催我,哈哈。现在我的第一篇关于Linux的文章终于写完了。第二篇和第三篇会越来越快,质量会越来越高。希望大家多多支持!面试的时候经常被问进程和线程的区别,你回答面试官一个进程有很多线程,他不是很开心。他嫌你回答太少,想吊死你。本系列我们以Linux中的进程和线程为例,详细说说。今天的文章首先扩展Linux进程是如何创建的。本系列会持续更新,欢迎持续关注!Linux进程是如何创建的?Linux系统创建进程由已有进程(进程0除外)创建。创建的进程称为子进程,创建子进程的进程成为父进程。这句话是不是有点眼熟,没错,Linux进程串起来也是一个树形结构。像这样:在Linux中,创建子进程,父进程使用系统调用fork创建子进程。fork()实际上是父进程的一份副本(子进程有自己的特性,如标识、状态、数据空间等;子进程与父进程共享程序代码、共享时间片等).可以看到如下代码:#include#includeintmain(){intp_num=0;intc_num=0;intpid=fork();if(pid==0)//返回了子进程pid为0{c_num++;}else{p_num++;//父进程返回的pid大于0}printf("p_num=%d,c_num=%d\n",p_num,c_num);printf("pid=%d\n",pid);return0;}//运行结果如下p_num=1,c_num=0pid=36101p_num=0,c_num=1pid=0你看,调用fork之后代码中,后面的程序执行了两次。子进程和父进程的变量互不干扰。但是子进程和父进程执行相同的代码,子进程和父进程的资源使用情况如下图所示:可以看到fork之后,子进程并不独立于父进程过程,但使用相同的代码。此外,还有一个问题。这时候子进程的时间片就一分为二与父进程共享。创建这样的子进程有什么意义?为了将父进程与子进程完全分开,使用了系统调用execv()。看下面的代码://process.c#include#includeintmain(){intpid=fork();if(pid==0){execv("./test.o",NULL);//test.o是编译好的c语言文件,记得把test.o的绝对路径放在这里}printf("Thisisparentprocess\n");return0;}//test.c#includeintmain(){printf("Thisischildprocess");return0;}//运行结果如下自己的代码是的,下面的代码并没有像前面的代码那样执行两次。通过调用execv(),子进程与父进程基本分离。下面结合系统,继续看看Linux的进程树长什么样。通过上面的介绍,大家应该对进程是如何创建的有了一定的了解。如果你想继续学习,让我们继续增加强度。我们在Linux系统上通过ps-ef命令查看系统当前进程:/[root@localhostlucas]#ps-efUIDPIDPPIDCSTIMETTYTIMECMDroot10321:41?00:02:38/usr/lib/systemd/systemd--sroot20021:41?00:00:07[kthreadd]root32021:41?00:00:00[rcu_gp]root42021:41?00:00:00[rcu_par_gp]...rtkit11511021:41?00:00:14/usr/libexec/rtkit-daemonroot11521021:41?00:00:00/usr/sbin/ModemManageravahi11551021:41?00:00:06avahi-daemon:running[linuxroot11591021:41?00:00:02/usr/lib/systemd/systemd-mac让我解释一下table是什么意思。首先,每个进程必须属于一个用户,UID是用户的标识(root用户创建的进程的UID是root,如果是我自己创建的,应该是我的用户名,比如我的名称“大白”)。其次,每个进程都要有一个ID来代表进程,PID代表当前进程的id。最后,如上所述,除了0号进程外,每个进程都是由其父进程创建的,PPID代表当前进程的父进程id。通过0号进程创建1号进程和2号进程,然后1号进程创建用户态进程,2号进程创建内核态进程,生成Linux进程树。“什么是进程0、进程1和进程2?”。进程0:在内核初始化期间,进程0将由命令structtask_structinit_task=INIT_TASK(init_task)创建。这是唯一一个不是由fork或kernel_thread产生的进程。是进程列表中的第一个。但是这个过程并不是实际意义上的过程,类似于链表的表头。所以进程0虽然是在内核态创建的,但不能说进程0是内核态第一个进程,而是进程2是内核态第一个进程。1号进程:通过调用命令kernel_thread(kernel_init,NULL,CLONE_FS)从内核态切换到用户态创建。1号进程是所有用户模式的祖先。进程号2:通过调用命令kernel_thread(kthreadd,NULL,ClONE_FS|CLONE_FILES)创建。2号进程负责所有内核态进程的调度和管理,是内核态所有进程的祖先。(注意内核态不区分线程和进程,所以进程和线程都是任务)“为什么要先创建进程0而不是进程1?”有很多讨论,而不是直接创建进程1和2。我觉得……算了,我不觉得,这篇文章展开了就没完了,以后可以专门写一篇文章来讨论这个。简单的说,linux的第一个进程不适合当真进程,需要一个没有数据的假进程等等。“为什么要区分用户态和内核态?”因为有多个进程,对于关键资源,会出现争用和误操作破坏资源。这时候就需要对资源的访问权限进行一定的限制。x86提供了分级权限机制,内核态拥有最高的访问权限,用户态必须切换到内核态才能访问核心资源。嗯,查了一下字数,这篇文章挺长的。接下来,我会继续分享更多的进程和线程的细节,也会根据读者的反馈,不断完善完成的文章。欢迎大家持续关注!参考资料:[1]Linux进程的创建与管理:https://blog.csdn.net/qq_38410730/article/details/81193118[2]极客时间:《趣谈Linux操作系统》