linux创建进程的命令有哪些

83次阅读
没有评论

共计 3868 个字符,预计需要花费 10 分钟才能阅读完成。

这篇“linux 创建进程的命令有哪些”文章的知识点大部分人都不太理解,所以丸趣 TV 小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“linux 创建进程的命令有哪些”文章吧。

linux 创建进程的命令:1、fork 命令,可以从已存在进程中创建一个新进程,该新进程为子进程,而原进程为父进程;子进程完全复制父进程的资源。2、vfork 命令,创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上。3、clone 命令,可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享。

Linux 系统种创建进程有 fork、vfork、clone 这个三名命令可供使用。

fork

fork 创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的 task_struct 结构和 pid, 但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。所以,这一步所做的是复制。这样得到的子进程独立于父进程,具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,共享内存等机制,另外通过 fork 创建子进程,需要将上面描述的每种资源都复制一个副本。这样看来,fork 是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程 fork 出一个子进程后,其子进程仅仅是为了调用 exec 执行另一个可执行文件,那么在 fork 过程中对于虚存空间的复制将是一个多余的过程。但由于现在 Linux 中是采取了 copy-on-write(COW 写时复制)技术,为了降低开销,fork 最初并不会真的产生两个不同的拷贝,因为在那个时候,大量的数据其实完全是一样的。写时复制是在推迟真正的数据拷贝。若后来确实发生了写入,那意味着 parent 和 child 的数据不一致了,于是产生复制动作,每个进程拿到属于自己的那一份,这样就可以降低系统调用的开销。所以有了写时复制后呢,vfork 其实现意义就不大了。

fork()调用执行一次返回两个值,对于父进程,fork 函数返回子程序的进程号,而对于子程序,fork 函数则返回零,这就是一个函数返回两次的本质。

在 fork 之后,子进程和父进程都会继续执行 fork 调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由 cpu 执行的机器指令,通常是 read-only 的。

vfork

vfork 系统调用不同于 fork,用 vfork 创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。

因此,上面的例子如果改用 vfork()的话,那么两次打印 a,b 的值是相同的,所在地址也是相同的。

但此处有一点要注意的是用 vfork()创建的子进程必须显示调用 exit()来结束,否则子进程将不能结束,而 fork()则不存在这个情况。

Vfork 也是在父进程中返回子进程的进程号,在子进程中返回 0。

用 vfork 创建子进程后,父进程会被阻塞直到子进程调用 exec(exec,将一个新的可执行文件载入到地址空间并执行之。)或 exit。vfork 的好处是在子进程被创建后往往仅仅是为了调用 exec 执行另一个程序,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的,因此通过 vfork 共享内存可以减少不必要的开销。

clone

系统调用 fork()和 vfork()是无参数的,而 clone()则带有参数。fork()是全部复制,vfork()是共享内存,而 clone() 是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的 clone_flags 来决定。另外,clone()返回的是子进程的 pid。

下面详细了解 fork 命令(进程创建)。

深入 fork 函数

在 Linux 中 fork 函数是一个非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

fork 函数的返回值:

给父进程返回子进程的 pid

给子进程返回 0

接下来我们举例使用一下 fork 函数 ()

我们编译,然后运行一下:

fork 的常规用法

一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

一个进程要执行一个不同的程序。例如子进程从 fork 返回后,调用 exec 函数。

fork 调用失败的原因

系统中有太多的进程

实际用户的进程数超过了限制

在重温了一下 fork 函数的使用后,接下来我们来研究一个话题:

fork() 创建子进程,操作系统做了哪些操作?

进程调用 fork,当控制转移到内核中的 fork 代码后,内核做了以下操作:

分配新得内存块和内核数据结构给子进程。

将父进程部分数据结构内容拷贝至子进程。

添加子进程到系统进程列表中。

fork 返回,开始调度器调度。

父进程执行完 fork 之前的代码 (before) 后,调用 fork 创建子进程,父子两个执行流分别执行。注意:fork 之后,谁先执行完全由调度器决定。

这里还有一个问题,当 fork 之后,父子进程代码共享是 after 共享,还是所有代码都进行共享? 为什么子进程总准确地执行 fork 之后对应的代码?

答案:所有代码共享,因为 CPU 记录了进程的执行位置。

代码进行汇编之后,会有很多行代码,而且每行代码加载到内存之后,都有对应的地址。

