Linux内核编程 fork( )系统调用

目录

一:fork系统调用

二:多进程特性

三:fork创建进程


一:fork系统调用

查看fork使用手册                         man fork

man fork

使用fork()函数需要引入头文件

#include <sys/types.h>

#include <unistd.h> 

pid_t fork(void);

这个fork没有任何的参数,但是会返回一个pid_t,pid_t称为进程ID号,也就是PID,用Int来接收也可以,但习惯性使用pid_t标准接收

这个fork函数就是用来开辟出一个新的进程,也称为创建子进程(子进程:因为我们在执行程序的时候一定会main开始走,这个程序一旦被运行,这个程序本身立马转变成一个进程,在代码里面再开一个进程,创建这个新的进程就是子进程)

fork机制:一次调用 两次返回

子进程返回0,父进程返回子进程ID,出错为-1【一次调用,两次返回】

以往学习的函数,调用只能得到一个结果

今天学习的这个fork函数的调用,可以返回出3种结果

成功返回出的一个是非负整数,一个为0,失败返回-1

二:多进程特性

fork机制: 一次调用 两次返回

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{
		cout << "1111" << endl;
	}
	else if(id == 0)
	{
		cout << "2222" << endl;
	}

	return 0;
}

 看起来if逻辑不合理,但是要是理解了这个fork有两次返回,就可以明白这个结果了

可以查看fork两次返回PID值

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{
		cout << "1111 pid = " << getpid() << endl;
	}
	else if(id == 0)
	{
		cout << "2222 pid = " << getpid() << endl;
	}

	return 0;
}

说明当前这个程序有两个进程,一个进程47590,一个进程47660,

也就是说一次一个进程47590返回,一次是另外一个进程47660返回

父进程返回id是子进程pid    父进程返回子进程ID

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{
		cout << "父进程 pid = " << getpid( ) << "返回的id = " << id <<  endl;
	}
	else if(id == 0)
	{
		cout << "子进程 pid = " << getpid( ) << endl;
	}

	return 0;
}

父进程返回子进程ID

为什么两个进程可以同时跑呢?

在ubuntu系统中运行程序,多个进程对于一个操作系统来说是可以同时运行的

操作系统允许多个进程同时运行

只要是linux认为是一个进程的东西,根据PCB,那个进程就应该要有一个代码段,父进程有一个代码段,子进程也有一个代码段

一次调用,两次返回:

通过fork这个函数生成一个新的进程,那个新的进程,会将当前的整段所有的代码全部复制一份交给进程来执行,各自执行各自的内容,各自返回各自fork的返回值就不一样了

父进程返回一个结果,子进程返回一个结果,也就是两次返回

不同的环境下,返回的结果值不同

进程特点:

1.一台计算机中同时运行

2.一个程序不止有一个进程(就像main函数只有一个,跑起来会有两个进程)

3.进程之间数据不共享

也就是说,程序运行的时候,代码可以创建 n个进程(理论是n个,实际效果还要根据各自电脑的硬件支持,CPU不好的就跑不了几个)

电脑要是用软件很卡,要么就扩展内存,要么就换个好点的CPU(运算效率更高,执行东西更多)

可以做到一个程序   可以生成有    n个进程

既不在父进程也不在子进程的代码段,打印over测试一下如下

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{
		cout << "父进程 pid = " << getpid( ) << "返回的id = " << id <<  endl;
	}
	else if(id == 0)
	{
		cout << "子进程 pid = " << getpid( ) << endl;
	}
	cout << "over" << endl;

	return 0;
}

两个over ,父进程子进程都有属于自己的代码段,复制代码执行两次,有两个over,

可以查看一下id,如下

	cout << "over" << getpid( )<< endl;

一个over是49195,一个over是49265 

父进程 子进程都有属于自己的代码段

进程同时执行,但是什么时候被调用什么时候去执行是随机的 (父进程子进程随机先后顺序)

有可能走一句走over,也有可能父子全部走完再走over

现实中的例子就比如VM虚拟机,VS2019同时在使用,都是进程都在运行,

CPU自己会主动去切,一会跑VM,一会跑VS,这就是随机调用

也就是CPU的随机时间片轮转(随机轮片)

* 多进程可以同时运行,一个程序运行不代表只有一个进程 

做个测试:一个程序,n个进程,这n个进程是否相互影响

在父进程和子进程中分别添加一个死循环测试一下,用到死循环(return处断点执行不到),

重新生成解决方案,在ubuntu中 终端命令运行程序测试

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{

		while (1)
		{
			cout << "父进程 pid = " << getpid() << "返回的id = " << id << endl;
			sleep(1);
		}
	}
	else if(id == 0)
	{
		while (1)
		{
			cout << "子进程 pid = " << getpid() << endl;
			sleep(1);
		}
	}
	
	cout << "over" << getpid()<< endl;

	return 0;
}

观察可知

父进程和子进程都有在跑 ,二者逻辑相互不受影响。

因为它们都是进程,它们同时运行,所以它们相互不受影响

PCB管理中代码段是独立的,一个代码段死循环,对另外一个代码段不会产生什么影响

即便是同一个程序产生的多个进程,代码段依然是独立的,相互之间不受任何影响

多进程之间代码独立 不会互相影响

不同的进程执行不同的业务:在使用VS编写代码过程中也可以同时QQ音乐播放听歌

由此可见,多进程的编程方式就显得比较重要,非常值得学习!

做个测试:父进程,子进程之间数据独立 (PCB中数据段 独立)

