进程的概述(linux)

目录

一、进程

1. 进程的定义

2. PCB(进程控制块)

3. 进程调度算法

4. 进程状态

 5.进程体系

6.进程命令

二、进程相关API函数

1. 获取当前进程ID:getpid函数

2. 获取当前进程父进程ID:getppid

3. 创建进程

1)fork函数

2)vfork函数

3)system函数(应用层)

4)execl函数

4. 进程退出

1)return函数

2)exit函数

3)_exit函数

5. 注册退出函数(设置程序正常结束前调用的函数)

6. 释放无用子进程所占用资源

1)wait函数

2)waitpid函数

三、特殊进程

        1.孤儿进程

        2.僵尸进程


一、进程

1.1 进程的定义

        进程是指在程序编译之后(可执行文件),运行起来的二进制文件。整个过程包含:进程的创建、运行、消亡。进程和程序之间的区别是进程属于动态过程,程序属性静态过程。

1.2 PCB(进程控制块)

        为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块(PCB Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。是一个结构体

该结构体主要包含:1)进程id:每个进程都有唯一的id,用于区分各进程;进程号为“0的进程是调度进程,常被称为交换进程(空闲进程),它不属于任何程序,是内核中的一部分。因此也被称为系统进程(不可见)。进程号为“1的进程为始祖进程(祖先进程、init进程),系统内核启动后运行的第一个进程(系统初始化)。“init进程”负责引导系统、启动守护(后台)进程并且运行必要的程序。

                                2)进程状态:就绪态、执行态、等待态、表示进程当下的状态;

                                3)进程切换时需要保存和恢复的一些CPU寄存器状态;

                                4)描述虚拟地址空间的信息:虚拟地址和物理地址的对应关系;

                                5)当前工作目录;

                                6)umask掩码:保护文件创建和修改的权限;

                                7)文件描述符;

                                8)和信号相关的信息;

                                9)用户id和组id

                              10)会话和进程组;

1.3 进程调度算法

        1)先来先服务调度算法 2)短进程优先调度算法 3)高优先级调度算法 4)时间片轮询法。

1.4 进程状态

        进程的最基本状态分为:就绪态、等待态(阻塞态、挂起态)以及执行态(运行态)

        1)执行态:进程占有CPU资源,正在运行。

        2)就绪态:进程本身具备运行条件,系统分配处理器即可运行。

        3)等待态:进程本身不具备运行条件,即使给它分配处理器也无法执行,进程正在等待某一事件的发生。

1.5 进程体系

        1)  创建进程的进程为父进程,而新进程被称为子进程。

        2)  每个进程都是由其它进程创建(除了init进程),每个子进程都有一个父进程

        3)  每个进程都属于某个用户和某个组。

1.6 进程命令

        1)静态查询:ps 参数。-aux:查看进程状态,-axjf:查看进程详细信息   

        2)动态查询:top 参数

        3)前后台进程应用

                进程前后台切换:

                ① 在终端后台执行进程:./可执行文件 &

                ② 查看终端的后台进程:jobs 

                ③ 把终端后台进程带入前台进程:fg 后台进程号

                ④ 把前台进程已停止状态进入后台进程:ctrl + z

                杀死进程:kill 参数 进程ID

二、进程相关API函数

2.1 获取当前进程ID:getpid函数

        头文件: #include<unistd.h>

        函数原型: pid_t getpid(void);

        函数功能说明: getpid()用来取得目前进程的进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。

        返回值: 目前进程的进程识别码。(必然有返回)

#include <stdio.h>
#include <unistd.h>
int main()
{
	pid_t currentpid = getpid();
	printf("当前进程ID:%d\n",currentpid);
	while(1);
	return 0;
}

2.2 获取当前进程父进程ID:getppid

        头文件: #include<unistd.h>

        函数原型: pid_t getppid(void);

        函数功能: getppid()用来取得目前进程的父进程识别码。

        返回值: 目前进程的父进程识别码。(必然成功)

#include <stdio.h>
#include <unistd.h>
int main()
{
	pid_t currentpid = getpid();
	printf("当前进程ID:%d\n",currentpid);
	pid_t currentppid = getppid();
	printf("当前进程父进程ID:%d\n",currentppid);
	while(1);
	return 0;
}

2.3 创建进程

1)fork函数

        头文件: #include<unistd.h>

        函数原型: pid_t fork(void);

        函数功能: ① 会产生一个新的子进程

                        ② 其子进程会复制父进程的数据与堆栈空间;

                        ③ 继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。

                        ④ 子进程对变量的修改和父进程并不会同步。

        返回值: 如果 fork()成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回 0。如果 fork 失败则直接返回-1,失败原因存于 errno中。

