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

来源:互联网 发布:淘宝买账号 编辑:程序博客网 时间:2024/05/23 00:07

摘要: 总结了进程控制理论,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函数族中,后缀lvpe指定函数将具有某种操作能力:

后缀

操作能力

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; }

    运行结果如下:

    其余的就不一一做实验了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!

0 0
原创粉丝点击