操作系统实践(基于Linux的应用)多进程程序开发

1、进程概念

2、进程的创建

​ 进程在执行过程中,可以通过系统调用的方式创建多个新进程。Linux系统提供了两种创建进程的方式:函数fork()用来创建一个新的进程,该进程几乎是当前进程的一个完全副本;函数exec()用来启动另外的进程以取代当前运行的进程。在这里,本章主要介绍如何利用fork函数创建新进程以及fork函数的工作机制。

1、fork函数说明

fork函数原型:pid_t fork( void );

头文件:<unistd.h>

功能:fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是说两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。调用失败大多是因为内存不够用了,或者进程太多而系统不允许创建了(受物理内存限制或管理员设定)。

​ 除了fork函数外,还有两个在进程相关程序中常用到的函数,分别为getpid和getppid,说明如下:
​ 1)getpid

​ 函数原型:pid_t getpid( void );

​ 头文件:<unistd.h>

​ 功能:返回当前进程的标识

​ 2)getppid

​ 函数原型:pid_t getppid( void );

​ 头文件:<unistd.h>

​ 功能:返回父进程的标识

​ 从上面fork函数的定义可以看出,这个调用非常简单,简单到连参数都没有,只有一个返回值。

2、fork函数实例程序

​ 下面以一个简单程序说明fork函数的工作机制,源代码文件process_sfork.c存储到"exp1"目录下

#include <stdio.h>
#include <unistd.h>
int main( int argc, char *argv[])
{
    pid_t pid;
    int var = 0;
    pid = fork();
    if (pid < 0)
    {
        printf( "error in fork!\n" );
    }
    else if (pid == 0)
    {
        printf("This is the child process, pid is %d.\n", getpid());
        var = 100;
    }
    else
    {
        printf("This is the parent process, pid is %d.\n", getpid());
        var = 50;
    }
    printf("var is %d.\n",var);
    return 0;
}

上述代码的执行结果如下

This is the parent process, pid is 18588.
var is 50.
This is the child process, pid is 18589.
var is 100.

​ 通过var的值可以看出,最后一个printf()的输出是在两个不同的进程中。从上面实验的结果可以看出,fork函数不需要额外的参数去传递一个类似线程的那种主函数。另外,fork函数调用后,执行的就是后续代码,没有任何迹象表明哪些是父进程特有的,哪些是子进程特有的。至于为什么代码能够指向不同分支,只要是因为if语句判断了它的返回值,根据fork返回值判断当前进程是父进程还是子进程。

3、fork函数创建的子进程和父进程的区别

​ 本质来说,fork函数启动一个新的进程,这个进程几乎是当前进程的一个副本:子进程和父进程使用相同的代码段;子进程复制父进程的堆栈段和数据段。这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。它们再交互信息时,只有通过进程间通信来实现。既然它们如此相像,Linux系统又是如何来区分它们的呢?正如前面所描述的,这是由函数的返回值来决定的。对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零。在Linux系统中,用getpid函数就可以看到不同的进程号。对父进程而言,它的进程号是由比它更低层的系统调用赋予的,而对于子进程而言,它的进程号即是fork函数对父进程的返回值。

3、连续调用多次fork函数

​ 为了进一步考察fork函数的工作机制,下面给出了利用for循环实现连续调用两次fork函数的程序,源文件代码process_mfork.c存储在“ch06/exp2”目录下

#include <stdio.h>
#include <unistd.h>
int main( int argc, char *argv[])
{
    int i = 0;
    int root = 0;
    printf("r\t i\t C/P\t ppid\t pid\n");
    for(i = 0; i < 2 ; i++){
        if(fork() > 0){
            printf("%d\t %d\t parent\t %d\t %d\n", root, i, getppid(), getpid());
            sleep(1);
        }
        else
        {
            root++;
            printf("%d\t %d\t child\t %d\t %d\n", root, i, getppid(), getpid());
        }
    }
    return 0;
}

​ 上述实验的执行结果如下:

r	 i	 C/P	 ppid	 pid
0	 0	 parent	 18396	 18653
1	 0	 child	 18653	 18654
1	 1	 parent	 18653	 18654
2	 1	 child	 18654	 18655
0	 1	 parent	 18396	 18653
1	 1	 child	 18653	 18656

​ 在上述的实验输出结果中,第1列表示当前进程是否为根进程,如果为0表示为根进程,否则为由fork创建的进程;第2列表示for循环中i的值;第3列描述当前进程是父进程还是子进程;第4列ppid表示当前进程的父进程ID;第5列pid表示当前进程ID。

​ 多进程程序开发并不是简单的调用一下fork,产生另一个执行相同任务的进程。多进程程序的目的是创建多个进程协作完成一项任务,以提高计算资源的利用率。

4、启动外部程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值