#include <stdio.h>
#include <unistd.h>
int main()
{
	int A =10;
	pid_t currentpid = getpid();
	printf("当前进程ID:%d\n",currentpid);
	//创建进程
	pid_t pid=fork();
	if(pid > 0)
	{
		while(1)
		{
			printf("父进程:%d\n",getpid());
			sleep(1);
			printf("父进程中A:%d\n",A);
		}
	}
	else if(pid == 0)
	{
		while(1)
		{
			printf("子进程:%d---%d\n",getpid(),getppid());
			sleep(1);
			printf("子进程进程中A:%d\n",A++);
		}
	}
	else
	{
		perror("fork");
		return 1;
	}
	return 0;
}

2)vfork函数

        头文件: #include<unistd.h>

        函数原型: pid_t vfork(void);

        函数功能: ①会产生一个新的子进程

                        ②其子进程会复制父进程的数据与堆栈空间;

                        ③继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。

                        ④Vfork函数数据区是父子进程共享的。(全局变量区堆区)

                        ⑤父进程运行到vfork后会进入无法中断休眠态,子进程先运行,子进程结束后父进程才运行;

                        ⑥子进程先于父进程退出,子进程变为僵尸进程;

                        ⑦Exit退出函数,在子进程中调用exit函数会导致子进程退出;

                        ⑧Execl该函数也会导致父进程不再阻塞,父子进程抢占运行;

        返回值: 如果 vfork()成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回 0。如果 fork 失败则直接返回-1,失败原因存于 errno中。

#include <stdio.h>
#include <unistd.h>
int A =10;
int main()
{
	pid_t currentpid = getpid();
	printf("当前进程ID:%d\n",currentpid);
	//创建进程
	pid_t pid=vfork();
	if(pid > 0)
	{
		while(1)
		{
			printf("父进程:%d\n",getpid());
			sleep(1);
			printf("父进程中A:%d\n",A);
		}
	}
	else if(pid == 0)
	{
		for(int i = 0; i <15;i++)
		{
			printf("子进程:%d---%d\n",getpid(),getppid());
			sleep(1);
			printf("子进程进程中A:%d\n",A++);
		}
	}
	else
	{
		perror("fork");
		return 1;
	}
	return 0;
}

3)system函数(应用层)

        头文件: #include<stdlib.h>

        函数名称: int system(const char * string);

        函数功能: system()会调用 fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数 string 字符串所代表的命令,此命令执行完后随即返回原调用的进程。

        返回值: 如果 system()在调用/bin/sh 时失败则返回 127,其他失败原因返回-1。若参数 string 为空指针(NULL),则返回非零值。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	printf("test1可执行文件\n");
	system("./fun");    //调用命令或需要运行的可执行文件
	while(1)
	{
		printf("test1循环\n");
		sleep(1);
	}
	return 0;
}

4)execl函数

        头文件: #include<unistd.h>

        函数原型: int execl(const char * path,const char * arg,....);

        函数功能: execl()用来执行参数 path 字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的 argv0〕、argv[1]……,最后一个参数必须用空指针(NULL)作结束。

        返回值: 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于 errno 中。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	printf("test1可执行文件\n");
	sleep(12);
	execl("./fun","./fun",NULL);
	while(1)
	{
		printf("test1循环\n");
		sleep(1);
	}
	return 0;
}

2.4 进程退出

1)return函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	printf("hello\n");
	return 0;
}

2)exit函数

        头文件: #include<stdlib.h>

        函数原型: void exit(int status);

        函数功能: exit()用来正常终结目前进程的执行,并把参数 status 返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。

        返回值:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void fun()
{
	printf("join in fun function\n");
	exit(3);
	printf("exit fun function\n");
}
int main()
{
	printf("hello\n");
	fun();
	printf("main return \n");
	return 0;
}

3)_exit函数

        头文件: #include<unistd.h>

        函数原型: void _exit(int status);

        函数功能: _exit()用来立刻结束目前进程的执行,并把参数 status 返回给父进程,并关闭未关闭的文件。此函数调用后不会返回,并且会传递SIGCHLD 信号给父进程,父进程可以由 wait 函数取得子进程结束状态。

        返回值:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void fun()
{
	printf("join in fun function");
	_exit(3);
	printf("exit fun function\n");
}
int main()
{
	printf("hello\n");
	fun();
	printf("main return \n");
	return 0;
}

        注意:exit与_exit都是结束进程,_exit是直接结束程序(不再运行_exit往下的内容),exit是刷新缓冲区再退出整个程序

