Linux系统编程学习--第5天多进程

本文详细解释了进程和程序的概念,探讨了进程的动态性、并发性、独立性和异步性,介绍了进程号、状态、内存分布,包括进程间通信和进程控制方法如fork、vfork、wait、exit和exec系列函数。此外,还讨论了特殊进程如孤儿进程和僵尸进程,以及如何查看和操作进程资源。
摘要由CSDN通过智能技术生成

什么是进程?
程序被加载到内存中运行,系统中基本的执行单元。
什么是程序?
可执行的二进制代码文件。

1、进程和程序之间的联系?
具有一定独立功能的程序的一次运行活动,操作系统动态执行的单元,包含程序从调度到消亡的整个过程是动态的过程
运行着的程序都是一个进程,系统本身也运行着许多管理系统资源和用户访问的程序

windows任务管理器中就显示很多进程
在这里插入图片描述
2、如何查看进程
在终端输入ps -ef查看进程
在这里插入图片描述
从左到右是
UID PID PPID C STIME TTY TIME CMD
用户ID 进程ID号 父进程ID CPU占用率 开始时间 启动的终端 占用CPU总时间 启动命令

ps -aux查看进程的详细信息
在这里插入图片描述
3、杀死进程的shell命令:kill -9 进程号
演示:打开两个终端
在这里插入图片描述
可以看到这两个终端的是两个进程,进程号分别是72882、72897.通过kill -9命令杀死一个进程
在这里插入图片描述

4、进程有进程号和运行状态
4.1进程号:pid是无符号整型,进程号唯一就像身份证。内核限制进程号小于等于32767,达到时重置进程号计数器,计数器重置从300开始,1-300被系统进程和守护进程占用,32位开发平台最大是32767,64位达2的22次方

4.2进程的状态:

执行态:该进程正在运行,即进程正在占用 CPU, 任何时候都只有一个进程。
就绪态:进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。
等待态:进程正在等待某些事件,当前不能分配时间片, 进程不能使用 CPU,若等待事件发生(等待的资源分配到)则可将其唤醒,变成就绪态

父进程:

创建/启动一个进程的进程称之为该进程的父进程。

子进程:

相对于该程序的父进程,该进程为子进程

5、进程的内存分布:

每个进程相互独立,都运行在自己的虚拟内存空间中,
与其他进程相互隔离(系统为每个进程维护了一个内存
映射表,表明物理内存和虚拟内存的对应关系)。

文本段:包含进程运行的程序机器语言指令
文本段的只读性:防止进程通过错误指针以外修改自身的指令

数据段:初始化数据段:包含显示初始化的全局变量和静态变量,程序加载到内存的时候读取这部分变量
未初始化数据段(BSS段):未进行限行初始化的全局变量和静态变量,程序启动之前,系统将本段所有的内存初始化为0。
数据段就是分为给过值的全局变量和静态变量、没给过值(默认值为0)

堆:动态开辟的内存

栈:动态增长和收缩的段,由栈帧组成,存放局部变量、函数参数值等

在这里插入图片描述
6、如何查看一个程序的内存分布?
使用size命令查看
在这里插入图片描述
text表示正文段大小,单位全为byte
data表示包含静态变量和已经初始化(可执行文件包含了初始化的值)的全局变量的数据段大小
bss由可执行文件中不含其初始化值的全局变量组成
dec前三个的总和

详细请看https://www.cnblogs.com/z3286586/p/14817832.html

7、任务调度

进程间是以调度的方式并行运行的。

8、进程的特点

1、动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
如何理解?动态性是进程最基本的特性,可表现为由创建而产生、由调度而执行,因得不到资源而暂停执行,以及由撤销而消亡,因此进程有一定的生命周期。而程序只是一组有序指令的集合,是静态实体。

2、并发性:任何进程都可以同其他进程一起并发执行
如何理解?并行是指两个或多个事件可以在同一个时刻发生。并发是指两个或多个事件可以在同一个时间间隔发生

3、独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;

4、异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
比如,当正在执行的进程提出某种资源请求时,如打印请求,而此时打印机正在为其他某进程打印,由于打印机属于临界资源,因此正在执行的进程必须等待,且放弃处理机,直到打印机空闲,并再次把处理机分配给该进程时,该进程方能继续执行。可见,由于资源等因素的限制,进程的执行通常都不是“一气呵成”,而是以“停停走走”的方式运行。

问题:进程的执行态只能有一个进程,为什么进程具有并发性?
需要理解并发和并行的区别。

9、获得进程号函数getpid

函数原型:pid _t getpid(void)pid_tunsigned int
函数功能:获得当前进程id
函数参数:无
函数返回值:调用该函数的进程id
头文件: #include <sys/types.h>
		 #include <unistd.h>

编程:运行一段程序,打印进程id(程序结束进程消失,进程号也不存在了)

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