因为进程随时都可能被中断(可能并没有执行完),下次继续执行时,还必须从之前的位置继续运行(并不是程序最开始或 main 函数处),这就要求 CPU 必须实时记录下当前进程执行的位置。

所以,CPU 内有对应的寄存器数据,用来记录当前进程的执行位置,此寄存器叫做 EIP,也称作为 pc(point code 程序计数器),用来记录正在执行代码的下一行代码的地址(上下文数据)。

当子进程创建时,会修改其 EIP。此时子进程便会认为 EIP 的中保存下的数据,就是要执行的代码。

创建子进程时,操作系统给子进程分配对应的数据结构,子进程独立运行,因为进程具有独立性。

理论上,子进程也要有自己的代码和数据,但是一般而言,创建子进程没有加载的过程,子进程本身并没有自己的代码和数据。

所以,子进程只能 使用 父进程的代码和数据,而代码是只读的,父子共享不会冲突;而数据是可能被修改的,必须进行分离。

这时,操作系统便采用写时拷贝的策略。

写时拷贝

OS 为何采用写时拷贝技术,对父子进程进行分离

写入时再进行拷贝,是高效使用内存的一种表现。

提高了系统的运行效率。

OS 无法在代码执行前预知哪些空间会被访问。

扩展知识:进程终止和

进程终止

当进程终止时,操作系统释放了进程申请的相关内核数据结构和对应的代码的数据,其本质就是释放系统资源,

1、进程退出码

进程终止的常见方式:

代码跑完,结果正确。

代码跑完,结果不正确。

代码没有跑完,程序崩溃了。

区分第一种情况和第二种情况我们可以通过进程的退出码很清晰的辨别。

关于学习的 C 语言中 main 函数的返回值,其中 main 函数的返回值就是进程的退出码。其意义是返回给上一级进程,用来评判该进程执行结果。

现在我们编写一个简单的 C 程序。

然后我们可以通过 echo $? 获取最近一个进程的退出码。

进程的返回值有 0 和非 0 两种情况,其中 0 表示程序成功运行并结果正确,而非 0 表示成功运行但结果有误,非零值有无数个,不同的非零值就可以就表示着不同的错误,方便我们定义错误的原因。

那常见的错误信息有哪些呢?

我们可以使用 strerror 将其打印出来

结果如下:

发现 linux 下,共有 133 条错误码

当然,程序崩溃的时候,退出码没有意义。

众所周知,Linux 是用 C 语言写的,其中命令本质就是 C 语言程序,所以我们可以简单的拿 ls 命令来举例

而 2 号退出码对应的报错信息:

linux 创建进程的命令有哪些

2、exit 与 _exit

关于终止一个进程可以使用 return 语句,还可以调用 exit 和 _exit 函数

exit 函数:

linux 创建进程的命令有哪些

_exit 函数:

linux 创建进程的命令有哪些

关于这两个函数的区别有很多,我们先举一个小例:

我们接下来使用 printf 打印一条信息,然后 sleep 三秒,再使用 exit 退出,并观察结果

linux 创建进程的命令有哪些

因为我们带上了 \n,加上 \n 会刷新缓冲区,屏幕上即出现我们打印的内容。

linux 创建进程的命令有哪些

如果我们不带上 \n,我们再观察结果:

linux 创建进程的命令有哪些

发现:因为没有 \n,所以 printf 中的内容并没有在休眠前被打印出来,而是调用 exit 后将缓冲区的内容刷新出来输出在屏幕上。

接下来我们使用 _exit 函数

linux 创建进程的命令有哪些

运行可执行文件 b:

linux 创建进程的命令有哪些

发现什么内容都没有被打印出来,使用 echo $? 打印最近进程退出码,发现 b 文件确实被执行了。

这说明,exit 是库函数,而_exit 是系统调用,其退出进程时并没有刷新缓冲区中的内容。

linux 创建进程的命令有哪些

此时我们便能得出一个结论:

printf 数据是保存在 缓冲区 中的,exit 可以将其刷新,而系统调用接口_exit 不能将其刷新。所以,缓冲区必定不在操作系统内部,而是由 C 标准库维护的。

以上就是关于“linux 创建进程的命令有哪些”这篇文章的内容,相信大家都有了一定的了解,希望丸趣 TV 小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-12发表,共计3868字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)