用一个全局变量number,简单测试一下

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int number = 0;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{

		while (1)
		{
			number++;
			cout << "父进程 pid = " << getpid() << "返回的id = " << id <<"number = "<<number<< endl;
			sleep(1);
		}
	}
	else if(id == 0)
	{
		while (1)
		{
			number++;
			cout << "子进程 pid = " << getpid() << "number = " << number << endl;
			sleep(1);
		}
	}
	
	cout << "over" << getpid()<< endl;

	return 0;
}

在ubuntu中测试结果如下

不难看出,父1子1,父2子2,父3子3... :没有叠加(PCB中除了代码段,还有数据段)

多进程之间数据独立,不能共享

有时候父先有时候子先:多进程 随机调用

可以将

int number = 0;

放入main函数中

全局变量  换   局部变量       简单测试一下,发现最终的结果都是一样的

也就是说进程的数据都不会叠加,数据是独立的,数据不共享!

多进程之间数据独立,不能共享

做个测试:对于这个number数据,只占用一个地址

#include<iostream>
#include <sys/types.h>
#include <unistd.h>


using namespace std;

int number = 0;

int main()
{
	pid_t id = 0;
	
	//一次调用 两次返回
	id = fork();
	if (id > 0)
	{

		while (1)
		{
			number++;
			cout << "父进程 pid = " << getpid() << "返回的id = " << id <<"number = "<<&number<< endl;
			sleep(1);
		}
	}
	else if(id == 0)
	{
		while (1)
		{
			number++;
			cout << "子进程 pid = " << getpid() << "number = " << &number << endl;
			sleep(1);
		}
	}
	
	cout << "over" << getpid()<< endl;

	return 0;
}

测试结果如下

一个地址,数据各自加各自的【多进程之间数据不共享

如果进程越来越多,地址越多,内存也越来越大,这么一来,非常不合理

也就是说,地址只有一个,但是数据有其各自的堆栈段

(通过这个堆栈段计算数据,在要使用的时候再赋值给那一个地址就可以了),这样就保证了进程在不断增加的情况下,空间得到一定的节约

一个数据不需要很多地址,否则开一个进程,内存乘2,不符合实际应用场景

代码中就定义了一个变量,也就只分配给这个变量一个内存空间

* 多进程之间数据独立,不能共享,数据地址相同,值不同

小结:

fork机制:一次调用 两次返回 

子进程返回0 父进程返回子进程ID

原理:代码执行fork,会生成子进程,子进程将所有代码、数据进行拷贝

代码套路:

if(pid > 0)

{

   父进程逻辑

}

else if(pid == 0)

{

   子进程逻辑

}

三:fork创建进程

在for循环中执行fork

问如下情况开几个进程?

for(int i =0;i<2;i++)
{
   fork();
}

i = 0 ---》 1父 1子 (子再 自己(父) + 孙)

i = 1 ---》 1父 1子 (子再 自己(父) + 孙)

也就是4个(公式法: n的2次方)          2 的 2次方 = 4 

针对上述情况,对于子进程,需要break来退出循环,来退出子进程

for(int i =0;i<2;i++)
{
   fork();
   if(id > 0)//父进程
   { 
   }
   else if(id == 0)//子进程
   {
      break;
   }
}

退出子进程还有其他方法: 

return 0;//退出子进程

for(int i =0;i<2;i++)
{
   fork();
   if(id > 0)//父进程
   { 
   }
   else if(id == 0)//子进程
   {
      return 0;
   }
}

退出子进程还有其他方法: 

exit (0);//退出子进程

for(int i =0;i<2;i++)
{
   fork();
   if(id > 0)//父进程
   { 
   }
   else if(id == 0)//子进程
   {
     exit(0);
   }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Unix/Linux系统编程是指使用C语言编写程序,能够调用操作系统提供的系统调用和库函数来完成系统级任务的程序设计过程。Unix/Linux系统编程的目的是编写高效、可靠、安全、移植性好的应用程序或系统程序。 Unix/Linux系统编程的核心代码包括使用系统调用,文件操作(读写文件、目录操作等),进程控制(fork、exec等),信号处理,网络编程等。 在Unix/Linux中,系统调用是与内核进行通讯的标准方式。程序中使用系统调用来请求内核完成某个任务。例如,open()系统调用用于打开一个文件,并返回文件描述符。read()和write()系统调用用于读写文件。 文件操作是Unix/Linux系统编程中的一个重要部分。文件操作包括打开文件、读写文件、删除文件、重命名文件等操作。另外还有目录操作,如创建目录、删除目录、遍历目录等。 进程控制是Unix/Linux系统编程中最为复杂的部分之一。进程控制包括创建新进程、执行新进程、等待进程结束、发送信号给进程等等。其中最常见的系统调用fork()和exec()。fork()用于创建新进程,而在创建新进程之后,exec()则用于在新进程中执行新的程序。 信号处理是Unix/Linux系统编程中的一个重要概念。信号是由系统发出的一个异步事件,可以从进程内部或外部发出。进程可以对信号进行相应操作。常见的信号包括SIGINT(Ctrl+C中断信号)、SIGTERM(终止进程信号)和SIGKILL(强制终止进程信号)。 网络编程是Unix/Linux系统编程中的另一个重要部分。Unix/Linux提供了许多网络编程API,例如socket()、bind()、listen()和accept()等。使用这些API可以编写服务器端和客户端程序,进行网络通信。 总之,Unix/Linux系统编程涉及到许多重要的概念和操作,涉及到操作系统底层的各种操作。因此,需要开发人员有扎实的C编程能力、熟悉Unix/Linux系统调用和库函数、了解进程控制和信号处理的概念、熟悉网络编程API以及充分了解操作系统内部的机制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenruhan_QAQ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值