Linux系统基础知识(day1)

1.进程相关概念

1.1 程序和进程

程序:是指编译好的二进制文件,存放在磁盘上,占用磁盘空间,是一个静态的概念。
进程:一个启动的程序,进程占用的是系统资源,如物理内存,CPU,终端,是一个动态的概念
程序---->剧本
进程---->唱戏(舞台,灯光,道具,人等资源)
同一个程序可以在多个终端执行,类似同一台戏可以在多个舞台演出
每启动一个程序都会产生一个进程PID,即使是相同的进程多次启动也会产生不同的PID 

1.2 并行和并发

并发:在一个时间段内,同一个CPU上,同时运行多个程序
并行:在一个时间片内,有多个程序在执行(前提是有多个CPU)。
CPU会将一个大的时间段分成多个小的时间片,让进程轮流使用CPU的时间片

1.3 PCB-进程控制块

1:进程id,系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
2:进程的状态:就绪,运行,挂起,停止,等状态
3:进程切换时需要保存和恢复的一些CPU寄存器(保存上下文)
4:描述虚拟地址空间的信息
5:当前工作目录
6:umask掩码
7:文件描述符

2.fork/getpid/getppid函数的使用

fork:父进程调用fork函数创建一个子进程,子进程的用户区和父进程的用户区完全一样,但是内核区不完全一样,如父进程的PID和子进程的PID不一样。

一个子进程只有一个父进程,但一个父进程可以有多个子进程。

fork函数的返回值:父进程返回的是子进程的PID,这个值大于0,子进程返回0;
注意:并不是一个进程返回两个值,而是父子进程各自返回一个值。

父子进程执行逻辑:父进程执行pid>0的逻辑,子进程执行pid==0的逻辑。

父子进程的执行顺序:谁先抢到cpu时间片谁就先执行

fork函数测试(linux环境,包含getpid,getppid函数)

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

int main()
{
	printf("before fork,pid:[%d]\n",getpid());//查看这句话打印多少次,谁打印的
	//创建子进程
	//函数原型: pid_t fork(void)
	pid_t pid = fork();
	if(pid<0) //fork函数失败的情况
	{
		perror("fork error");
		return -1;
	}
	else if(pid>0) //父进程
	{
		printf("father:pid==[%d],fpid==[%d]\n",getpid(),getppid());
		sleep(1);
	}
	else if(pid==0)//子进程
	{
		printf("child:pid==[%d],fpid==[%d]\n",getpid(),getppid());
	}
	printf("after fork,pid:[%d]\n",getpid());//查看这句话打印多少次,谁打印的
	return 0; 
}

3.ps/kill命令的使用(部分)

ps命令:查看进程相关信息。
kill -9 2117:杀死pid=2117的进程

4.execl/execlp函数的使用

int execl(path,"ls","-l",NULL)
调用execl函数以后,子进程 的代码段会被ls命令的代码段替换,但子进程的地址空间没有变化,子进程的PID也没有变化。
execl函数原型:int execl(const char * path,const char * arg,.../*(char)NULL*/);
execlp函数原型: int execlp(const char * file,const char * arg,.../*(char)NULL*/);

5.孤儿进程和僵尸进程

5.1为什么要进行进程资源的回收

当一个进程退出之后,进程能够回收自己的用户区的资源,但是不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数完成对子进程的回收,避免造成系统资源的浪费。

5.2孤儿进程

孤儿进程:父进程先退出,子进程就变成了孤儿进程。

 当去掉父进程中的sleep函数,子进程可能会变成孤儿进程(父进程比子进程先退出),此时,init进程(1号进程)将成为该子进程的父进程,负责回收子进程的资源
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	printf("before fork,pid:[%d]\n",getpid());//查看这句话打印多少次,谁打印的
	//创建子进程
	//函数原型: pid_t fork(void)
	pid_t pid = fork();
	if(pid<0) //fork函数失败的情况
	{
		perror("fork error");
		return -1; 
	}
	else if(pid>0) //父进程
	{
		printf("father:pid==[%d]\n"getpid());
		//sleep(1);
	}
	else if(pid==0)//子进程
	{
		printf("child:pid==[%d]\n".getpid());
	}
	printf("after fork,pid:[%d]\n",getpid());//查看这句话打印多少次,谁打印的
	return 0;
}

5.3僵尸进程

僵尸进程:子进程先退出,父进程没有完成对子进程的回收,此时子进程就变成了僵尸进程。

在父进程中添加sleep函数,让子进程先退出,父进程后退出(延长父进程在系统中的存在时间),执行代码后,子进程就变成了僵尸进程(zombie进程),该进程无法用kill命令杀死(僵尸进程是一个死掉的进程,接收不了信号)。

解决办法:使用kill -9 命令杀死父进程,让init进程(1号进程)回收该子进程

僵尸进程带来的问题:当系统中出现大量的僵尸进程时,会占用大量的系统资源,此时系统将无法产生新的进程,需要避免此问题。
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	//创建子进程
	//函数原型: pid_t fork(void)
	pid_t pid = fork();
	if(pid<0) //fork函数失败的情况
	{
		perror("fork error");
		return -1; 
	}
	else if(pid>0) //父进程
	{
		sleep(20);
		printf("father:pid==[%d]\n"getpid());
	}
	else if(pid==0)//子进程
	{
		printf("child:pid==[%d]\n".getpid());
	}
	return 0;
}

6.wait函数的使用

函数原型:pid_t wait(int  * status);

函数作用:
1 阻塞并等待子进程退出
2 回收子进程残留资源
3 获取子进程结束状态(退出原因)

返回值:
成功:返回清理掉的子进程ID;
失败:-1(没有子进程)可表示子进程已经全部回收

status参数:子进程退出状态 ----传出参数
WIFEXITED(status): 为非0  ->进程正常结束
WEXITSTATUS(status): 获取进程退出状态
WIFSIGNALED(status): 为非0 ->进程异常终止
WTERMSIG(status): 取得进程终止的信号编号。
//父进程调用wait函数完成对子进程的回收
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	//创建子进程
	//函数原型: pid_t fork(void)
	pid_t pid = fork();
	if(pid<0) //fork函数失败的情况
	{
		perror("fork error");
		return -1;
	}
	else if(pid>0) //父进程
	{
		printf("father:pid==[%d],fpid==[%d]\n",getpid(),getppid());
		int status;
		pid_t wpid=wait(&status);
		printf("wpid==[%d]\n",wpid);
		if(WIFEXITED(status)) //正常退出
		{
			printf("child normal exit,status==[%d]\n",WEXITSTATUS(status));
		}
		else if(WIFSIGNALED(status)) //被信号杀死
		{
			printf("child killed by signal,signo==[%d]\n",WTERMSIG(status));
		}
	}
	else if(pid==0)//子进程
	{
		printf("child:pid==[%d],fpid==[%d]\n",getpid(),getppid());
		sleep(5);
		return 9; 
	}
	
}

7.waitpid函数的使用

函数原型:pid_t waitpid(pid_t pid,int * status,int options);

函数作用:
1 阻塞并等待子进程退出
2 回收子进程残留资源
3 获取子进程结束状态(退出原因)

函数参数:
pid: 
	pid=-1:等待任意子进程,与wait等效
	pid>0:等待其进程ID,与PID相等的子进程
	pid=0:等待进程组ID与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
	pid<-1:等待其组ID等于pid的绝对值的任一子进程。(适用于子进程在其他组的情况)
starus:子进程的退出状态,用法同wait函数。
options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞。

返回值:
>0:返回清理掉的子进程ID;
-1:(没有子进程)可表示子进程已经全部回收
0 :参数3为WNOHANG,且子进程正在运行。

注意:调用一次waitpid或者wait函数只能回收一个子进程
//父进程调用waitpid函数完成对子进程的回收
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	//创建子进程
	//函数原型: pid_t fork(void)
	pid_t pid = fork();
	if(pid<0) //fork函数失败的情况
	{
		perror("fork error");
		return -1;
	}
	else if(pid>0) //父进程
	{
		printf("father:pid==[%d],fpid==[%d]\n",getpid(),getppid());
		int status;
	while(1)
	{
		pid_t wpid=waitpid(-1,&status,WNOHANG); //-1表示等待任意子进程,WNOHANG:不阻塞
		printf("wpid==[%d]\n",wpid);
		if(wpid>0)
		{
			if(WIFEXITED(status)) //正常退出
			{
				printf("child normal exit,status==[%d]\n",WEXITSTATUS(status));
			}
			else if(WIFSIGNALED(status)) //被信号杀死
			{
				printf("child killed by signal,signo==[%d]\n",WTERMSIG(status));
			}
		}
		else if(wpid==0)//子进程还活着
		{
			pintf("child is living,wpid==[%d]\n",wpid);
		}
		else if(wpid==-1) //没有子进程了
		{
			printf("no child is living,wpid ==[%d]\n",wpid);
			break;
		}
	}
	else if(pid==0)//子进程
	{
		printf("child:pid==[%d],fpid==[%d]\n",getpid(),getppid());
		sleep(5);
		return 9; 
	}
	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值