int main(int argv,char * argc[])
{
    unsigned int pid;
    pid =getpid();
    printf("进程id:%d\n",pid);
    return 0;    
}

在这里插入图片描述
可以看到每次的进程号都不一样,说明程序运行完进程消失、进程号被回收。
10、获得当前进程的父进程的id函数getppid

函数原型:pid_t getppid(void)
函数功能:获得当前进程的父进程的id
函数参数:无
函数返回值:调用该函数的进程的父进程id
头文件: #include <sys/types.h>
		 #include <unistd.h>

编程:打印进程的父进程id

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

int main(int argv,char * argc[])
{
    unsigned int pid;
    pid =getpid();
    printf("进程id:%d\n",pid);
    pid=getppid();
    printf("父进程id:%d\n",pid);
    return 0;    
}

在这里插入图片描述
可以看到几次运行由不同的进程号,但是父进程的id都一样,使用ps -ef查看进程信息,发现75057是你开的终端这个进程的id
在这里插入图片描述
11、创建新进程函数fork

函数原型:pid_t fork(void)
函数功能:创建新进程
函数参数:无
函数返回值:在父进程中返回子进程的PID,在子进程中返回0,失败返回-1
头文件:#include <unistd.h>
特点:fork成功后,会创建一个子进程,子进程会复制父进程资源父子进程同时从fork函数以下开始并行运行。互不干扰。拥有独立的数据段、堆栈,但无法确定父子进程的运行顺序

编程:创建一个子进程,分别在父子进程中打印进程id

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

int main(int argv,char * argc[])
{
    unsigned int pid;
    pid=fork();
    printf("进程id:%d\n",pid);
    return 0;    
}

在这里插入图片描述
可以看到,打印了两行pid,第一行显然是在父进程中打印的,第二行0是在子进程中打印的。
12、vfork函数

函数原型:pid_t vfork(void)
函数功能:创建子进程,并且阻塞父进程
函数参数:无
函数返回值:在父进程中返回子进程的PID,在子进程中返回0,失败返回-1
头文件:#include <unistd.h>
		#include <sys/type.h>
特点:vfork成功后,会创建一个子进程,子进程共用(独占)父进程资源,子进程退出父进程才会得到执行。分享父进程的数据段、堆,一定是子进程先运行

编程:fork和vfork的区别

1、fork子父进程不知道哪个先运行,vfork子先运行父后运行
2、fork父子进程拥有各自独立的空间,vfork公用空间

#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
int main(int argv,char * argc[])
{
	
	unsigned int pid,i=0;  
	pid=vfork();  
	if(pid!=0)
	{
		printf("父进程\n");
	}
	else
	{
		printf("子进程\n");
	}
	i++;
	printf("i:%d\n",i);
	_exit(0);//这里必须使用_exit(0);否则会段错误
}

在这里插入图片描述
可以看到i被加到2所以父子进程公用空间。
换成fork
在这里插入图片描述
在这里插入图片描述
输出两个1,所以父子进程都是独立的空间

13、特殊进程

0号进程:操作系统的引导程序
祖先进程:操作系统启动的第一个程序,1号进程
孤儿进程:父进程先退出,子进程被init接管,子进程退出后init会回收其占用的相关资源,缺点:子进程的相关资源无法清理回收
僵尸进程:子进程退出,父进程没有做清理工作。一种非常特殊的进程,它几乎已经放弃了所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间
		父进程退出会清理子进程
		查看僵尸进程的命名 ps -elf | grep defunct

14、进程等待wait

函数原型: pid_t wait(int *status);
函数功能:等待调用它的进程,直到子进程结束
函数参数:status 若为空,则代表任意状态结束的子进程,status 若不为空,则代表指定状态结束的子进程
函数返回值:成功返回终止的那个子进程的id,失败返回-1
头文件:#include <sys/types.h><sys/wait.h>
这个函数配合fork使用,因为当子进程的父进程先死去后子进程变成僵尸进程,使用这个函数可以让父进程先阻塞,等子进程结束后父进程再执行。

编程:子进程先运行,睡眠一秒后父进程运行

#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
#include <sys/wait.h>
int main(int argv,char * argc[])
{
	
	unsigned int pid,i=0;  
	int ss=0;
	pid=fork();  
	if(pid!=0)
	{
		wait(NULL);
		printf("父进程\n");
	}
	else
	{
		printf("子进程\n");
		sleep(1);
	}
	_exit(0);//这里必须使用_exit(0);否则会段错误
}

在这里插入图片描述
在这里插入图片描述
15、waitpid函数

函数原型:  pid_t waitpid(pid_t pid,int * status,int options);
函数功能:暂时停止目前进程的执行,直到有信号来到或子进程结束
函数参数:pid>0:等待指定的pid,pid-1:等待任意的PID。
		  status:保存进程退出时状态  一般为NULL
		  options:WNOHANG:若由 pid 指定的子进程不立即可用,则 waitpid 不阻塞,此时返回值为 00:同 wait,阻塞父进程,等待子进程退出
