欢迎来到博主的专栏:从0开始linux
博主ID:代码小豪
文章目录
- 查看进程
- 进程管理
- PID与PPID
- fork函数
在上一篇中我们了解到:当运行程序时,操作系统会将磁盘中的二进制文件读取到内存当中,程序运行到结束的过程称为进程,并且操作系统使用一个task_struct的结构体来管理进程。
查看进程
使用ps ajx
指令可以查看当前的进程信息。
由于linux需要运行大量的系统程序,所以直接查看全部进程的信息并不明智,因此最好使用检索特定命令的指令ps ajx | grep [name]
,这里的|
是管道,作用是将管道前面的指令的运行结果,交给管道后的指令去执行,比如ps ajx的结果是打印当前的全部进程,而grep的作用是搜索关键词,因此这个指令的意义是:在全部进程中检索[name]的程序的结果打印出来。
比如我们查看bash程序的进程信息,则输入
ps ajx | grep bash
但是这样做虽然能看到指定的进程的信息,但是我们由于看不到进程信息的对应属性,因此并不知道进程信息代表的意思是什么,因此博主推荐使用一个组合命令:ps ajx | head -1;ps ajx | grep [name]
。分号的作用是将多个指令的结果同时执行。因此,我们输入:
ps ajx | head -1; ps ajx | grep bash
之所以这段指令可以看到进程信息的对应属性,是因为进程属性在ps ajx 的结果的第一行,因此现将第一行的信息打印,就能看到进程信息的对应属性了。
进程管理
既然说二进制文件运行之后就会变成进程,那么我们来尝试写一个简单的代码。
我们将这段代码编译生成的二进制文件命令为hello_world。然后将其运行,接着输入
ps ajx | head -1;ps ajx | grep hello_world
此时奇怪的事情发生了,hello_world竟然没有被搜索到,上面只有一个grep --color=auto hello_world的进程,因为执行的指令也算进程。
那么这个hello_world去哪了呢?博主刚刚才提到,指令也算进程,那么这是否意味着,ls指令也能在进程管理中看到呢?事不宜迟,我们赶紧测试一下
可以发现,ls也是查看不到的,,那么为什么ls和hello_world都属于是进程,但是我们却看不到呢?其实原因很简单,因为一个进程,其实是一个程序从运行到结束的整个过程,而无论是hello_world,还是ls指令,它们的执行的过程其实非常短,短到我们难以通过进程管理去看到它们就已经结束了运行,像这种瞬时的进程我们是很难观察到的。
PID与PPID
既然瞬时的进程我们看不到信息,那么我们修改一下代码让其进入死循环呗,修改的代码如下:
接着我们运行代码,并且打开另外一个终端来查看查看进程。(ps:linux运行同一个用户使用不同的命令窗口,如下,博主使用的是同一个用户来执行不同的指令)
我们让其中一个窗口运行程序,另外一个窗口输入ps ajx | head-1 ;ps ajx | grep hello_world
现在,我们已经知道如何查看进程的状态了,但是关于这些状态的属性代表什么意思却不太了解,我们先来认识一下pid和ppid。
pid,英文全称为:Process Identifier,也可称为进程标识符,顾名思义,pid就是一个进程的标识符,当一个程序运行时,系统会为其进程自动分配一个进程标识符。
pid的类型是符号型整数,在linux c中,通常用pid_t类型的变量来接收pid的值。
getpid函数
getpid函数被包含在头文件<unistd.h>当中,该头文件中的函数通常都是与系统调用相关的函数,并不适用于windows系统。
getpid,返回调用该函数的进程的ID,比如,我们写下如下的代码:
编译该代码,并让其生成二进制文件testproc,接着运行代码,并且查看一下testproc程序的进程信息。
可以发现,程序中getpid的返回值,与testproc进程的实际pid一致,因此getpid的作用就是获得当前程序的pid。
每个进程都有其对应的pid,而进程与pid的关系有点像指针与变量,很多时候,我们要对进程进行管理时,都是使用pid,而非进程本身的名字。
关闭进程
关闭进程需要用到kill
指令,kill指令本身不单单能关闭进程,还有许多关于管理进程的选项,我们输入kill -l
可以看到与kill相关的所有选项
想要在一篇中,讲完kill的全部选项不太现实,因此博主将会在与进程相关的文章中,一边用,然后一边讲这些选项所代表的操作。
想要关闭进程,我们就要用选项(9),这个选项能让进程强行被关闭。具体指令如下:
kill -9 [pid]
现在,我们继续运行testproc程序。
现在我们在另一个窗口中输入:
kill -9 11759
可以发现,程序停止了,并且还有killed的信息以表示该进程已被停止,像这种强行把进程关闭的行为,我们称之为:杀进程。
** ppid**
在linux中,ppid指的是父进程的pid,即父进程的pid。当一个进程被创建时,创建它的那个进程被称作父进程。使用“ps ajx | grep [name]”命令来查看。
我们再次运行testproc程序,然后在另一个窗口当中使用ps ajx |head -1;ps ajx | grep testproc
查看testproc的进程信息。
那么testproc的ppid是testproc的父进程的pid,那么我们就可以用ppid去看看testproc的父进程是谁。指令如下:
ps ajx | head -1 ; ps ajx | grep 11674
这里发现,原来testproc的父进程是bash,至于为什么是bash博主先不谈,因为这涉及其他方面的知识,我们后面再聊。但是现在我们知道了,如果一个进程的ppid,其实就是其父进程的pid。
get ppid函数
get ppid函数可以返回当前进程的ppid。包含在头文件<unistd.h>当中,这个函数很简单,博主就不再展示它的用法了,和getpid的使用方法是一样的。
fork函数
好了,我们现在知道了父进程与子进程之间的关系,但是我们还没怎么见过父进程与子进程,但是没事,博主给大家介绍一个fork函数,这个函数能展示linux的父子进程之间的关系,还能展示linux系统的多线程功能。
我们打开man手册看看fork函数是怎么描述的
fork包含在头文件<unistd.h>当中。
Fork()通过复制调用进程来创建一个新进程。这个新进程,被称为子过程,该子进程是父进程的一份拷贝,子进程与父进程都会从代码的同一位置开始执行线程。
这段话该怎么理解呢?我们以一个例子举例。
接着我们运行代码。
可以发现,运行这段代码多一个未知进程的pid,根据fork函数当中的描述,我们可以大胆的猜测这个新的进程就是fork出来的子进程。我们修改源代码,让进程将各自的ppid打印出来。
到这里就很明显,当程序执行到fork时,会产生一个子进程,子进程与自己拥有一样的代码,但是数据不互通,而该子进程会从fork的位置开始继续运行程序。
关于fork的介绍就到此结束了吗?当然没有,我们还没看返回值呢。
如果fork成功创建了子进程,那么fork向子进程返回值为0,向父进程的返回值为子进程的pid,如果创建失败,则父进程的返回值为0,没有子进程生成。
利用这个特性我们就能让fork出来的子进程执行与子进程不同的事情。例子如下:
int main()
{
pid_t id=fork();//如果是父进程,则fork的返回值是子进程的pid
//如果是子进程,则返回值为0
if(id>0)//父进程执行
{
printf("i'm parent process pid:%d\n",getpid());
}
else if(id==0)//子进程执行
{
printf("i'm child process pid:%d;ppid%d\n",getpid(),getppid());
}
else if(id==-1)//创建子进程失败执行
{
printf("fork fail\n");
}
sleep(1);
return 0;
}
可以发现,利用fork的返回值,可以让父进程与子进程之间虽然代码共享,但是利用分支的关系,可以让子进程与父进程的运行过程不同。