您的位置:首页 > 运维架构 > Linux

Linux应用程序学习之进程函数

2015-06-12 10:36 429 查看
Linux应用程序学习之进程学习
Linux中进程的概念非常重要,其涉及到的知识也非常有用,希望能在此做一个抛砖引玉的作用,来让我们深入学习下linux。

进程是一个具有一定独立功能的程序的一次运行活动,其主要特点包括动态性、并发性、独立性和异步性。我们常见的进程包括三种状态,分别是就绪态,运行态,以及阻塞态,进程在三个状态之间相互转化。在linux系统中,我们用进程ID来标识一个进程,在我们创建一个进程的时候,系统就会为这个进程分配一个唯一的标识符,且为非负数,因此通过进程ID我们就可以操作一个进程了。Linux系统为我们提供了许多获取进程ID的函数,我们就简单看看下面两个

pid_t getpid(void); //返回调用进程的进程ID

pid_t getppid(void); //返回调用进程的父进程ID

有了函数名,我们就可以使用man命令去查看其相关的详细描述了,由于这个比较简单,就不再举例了,把重点放在我们下面要学习的函数中了。

(1)创建进程函数fork

调用fork函数可以创建一个子进程,在此我需要详细介绍一下这个函数,因为它对我来讲真的有点陌生,所以我在此尽可能把其详细信息列举出来

函数原型:

pid_t fork(void);

函数功能:

创建一个子进程

函数所属头文件:

#include <unistd.h>

函数返回值:

成功:返回子进程的ID号,同时在子进程中返回0

失败:返回-1

看到此大家都很疑惑其返回值到底是什么意思,下面我们就用一个简单的程序来说明一下:

#include<stdio.h>

#include<unistd.h>

voidmain()

{

fork();

printf("theprocess is end !\n");

exit(0);

}

在linux中运行这个程序,发现其打印了两行

the process is end!

这到底是为什么内?下面让我们到linux命令行下面man fork来自行查看其详细描述:



这段话翻译过来就是fork函数通过复制调用该函数的进程来创建一个子进程,这个子进程是精确的复制了其父进程的内容,除了…,大概就是这个意思,总之我们知道了,在运行有fork函数的程序中,至少有两个进程要出现了,首先是我们调用fork函数的程序作为父进程,然后调用fork函数又创建了一个子进程,之所以返回值有两个就是这个原因,父进程返回子进程的进程ID,而子进程返回0.

至于后面程序中之所以打印两个值,是因为fork创建一个子进程后,该子进程获得父进程的数据空间、堆和栈的副本,注意是副本,因此父子进程并不共享这些存储空间部分,其只是共享正文段。fork函数创建子进程后,由于共享正文段,所以都会继续执行fork调用函数之后的代码(如果子进程执行fork前的代码,则程序将无休止执行下去,这是不合理的),因此就会有两条打印信息,只是谁先被执行,则是不确定的。

(2)创建进程函数vfork

这里真没有写错,在linux中我们还提供了另一种创建进程的函数就是vfork了,该程序在原型和返回值都与fork相同,只是vfork并不将父进程的地址空间完全复制到子进程中,并且vfork函数可以保证子进程优先运行,在他调用了exec或者exit之后父进程才能被调度运行。下面我们看一个程序,猜一猜其输出是什么:

#include<unistd.h>

#include<stdio.h>

void main(void)

{

pid_t pid;

int count = 0;

pid = fork();

count++;

printf(“count = %d \n” , count);

exit(0);

}

大家可以先运行一下看看,这个程序打印出

count = 1

count = 1

两条信息,有关为什么是两条前面已经为大家介绍过了,至于两个都是1,而不是1,2是因为fork函数创建的子进程拥有独立的数据段和栈,因此其改变的是自己的count,父子栈中的内容互不影响,所以都是1,那么我们把这个程序中的fork函数改为vfork函数回事怎么样的内?前面介绍了vfork函数创建的子进程没有独立的数据段和栈,其使用的是父进程的存储空间,所以count变量是共用的,因此会打印出1,2这两个值。

(3)进程退出函数exit

