1.进程相关概念
下面五副图帮助了解上面是进程及进程相关的概念:
一、
二、
三、
pid的简易用法
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
printf("my pid is %d\n",pid);
while(1);
return 0;
}
四、
五、
下图就是对上图的东西文字描述:
2.创建进程函数fork的使用
以下代码(demo4. c)是通过pid来研究父子进程:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid = getpid();
printf("before fork: pid = %d\n",pid);
fork();
pid2 = getpid();
printf("after fork: pid = %d\n",pid2);
if(pid == pid2)
{
printf("this is father print\n");
}
else{
printf("this is child print,child pid = %d\n",getpid());
}
return 0;
}
代码运行结果如图:
用fork的返回值研究父子进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("father pid = %d\n",getpid());
pid = fork();
if(pid > 0)//返回值大于0,父进程才会运行
{
printf("this is father print,pid = %d\n",getpid());
}
else if(pid == 0){//返回小于0,子进程进入这个条件代码内
printf("this is child print,child pid = %d\n",getpid());
}
return 0;
}
下图是运行结果图(demo5.c)
3.创建进程函数fork的使用补充
这里是把fork的返回值具体等于多少给打印出来,代码(demo6.c是demo4.c进化版)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid = getpid();
printf("before fork: pid = %d\n",pid);
retpid = fork();//这里的retpid是被拷贝了一份,一份给父进程(在父进程存储空间里retpid值是大于0的并且大小刚好是子进程pid号);另一份给子进程(等于0是分配给子进程)
pid2 = getpid();
printf("after fork: pid = %d\n",pid2);//这里父子进程都会打印。
if(pid == pid2)
{
printf("this is father print,retpid=%d\n",retpid);//打印出父进程fork具体返回值,就是子进程的pid值。
}
else{
printf("this is child print,retpid=%d,child pid = %d\n",retpid,getpid());//打印子进程fork返回值。
}
return 0;
}
以下是运行的结果:(demo4的进化版)
4.进程创建发生了什么事
早期的Linux操作系统进程操作会把旧进程的所有(下图黄色的)内存空间(如代码段,数据段,bbs段,堆,栈,及高地址)全部拷贝;现在的话是有选择的拷贝(写实拷贝copy on write)比如子进程不对变量(如var)进行修改操作就不复制,采用共享的方式(共享不修改的变量的那些空间),这个就是写实拷贝,可以提高效率。
这里就说说早期的linux在fork发生了什么事:父进程的内存空间被子进程拷贝一份(如下图:课上的图)
由上图可知,当修改各自进程的值的时候,不会影响到其他进程值。代码(demo7.c)验证:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int data = 10;
printf("father pid = %d\n",getpid());
pid = fork();
if(pid > 0)
{
printf("this is father print,pid = %d\n",getpid());
}
else if(pid == 0){
printf("this is child print,child pid = %d\n",getpid());
data = data + 10;
}
printf("data=%d\n",data);
return 0;
}
运行结果如下图可以看出打印出来的结果,父进程data10,子进程data20,说明进程间的地址是独立的(就算时新的Linux写实拷贝时,改变data的值就要重新的拷贝data的地址)
5.创建新进程的实际应用场景及fork总结
常用的应用(如下图)场景是当服务端接收到客户端连接的请求时创建子进程与客户端对接数据交互。
模拟连接到客户端创建子进程的一个应用场景代码(demo8.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int data;
printf("father pid = %d\n",getpid());
while(1){
printf("please input a number\n");
scanf("%d",&data);
if(data == 1){
pid = fork();//输入1(模拟socket接入客户端)创建子进程
if(pid > 0)
{
}
else if(pid == 0){
while(1){
printf("do net request,pid=%d\n",getpid());//模拟每个客户端接入后与客户端做交互
sleep(3);
}
}
}
else{
printf("wait do nothing\n");//
}
}
return 0;
}
下图是上图进程的运行结果(父进程一直等待创建新连接然后创建子进程,当父进程每接收到一个连接就会创建一个新子进程《看每个子进程的pid号都不同》):
下图是对fork的小结: