Linux --------进程:(一)

23 篇文章 0 订阅

fork()函数:

一:介绍

  fork是在进程管理模块中的一个重要的函数。那么怎样来创建进程呢?

①:程序----- (执行)-----》进程   ./main
②:由程序创建进程   pid_t fork(void)

 

二:函数简介

一个现有进程可以调用fork函数创建一个新的进程。

  1.描述:一个现有进程可以调用fork函数创建一个新的进程。
  2.头文件:#include<unistd.h>
3.使用方法::pid_t n = fork()
  4.返回值:fork()函数被调用一次返回两次。 两次返回值的区别是:子进程返回值是0,父进程返回子进程ID。 出错返回0

 

三:函数特点

 

  1.为什么要将子进程的ID返回给父进程

    因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的ID。

   2.fork使子进程得到的返回值为0的理由是?

    一个进程只会有一个父进程,所以子进程总可以调用getppid以获得其父进程的ID(进程ID为0  是由内核交换进程使用,所以       一个子进程的进程ID不可能为0)。

   3.调用特点:父子进程继续执行fork调用后的指令,子进程是父进程的副本

       例:子进程获得父进程的数据空间,堆,栈的副本,注意这是子进程所拥有的副本,父子进程不共享这些存储空间部分-》所以父子进程的这些空间不再同一个位置。

               父子进程共享正文段。

    4.写时拷贝技术:fork之后父子进程共享所有的空间(数据段,栈,和堆),而且内核将他们的访问权限变为只读的。if父子进程中的任意一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常为虚拟机存储器中的一个“页”

  作用:①提高fork的效率;②fork之后,子进程往往会调用exec进行进程切换。

 

 

5.运行特点:

      父子进程是并发运行的:父进程创建出子进程后,两个进程就是独立的个体,各自运行互不干扰。

      父子进程谁先运行不由fork来决定,由系统当前环境和进程调度算法决定。

6.父子进程是否共享fork之前打开的文件描述符?

  共享:子进程能够使用fork之前打开的文件描述符访问文件内容。但是父子进程通过fork之前打开的文件描述符访问文件时,是互相影响的----》读写偏移量。

四:代码解析

#include"apue.h"

int globvar = 6;
char buf[] = "a write to stdout\n";

int main(int argc, char* argv[])
{
	int var = 88;
	pid_t pid;

	if (write(STDOUT_FILRNO, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		err_sys("write error");
	printf("before fork\n");//we do not flush stdout

	if ((pid = fork()) < 0)
	{
		err_sys("fork error:\n");
	}
	else if (pid == 0)//child process 
	{
		globvar++;
		var++;
	}
	else//father process
	{
		sleep(2);
	}

	printf("pid = %ld,glob = %d,var = %d\n", (long)getpid(), globvar, var);
	exit(0);

}

  :fork(之后谁先执行是不确定的,这取决于内核的调度算法。如果想使父子进程之间相互异步或者同步,则要求某种形式的进程之间的通信,在上述程序中,父进程使用了sleep(2),睡眠两秒钟的时间来使子进程先执行,但是并不能够保证2s已经足够,在后续中会补充讲解到。

       当标准输出时,我们将buf的长度减去1作为输出字节数,这是为了避免将终止字符null输写出。strlen计算时不包含终止null字节的字符串的长度,而sizeof则计算包括终止字符null字节的缓冲区的长度。另外两者之间的差别还有,使用strlen需进行一次函数调用,而对于sizeof来说,因为缓冲已经用已知字符串进行初始化了,其长度是固定的,所以sizeof是在编译时计算缓冲区的长度。


$  ./a.out

a  write to stdout

before  fork

pid  =  432,  glob  =  7,var  =  89 子进程的变量值改变了

pid  =  429,  glob  =  6,var  =  88父进程的变量值没有改变

 

主要是因为写诗拷贝技术的存在。

五:代码分析

程序一:

int main()
{
	pid_t n = fork();
	assert(n != -1);

	if (n == 0)
	{
		printf("child fork is running:!\n");
	}
	else
	{
		printf("father fork is running:!\n");
	}
	exit(0);
}

解析:

   fork()函数的调用父进程返回子进程的ID,子进程返回0,所以父进程执行else,子进程执行if。

 

程序二:

int main()
{
	if (fork() && fork())
	{
		printf("A \n");
	}
	else
	{
		printf("B \n");
	}
	exit(0);
}

解析:

  考察fork()函数返回值 和 子进程保留父进程转态,调用第一个fork()函数时,子进程1返回0,不执行逻辑与后面的判断,直接执行else,输出B。而父进程返回子进程1的ID,在判断第二个fork()函数,其子进程2返回0,则执行lese输出B,父进程返回子进程2的ID,执行if输出A。

输出两个B一个A。

程序三:

int main()
{
	int i = 0;
	for (; i < 2; ++i)
	{
		if (fork() == 0)
			printf("A \n");
		else
			printf("B \n");
	}

	exit(0);
}

解析:

由于这里面涉及到,循环所以我们要搞清楚循环过程中的父子进程直接的关系!

这里会输出3个A和3个B.

 

程序四:问将程序3的两个printf中的\n去掉,会出现什么样的结果呢?

 

 

 

 

 

  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值