在main函数中调用exit与调用return是一个效果,因此在父进程中我们一般可以采用这两种退出方法,但是子进程则只能采用exit函数来退出,exit函数中的参数意义为:如果为1表示是异常退出,为0表示正常退出,想详细了解该函数可在linux下man进行查看。

(4)进程等待函数wait

Linux中的进程等待函数原型为

pid_t wait(int *status);

这个函数的作用是阻塞调用它的进程,直到一个子进程终止。如果一个子进程已经终止,并且是一个僵死进程(一个已经终止、但是其父进程尚未对其进行善后处理的进程被称为僵死进程),则wait立即返回并且取得该子进程的状态。

这个函数的参数如果不是空指针,则终止进程的终止状态就存放在它所指向的单元内,如果我们不需要了解其状态,则可将该参数指定为空指针,好像我们前面的time函数也有这个特点。

下面我们就用一个程序来感受下这个函数特点:

#include <sys/types.h>

#include <stdio.h>

#include <sys/wait.h>

#include <unistd.h>

void main(void)

{

pid_t pid;

pid = fork();

if(pid > 0)

{

// wait(NULL);

printf("this is fatherprocess\n");

return 0;

}

else

{

printf("this is child process\n");

exit(0);

}

}

在这个程序中,我们将wait函数先注释掉,则其打印结果我们在前面也提到了,即

this is father process

this is child process

但是如果加入了wait函数后,结果又会怎么样?由前面的分析我们可以知道,加入wait函数后,父进程会阻塞,此时会去执行子进程,因此我们先打印子进程消息,等到子进程退出后我们再打印父进程消息,因此打印输出为

this ischild process

this isfather process

大家可以自行在linux中运行看看结果,这样对wait函数的功能也能更加直观感受。

(5)执行函数exec

进程执行函数应该是一个家族函数,其实它包括下面好几个函数

intexecl(const char* path,const char *arg,...);

intexecle(const char *path,const char *arg ,…, char *const envp[]);

intexecv(const char *path , char *const argv[]);

intexecvp(const char *file , char *const argv[]);

int execlp(const char *file,const char&arg,…);

这五个函数都是如果出错,则返回-1,成功则不返回值,在这五个函数中,前三个函数的第一个参数都是一个路径名,而后两个函数参数则为文件名。至于第二个参数其为一个参数参数序列,我们必须在最后补一个NULL空指针来作为结尾。这几个函数的功能是当进程调用一种exec函数时,该进程执行的程序完全替换为新进程,而新程序则从其main函数开始执行。由于调用exec并不创建新进程,所以前后进程ID并未改变,exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。这段功能描述可能有点不好理解,下面我们就来举一个程序例子来说明:

#include <stdio.h>

#include <unistd.h>

void main(void)

{

pid_t pid;

pid = fork();

if(pid > 0)

{

printf("this is fatherprocess\n");

return 0;

}

else

{

execl("/bin/ls" , "ls" ,"/home" , NULL);

printf("this is child process\n");

}

}

这段程序,我们现在已经见过很多次了,唯一的改变是在子进程中加了一个execl函数,如果不加如这个函数,我们知道会打印两句话,但是加入这个之后,运行程序,程序打印出如下信息



先是打印出this is father process,然后将/home目录下的文件都列举出来了,再看看原程序和我们对exec族函数的的解释,是不是会有所感觉?单就这个程序来说,我采用execl函数就是为了执行linux下的ls命令的功能,其第一参数就是列出其所在路径,第二个参数就是这个命令的名字,第三个参数以及之后的参数就是这个命令之后需要跟的参数,这里就是我们ls后面需要跟的参数,我们在此只是跟了一个参数,也就是/home,所以整个执行下来就是ls
/home了,所以会打印出home目录下的文件,至于最后一个参数一定是以NULL来表示结尾的。那么另一个问题就是为什么子进程的那个printf没打印出来?我们前面说过,exec族函数,会完全替换掉调用它的进程,从新程序的main函数开始执行,我们上面的程序的新程序就是ls这个命令的相关程序了,所以子进程调用exec后,就只是执行了ls这个命令程序了,所以才会打印出这中结果,如此我们应该稍微理解了吧。

有关其它几个函数的用法,我们可以按照自行编程测试一下,原理都差不多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: