13.进程---

一、进程的概念

1.可以用ps或top查看进程

2.pid=0;交换进程(作用是进程调度)
pid=1;init进程(作用是系统初始化)

3.getpid();//获取自身的进程标识符

4.getppid();//获取父进程的进程标识符
什么是父进程?
答:进程A创建了进程B,那么A叫做父进程,B叫做子进程。

二、进程的创建

使用fork函数创建一个进程,
pid_t fork(void),
fork函数调用成功,返回两次。
返回值为0,代表当前进程为子进程。
返回值为非负数,代表当前进程为父进程。
返回值为-1,代表调用失败。
(1)查看pid_t fork(void)函数原型,判断怎样使用该api.

man 2 fork

在这里插入图片描述发现需要2个头文件。
(2)调用api创建进程并查看其pid

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

int main(void)
{
	pid_t pid,pid1,pid2;
	char return_of_fork;
	pid1 = getpid();//pid of 1_creat_process.c
	
	printf("this is father process,pid = %d\n",pid1);

	return_of_fork = fork();
	

	if(return_of_fork > 0)
	{
		printf("this is father process,pid = %d\n",getpid());
	}
	else if(return_of_fork == 0)
	{
		printf("this is son process,pid = %d\n",getpid());
	}
	else if(return_of_fork < 0)
	{
		printf("fork failed\n");
	}

	return 0;
}

在这里插入图片描述
注意,执行完fork,
linux系统会为子进程拷贝一份文件(包括程序的正文,初始化的数据,未被初始化的数据,堆,栈)
然后运行父进程和子进程(进程调度),
运行父进程后,return_of_fork >0,并打印,
运行子进程后,return_of_fork =0,并打印,
*因此才能看到既满足 if(return_of_fork > 0),又满足 if(return_of_fork == 0); *
其实并不是同时满足,
上面提到,系统会为紫荆城拷贝一份文件,
在进程调度时,会分别执行父进程和子进程(用不太规范的话来描述:执行了2份main函数),
在父进程的调度中return_of_fork > 0并打印,
在子进程的调度中return_of_fork > 0==0并打印。

注意:向执行memcpy()时在地上申请空间
调用函数时,在栈上运行

三、创建进程时发生了什么

四、fork的应用场景

要求:父进程解析键盘输入的数值,当键盘输入的值为9时创建子进程并屏幕输出。

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

int main(void)
{
	char return_of_fork;
	int data = 1;
	while(1)
	{
		printf("please input a data\n");
		scanf("%d",&data);

		if(data == 9)
		{
			return_of_fork = fork();
			if(return_of_fork > 0)
			{
				printf("father process,pid = %d\n",getpid());
			}
			else if(return_of_fork == 0)
			{
				while(1)
				{
					printf("child process,pid = %d\n",getpid());
					sleep(1);
				}
		                
			}
		}
		else
		{
			printf("wait !  do nothing\n");
		}

	}
	return 0;
}

五、vfork和fork的区别

(1)直接使用父进程空间,不拷贝
(2)保证子进程优先运行,当子进程调用exit退出后,父进程才执行

六、进程的退出

  1. 种类:5种正常退出+3种异常退出
    (1)5种正常退出:
    main函数调用return;
    进程调用exit();
    进程调用_exit()或_Exit()
    进程最后1个线程返回;
    最后1个线程调用pthread_exit

(2)3种异常退出
调用adort;
当进程收到某些信号时;
最后1个线程对取消(cancellation)请求做出响应

七、父进程等待子进程退出

1.调用的api:
wait();//调用wait,可以防止子进程变成僵尸进程(子进程退出状态未被收集,则子进程会变为僵尸进程)
或waitpid();

man 2 wait

在这里插入图片描述
(1)wait()的使用方法,和常见使用场景
等待子进程的退出,并且收集子进程退出的状态(子进程正常退出和异常退出返回的状态不同)。
子进程的状态被返回给wait(int *wstatus)的参数

a)背景:忽略使用wait()时会导致子进程变成僵尸进程。
运行下面的代码,键盘输入9后后创建子进程,每隔1秒屏幕输出1次,打印三次后会exit子进程

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	pid_treturn_of_fork;
	int cnt=0;
	int data = 1;
	return_of_fork = fork();
	while(1)
	{
		printf("please input a data\n");
		scanf("%d",&data);

		
			
			if(return_of_fork > 0)
			{
				while(1)
				{
				
				printf("father process,pid = %d\n",getpid());
				sleep(1);
				}
				
			}
			else if(return_of_fork == 0 && data == 9)
			{
				while(1)
				{
					
					
					printf("child process,pid = %d\n",getpid());
                                        sleep(1);
                                        cnt++;
					if(cnt ==3)
					{
					exit(0);
					}
				

				}
		                
			}
		
		else
		{
			printf("wait !  do nothing\n");
		}

	}
	return 0;
}

在这里插入图片描述
如图,键盘输入1后,一直调度父进程。
当键盘输入9后,开始调度子进程。子进程运行3次之后,子进程被结束。
在这里插入图片描述查看pid,发现子进程pid为22507,子进程变成僵尸进程了。
Z+指僵尸进程

b)使用wait()获取子进程退出状态,避免子进程变成僵尸进程
使用wait(NULL);(如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
	pid_t return_of_fork;
	int cnt=0;
	int data = 1;
	return_of_fork = fork();


	while(1)
	{
		    printf("please input a data\n");
		    scanf("%d",&data);
	
			if(return_of_fork > 0)
			{	
				wait(NULL);
				while(1)
				{
				
				printf("father process,pid = %d\n",getpid());
				sleep(1);
				}
				
			}
			else if(return_of_fork == 0 )
			{
				while(data == 9)
				{
					
					
					printf("child process,pid = %d\n",getpid());
                                        sleep(1);
                                        cnt++;
					if(cnt ==3)
					{
					exit(0);
					}
				

				}
		                
			}
		
		else
		{
			printf("wait !  do nothing\n");
		}

	}
	return 0;
}

在这里插入图片描述如图,使用wait(NULL),可以关掉僵尸进程
/------------/
在这里插入图片描述如图,使用WEXITSTATUS(status)可以查看子进程正常终止返回的状态;
使用方法如下:
int status_childProcess;
wait(&status_childProcess);
printf(“child process exit status:%d\n”,WEXITSTATUS(status_childProcess;));

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
	pid_t return_of_fork;
	int cnt=0;
	int data = 1;
	int status_childProcess;
	return_of_fork = fork();


	while(1)
	{
		        printf("please input a data\n");
		        scanf("%d",&data);

		
			
			if(return_of_fork > 0)
			{	
				wait(&status_childProcess);
				printf("child process exit status:%d\n",WEXITSTATUS(status_childProcess));
				while(1)
				{
				
				printf("father process,pid = %d\n",getpid());
				sleep(1);
				}
				
			}
			else if(return_of_fork == 0 )
			{
				while(data == 9)
				{
					
					
					printf("child process,pid = %d\n",getpid());
                                        sleep(1);
                                        cnt++;
					if(cnt ==3)
					{
					exit(1);
					}
				

				}
		                
			}
		
		else
		{
			printf("wait !  do nothing\n");
		}

	}
	return 0;
}

在这里插入图片描述如图所示配合wait()相关的宏,可以获得子进程退出状态。而且父进程是在子进程退出后才执行的。

(2)waitpid()的使用方法,和常见使用场景
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

(3)孤儿进程
在这里插入图片描述
获取父进程的api:
getppid();
init进程的pid号==1

八、exec族函数

1.exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。
执行成功后不会返回,也不会从源程序调用点接着往下执行。

函数原型:


int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ..., char * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

参数


path参数表示你要启动程序的名称包括路径名

arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束

返回值:成功返回0,失败返回-1

2.其他知识
(1)如何查看linux指令所在路径
在这里插入图片描述如图,date指令用于查看系统时间。
ls指令用于查看该路径下的文件。
whereis用于获取linux指令的路径。

如上图所示,ls所在路径为“/bin/ls”,现在要求用execl调用ls.
程序如下

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
	printf("before exec\n");
	if(execl("/bin/ls","ls","-l",NULL) == -1)//exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。执行成功后不会返回,也不会从源程序调用点接着往下执行。
	{
		printf("exec failed\n");
	}
	printf("after exec\n");
}

运行结果:在这里插入图片描述如图运行成功,execl调用了ls.
调用成功,不返回-1,不执行 printf(“exec failed\n”);
调用成功,也没有继续往下执行,即不执行 printf(“after exec\n”);

若调用失败,则如图所示(失败的原有是 ls的路径不对)
在这里插入图片描述

(2)怎样查看环境变量
在这里插入图片描述

(3)怎样配置临时环境变量(临时环境变量的生存周期在该终端框被关闭时结束)
在这里插入图片描述先使用pwd查看当前路径,
再使用export PATH=
"="后面的参数¥PATH是指原先的环境变量。
添加好临时环境变量后就可以在别的路径下调用这里的可执行文件。
例如myechoarg是可执行文件,在配置环境变量前myechoarg只能在这个路径执行,
配置环境变量后,就可以在任意路径下执行。

(4)perror();的用法
(5)

九、system

system()是封装后的execl();
system()和execl()的区别是:system执行后,会从源程序调用点继续往下执行。
函数原型:

如图,是system的使用方法,system的参数是可执行文件的路径
在这里插入图片描述在本路径下,有4个文件
main.c编译生成的目标文件是work,其运行结果是:屏幕输出hello
my_system.c是调用system()的demo,其编译生成目标文件是myrun,运行结果是"执行work文件"

补充:可直接调用shell指令
如图,调用ls
在这里插入图片描述

十、popen

popen和system相比,
有1个好处,
即:可以获得运行的结果

函数原型
在这里插入图片描述其返回值为FILE型,

#include <stdio.h>
#include <stdlib.h>

int main()
{
	char return_buffer[1024] = {0};
	FILE *fp;
	fp = popen("ps","r");

	int nread = fread(return_buffer,1,1024,fp);
	printf("return_buffer %d byte,buffer:%s\n",nread,return_buffer);
	return 0;
}

在这里插入图片描述如图,popen的返回值是运动的结果;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值