函数返回值:成功返回子进程识别码(PID) ,如果有错误发生则返回-1
头文件:#include <sys/types.h><sys/wait.h>
waitpid(pid,NULL,0);//指定的进程退出
waitpid(-1,NULL,0)//同wait(NULL);

编程

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


int main()
{
	pid_t pid1 = fork();
	if(pid1 == 0)
	{
		sleep(5);
		printf("i'm son father:%d \n",getppid());
		
	}
	else
	{
		waitpid(pid1,NULL,0);//阻塞			分别把这两句给注释就可以看到区别
		waitpid(pid1,NULL,WNOHANG);//非阻塞
		printf("im father son'spid:%d\n",pid1);
		printf("父进程死了\n");
	}
	return 0;
}

16、进程退出
在这里插入图片描述
exit和_exit的区别:

exit会清理缓冲区,将缓冲区的内容写入到文件。
_exit不会清理缓冲区,内容直接丢弃。

17、exec函数族
exec 函数族就提供了一个在进程中启动另一个程序执行,会覆盖原有进程,一般和vfork连用。

1)execl函数

函数原型:  int execl(const char *path, const char *arg, ...);
函数功能:使用完整的文件目录来查找对应的可执行文件。注意目录必须以“/”开头,否则将其视为文件名
函数参数:path:执行文件的路径,arg可执行文件所需要的参数,参数以NULL结尾。

函数返回值:无
头文件: #include <unistd.h>
当调用execl函数的时候,程序的代码段发生变化,变为execl要执行的功能的代码段,必须以 NULL 表示结束,如果使用逐个列举方式,那么要把它强制转化成一个字符指针

编程:使用execl函数在一个子进程中执行一个进程,进程为打印一个参数

//execl函数运行的程序
#include <stdio.h>
int main(int argc,char *args[])
{
	printf("%s\n",args[1]);
}
//主程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
	pid_t pid1 = vfork();
	if(pid1 > 0)
	{
		wait(NULL);
		printf("im father son'spid:%d\n",pid1);
		printf("父进程死了\n");
	}
	else if(pid1==0)
	{
		printf("son\n");
		execl("/home/xxx/Desktop/shell/2.out","2.out","execl重塑函数",NULL);
	}
	
	return 0;
}

现象:
在这里插入图片描述
18、execlp函数从环境变量中调用函数

函数原型: int execlp(const char * file,const char * arg,...,(char *)0);
函数功能:从环境变量中,运行命令
头文件: #include <unistd.h>
当调用execl函数的时候,程序的代码段发生变化,变为execl要执行的功能的代码段,必须以 NULL 表示结束,如果使用逐个列举方式,那么要把它强制转化成一个字符指针

用法:

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
	execlp("ls","ls","-l",NULL);
	return 0;
}

现象:
在这里插入图片描述
如何查看环境变量?
在终端输入echo $PATH即可查看
在这里插入图片描述
这个文件夹中全是命令ls、cat、mount等命令,可以将你的程序复制到这个文件夹中即可使用execlp函数运行。
19、shell命令调用函数system

函数原型:int system(const char *command);
函数功能:在程序中调用shell命令
函数参数:command:需要调用的shell命令
函数返回值:如果发生错误返回对应的错误码。否则返回0。
函数头文件:#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
	i=system("ls -l");
	printf("commond:%d\n",i);
	return 0;
}

在这里插入图片描述
20、glob函数

函数原型:int glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno),glob_t *pglob);
函数功能:找出某一路径下的某一种类型的文件,并将文件数和文件名存储在glob_t类型的结构体中
函数参数:pattern:路径下的文件格式例如:”/home/yym/mp3file/*.mp3”		flag:0,errgunc:NULL,PGLOB:要保存的位置
typedef struct {
         size_t gl_pathc; /*找到的文件个数*/
         char **gl_pathv;/*文件名:gl_pathv[0]  gl_pathv[1]  。。。。 */
         size_t   gl_offs; /* 不管 */
 } glob_t;

头文件:#include <glob.h>

编程找出一个文件中的所有txt文件并且打印名字

#include <stdio.h>
#include <glob.h>
int main()
{
	glob_t g={0};
	glob("./*.txt",0,NULL,&g);
	printf("文件数:%ld\n",g.gl_pathc);
	for(int i=0;i<g.gl_pathc;i++)
	{
		printf("%s\n",g.gl_pathv[i]);
	}
}

现象:在这里插入图片描述
21、安装MP3播放器:mgp123
shell命令:sudo apt-get install mpg123
mpg123的使用方式
格式:mpg123 XXX.mp3
在这里插入图片描述

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值