2.5 注册退出函数(设置程序正常结束前调用的函数)

        头文件: #include<stdlib.h>

        函数原型:int atexit (void (*function)(void));

        函数功能: atexit()用来设置一个程序正常结束前调用的函数。当程序通过调用 exit()或从 main 中返回时,参数 function 所指定的函数会先被调用,然后才真正由 exit()结束程序。

        返回值: 如果执行成功则返回 0,否则返回-1,失败原因存于 errno 中。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void exitfunction(void)
{
	printf("join in fun function\n");
	printf("exit fun function\n");
}
int main()
{
	atexit(exitfunction);
	printf("hello\n");
	exit(2);
	printf("main return \n");
	return 0;
}

2.6 释放无用子进程所占用资源

1)wait函数

        头文件: #include <sys/types.h>

                     #include <sys/wait.h>

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

        函数功能: wait()会暂时停止目前进程的执行,直到有信号来到或子进程结 束。如果在调用 wait()时子进程已经结束,则 wait()会立即返 回子进程结束状态值。子进程的结束状态值会由参数 status 返回, 而子进程的进程识别码也会一快返回。如果不在意结束状态值,则 参数status 可以设成 NULL。

        返回值: 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回 -1。失败原因存于 errno 中。

#include<sys/types.h> 
#include<sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	pid_t pid = fork();
	if(pid > 0)
	{
		while(1)
		{
			printf("我是父进程\n");
			sleep(1);
			wait(NULL);
		}
		printf("父进程退出\n");
	}
	else if(pid == 0)
	{
		for(int i = 0; i< 10; i++)
		{
			printf("我是子进程\n");
			sleep(1);
		}
	}
	return 0;
}

2)waitpid函数

        头文件: #include <sys/types.h>

                     #include <sys/wait.h>

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

        函数功能: waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则 wait()会立即 返回子进程结束状态值。子进程的结束状态值会由参数 status 返回, 而子进程的进程识别码也会一快返回。如果不在意结束状态值,则 参数 status 可以设成 NULL

        参数 pid 为欲等待的子进程识别码, 其他数值意义如下:

        pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。

        pid=-1 等待任何子进程,相当于 wait()返回值 范例 参考 wait()。

        pid=0 等待进程组识别码与目前进程相同的任何子进程。

        pid>0 等待任何子进程识别码为 pid 的子进程。

        参数 option 可以为 0 或下面的 OR 组合:

        WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以 等待。

        WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

        子进程的结束状态返回后存于 status,底下有几个宏可判别结束情 况:

        WIFEXITED(status)如果子进程正常结束则为非 0 值。

        WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一 般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。

        WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为 真

        WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般 会先用 WIFSIGNALED 来判断后才使用此宏。

        WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为 真。一般只有使用 WUNTRACED 时才会有此情况。

        WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先 用 WIFSTOPPED 来判断后才使用此宏。

        返回值: 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回 -1。失败原因存于 errno 中。

#include<sys/types.h> 
#include<sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
    int status;
	pid_t pid = fork();
	if(pid > 0)
	{
		while(1)
		{
			printf("我是父进程\n");
			sleep(1);
			waitpid(0,&status,WNOHANG);
		}
		printf("父进程退出\n");
	}
	else if(pid == 0)
	{
		for(int i = 0; i< 10; i++)
		{
			printf("我是子进程\n");
			sleep(1);
		}
	}
	return 0;
}

三、特殊进程

3.1 孤儿进程

                孤儿进程的本质上还是个普通的进程,是指一个父进程退出,而它的一个或者多个子进程还在运行,那么这些子进程就称为孤儿进程。孤儿进程会被系统指定的进程所收养,并由收养孤儿进程的进程对他们进程完成状态收集工作。

注:在“红帽Linux系统”中,孤儿进程将被“init进程(进程号为1)”的进程所收养,但是在“乌班图Linux系统中”,是由“upstart进程”所收养。

3.2 僵尸进程

                Linux的进程在结束的时候不会对自己的资源进行清零,只有父进程结束时才会对子进程的资源进程清零。僵尸进程是指子进程先于父进程退出,父进程退出的时候没有回收子进程退出状态的资源(task_struct),因此子进程就称为僵尸进程(孩子死了,父亲没有收尸)。写程序时要尽量避免出现僵尸进程(系统垃圾),利用等待进程退出函数(wait/waitpid)可以解决僵尸进程问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值