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

linux应用编程笔记(8)多进程程序设计

2015-10-13 16:19 281 查看
摘要: 总结了进程控制理论,getpid的用法,多进程程序设计中创建进程,等待进程,退出进程,fork和vfork的区别,exec函数族的用法,每个总结给出一个实例加深理解。

一、进程控制理论

    关于进程的互斥,同步,竞争,死锁,调度策略,优先级等,之前有一篇帖子已经介绍了,链接地址如下:

    http://blog.csdn.net/deep_l_zh/article/details/48346287
    这里介绍一个函数,就是getpid:

    函数原型:pid_t getpid(void)

    函数作用:返回调用该函数的进程的id

    头文件:include <sys/types.h> include <unistd.h>

    参数:无

    返回值:调用该函数的进程的id

例子:example1.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

pid_t pid1,pid2;

void fun(void)
{
pid2=getpid();
printf("process2 id is:%d\n",pid2);
}

int main(void)
{
pid1=getpid();
printf("process1 id is:%d\n",pid1);
fun();
return0;
}
得到的结果如下:
#process1 id is:29045
#process2 id is:29045
    可以看到我们得到了进程的pid号,虽然是在不同的函数里面调用的,为什么打印的pid号是一样的,这是因为他们属于同一个进程!

二、多进程程序设计

1.创建进程fork

    函数原型:pid_t fork(void)

    函数作用:创建一个子进程

    头文件:include <unistd.h>

参数:无

    返回值:成功:父进程中返回子进程的pid

                  子进程中返回0

                  失败:父进程中返回-1.子进程不会被创建

<span style="font-size:18px;">例程:myfork.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
pid_tpid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
printf("thisis child process!%d\n",pid);
exit(0);
}

return0;
}
输出结果如下:
#thisis father process!30339
#thisis child process!0</span>
    可以看到打印出来了父进程和子进程的pid号,这里fork之后,子进程从该处开始和父进程共用代码段,也就是为什么fork之后,紧接着一个printf会打印两条相同的语句,一个是父进程执行的,一个是子进程执行的。这里我们通过判断pid的大小来判断是父进程还是子进程,虽然是if…else,但是两个分支里面的语句都被执行了,这说明子进程在运行。

2.创建进程vfork

    函数原型:pid_t vfork

    函数作用:创建一个子进程并且阻塞父进程

    头文件:include <sys/types.h> include <unistd.h>

    参数:无

    返回值:成功:父进程中返回子进程的pid

                  子进程中返回0

                  失败:父进程中返回-1.子进程不会被创建

例程:myvfork.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
pid_t pid;
pid=vfork();
if(pid>0)
{
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
sleep(2);
exit(0);
}

return0;
}
运行结果如下:
#this is child process!0
#this is father process!3717
    但是这里有一个过程需要注意,红色字体是标出来需要注意的,虽然程序和上一个很类似,我们在子进程当中让程序睡眠了2S钟,所以在运行的时候,子进程先打印,然后睡眠2S,这时候父进程才打印出了相关信息,这是vfork和fork一个不同之处:子进程先执行,在子进程执行结束之前,父进程是阻塞的,子进程退出了,父进程在继续执行!

三、for和vfork的不同之处

    1.fork:子进程拥有独立的数据段,堆栈。

        vfork:子进程与父进程共享数据段,堆栈。

    2.fork:父、子进程的执行次序不确定。

        vfork:子进程先运行,父进程后运行。

<span style="font-size:18px;">例程:dif.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
intcount=0;
pid_tpid;
pid=fork();//vfork()

count++;
printf("%d\n",count);

exit(0);

return0;
}</span>
    当红色字体部分为fork时候,输出两个1,当为vfork的时候,输出了1和2,这就是因为当我们fork的时候,子进程复制了父进程的数据段和堆栈,因此他们是各自在count=0的基础上+1,而vfork创建的子进程和父进程共同数据段和堆栈,所以count被加了两次输出1和2。

三、进程退出

    1.一般正常情况下退出:exit(0),异常情况退出exit(1).

    2.父进程可以使用return 0和exit(0)来退出,子进程只能使用exit(0),不能使用return 0来退出。

四、进程等待

    函数原型:pid_t wait(int *status)

    函数功能:挂起调用它的进程,直到其子进程运行结束

    头文件:include <sys/type.s> include <unistd.h>

    返回值:成功:返回被终止的子进程的id

           失败:返回-1

    参数说明:status记录子进程退出时的状态,可以设置为NULL。

例子:mywait.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
wait(NULL);
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
exit(0);
}

return0;
}
程序运行结果如下:
#this is child process!0
#this is father process!9802
    可以看到父进程等待子进程退出之后才继续执行。

五、exec函数族

    之前的fork,它在创建子进程之后,子进程和父进程共用数据段和堆栈,得到的是一个父进程的拷贝,那么我们fork有什么意义呢?这时候可以配合exec函数族,在fork后的子进程中使用exec函数族,可以装入和运行其它程序,这样子进程替换原有进程,和父进程做不同的事。

    1.exec在替换了原有的进程之后,还是会使用调用它的子进程的pid。

    该函数族如下:

    (1)int execl(const char*path, const char *arg, ......);

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

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

    (4)intexecve(const char *filename, char *const argv[], char *const envp[]);

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

    (6)intexeclp(const char *file, const char *arg, ......);

    exec函数族装入并运行程序path/file,并将参数arg0(arg1, arg2,argv[],
envp[])传递给子程序,出错返回-1.
在exec函数族中,后缀l、v、p、e指定函数将具有某种操作能力:
后缀
操作能力
l
希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
v
希望接收到一个以NULL结尾的字符串数组的指针
p
是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
e
函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
    2. 换句话说,exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

    3.与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

    4.在平时的编程中,如果用到了exec函数族,一定记得要加错误判断语句。先判断execl的返回值,如果出错,可以用perror( )函数打印出错误信息。

例程:myexec.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
if(execl("/bin/ls","ls","-a",NULL)==-1)
{
perror("execlerror!");
exit(1);
}
printf("thisis child process!%d\n",pid);
exit(0);
}

return0;
}
    运行结果如下:



    其余的就不一一做实验了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息