进程的初步认识
- 前言
- 查看进程
- 通过系统调用创建进程
- 关于创建进程的几点补充
前言
之前的一篇文章(文章链接)已经初步对于进程有了一个认识,这篇文章主要是介绍如何去查看进程的相关信息以及创建一个进程的相关知识
查看进程
查看进程的信息可以在/proc
系统文件夹中查看
当然也可以通过ps
或者top
指令来获取进程的信息,常见的指令就是ps -axj
上一篇文章已经提到如何去利用函数去获取一个进程的id,那么我们就可以通过获取到的进程id去观察我们自己写的代码所对应的进程
首先,先写一个一个简单的代码
然后,我们再根据代码运行起来时打印出来的进程id去/proc
系统文件夹查看对应的进程信息
即使在众多的进程信息中,我们也可以很轻易地发现有两个特别“突出“的两条
exe
其实就是该进程所依赖的可执行程序
cwd
(Current working directory)代表的就是当前工作目录,那么这个作用主要是什么呢?
首先可以回忆以下C语言的文件操作的一些知识,当我们写下以下代码
FILE * pFile;
pFile = fopen ("myfile.txt","w");
其中fopen
的第一个参数是可以填一个绝对路径或者是填一个相对路径的,我们都知道当我们填了一个相对路径时,比如这个例子中的,操作系统就会在当前的目录查找是否有myfile.txt
这个文件,如果没有的话就会在当前目录创建一个
其实,操作系统之所以会在当前目录创建文件其实就是因为该代码所对应进程中包含了相关工作目录的信息,当我们输入一个相对路径的时候,操作系统用这个cwd的信息帮我们将相对路径变成绝对路径从而在对应的地方创建文件
这里还要补充两点,
第一点就是这这个cwd
是可以更改的,要想更改,可以使用函数chdir
(change working directory),
第二点:当一个可执行程序已经跑起来了,现在将这个可执行程序删掉,发现其对应的进程信息还在,并且程序也在继续正常运行
这是因为在运行一个程序的时候,本质是将程序从磁盘拷贝到内存,删除掉磁盘中的文件并不会影响它此次的运行(也就是说当一个程序已经跑起来变成进程的时候,已经和磁盘没有关系了,示例exe出现红色字样只是说明该程序在磁盘中对应的可执行程序被删除了),当然了,当我们退出此次运行后,想要再次运行mybin
这个可执行程序时就无法找到对应的文件了
通过系统调用创建进程
要想创建一个进程,就需要认识一个系统调用fork
,同样的,我们通过man手册去了解一下这个函数该怎么使用
这个函数简单来说就是创建一个子进程,并且值得一提的是,如果创建子进程成功的话,就给父进程返回子进程的id,给子进程返回0,如果创建失败,就返回-1,并且,父子进程的代码共享,数据各自开辟空间
接下来,我们来通过一个代码样例来创建一个子进程
我们可以看到,打印语句一共被执行了两次,通过打印结果,我们不难看出,第一行打印是由父进程执行的而第二行打印是由子进程执行的,父进程得到fork
的返回值就是子进程的id,而由于子进程被成功创建出来了,子进程得到fork
的返回值就是0
通常情况下,我们在fork
创建子进程之后会使用if
语句进行分流,达到让不同的进程执行不同代码的目的
关于创建进程的几点补充
1、为什么fork给父进程返回的是子进程的pid,但是给子进程返回的是0?
因为一个子进程只有一个父进程,但是一个父进程可能有多个子进程,子进程要想找到父进程的pid只需要调用getppid()
方法,但是父进程没有获取子进程的pid的方法,所以将fork函数设计为给父进程返回子进程pid的方法使得父进程可以对自己创建出来的子进程更好地进行管理
2、fork函数为什么会返回两次
pid_t fork()
{
...
...
return id;
}
一般来说,一个函数执行到需要返回数据的时候,这个函数地大部分的工作已经做完了,什么意思呢?也就是说,在fork函数内部,还没有执行到return语句的时候,子进程已经被创建出来了,有句话叫做”fork之后,代码共享“,实际上在fork函数内部子进程被创建出来之后,代码已经被共享了,然后父进程和子进程分别独立去执行return语句,所以给人一种一个函数返回了两次返回值的感觉。
3、返回值ret是怎么做到即大于0又小于0的?
首先我们要知道在父进程和子进程之间的大部分数据其实都是共享的,但是当父/子进程试图对某个共享的数据(比如某个变量的值)做一些修改时,这个时候就会发生写时拷贝,以确保父子进程的修改不会相互影响,因此,在写时拷贝机制下,父子进程之间共享大部分数据,并且只有在实际需要修改共享数据时才会进行复制。这种延迟复制的方式既减少了内存占用,又提高了效率,同时保证了数据的一致性。
话说回来,返回值ret
其实也是父子进程共享的数据,但是当父/子进程要修改这个数据的时候,操作系统就会分别为父进程和子进程对该变量进行写时拷贝,所以,看似只有一个ret变量,其实是有多个ret
变量分别为父子进程所用