Linux系统进程控制基础

进程基础

1.进程相关概念

什么是程序?

  • 编译好的二进制文件

什么是进程?

  • 运行着的程序
  • 站在程序员的角度:运行一系列指令的过程
  • 站在操作系统角度:分配系统资源的基本单位

程序和进程的区别

  • 程序占用磁盘,不占用系统资源
  • 内存占用系统资源
  • 一个程序对应多个进程,一个进程对应一个程序
  • 程序没有生命周期,进程有生命周期

2、进程控制块PCB

在这里插入图片描述

进程控制块结构体声明在:/usr/include/linux/sched.h

进程和程序的区别?

​ (1)位置不一样:程序文件放在外部存储器,进程在内存中(正在运行的程序)

​ (2)程序文件:静态的,进程:动态的

3、进程的组织方式

①父子进程

​ (1)查看进程树,以树形图显示进程:pstree

​ (2)除了系统初始init进程以外,任何进程都必须有父进程

​ (3)如果一个进程的父进程死掉,那么这个进程就会被系统分配一个默认父进程给它

②进程运行

​ (1)./程序文件名 前台进程

​ (2)./程序文件名 & 后台进程

前台进程可以直接用ctrl+c关闭,后台进程不行,我们可以用kill <进程号>关闭该进程

当你的当前终端关闭,前台进程会关闭;后台进程不会关闭

Linux系统中每一个进程都有父进程,当这个进程的父进程死掉,子进程如果没有跟着父进程一起死掉,又没有被分配默认的父进程,就变成孤儿进程,但是linux不允许,它会将这个进程作为一个特殊的进程(类似孤儿院)的子进程。

③查看正在运行的进程

ps命令,默认显示当前终端的所有进程

选项:

​ -f 查看更多信息

​ -u 显示更多信息(和-f显示的信息不一样)

​ -a 显示所有用户的所有进程

​ -x 显示无终端的进程

​ -e 显示所有进程

ps -ef

④终止进程

kill <信号> <进程号>

kill -9 <进程号>

kill -l

在这里插入图片描述

⑤动态显示正在运行的进程

top

退出用q

4、进程的状态

在这里插入图片描述

  1. 就绪态
  2. 暂停态
  3. 睡眠态,睡眠进程既占用内存资源又占用CPU资源,睡眠的恢复是自动完成的,因为睡眠有一个睡眠时间,睡眠时间到则恢复到就绪态
  4. 挂起态,挂起进程不再占用CPU和内存资源
  5. 执行态
  6. 僵尸态
  7. 死亡态

5.进程相关函数

①创建一个新的进程

pid_t fork(void);

返回值:

  • 失败 -1
  • 成功,两次返回
    • 父进程返回子进程的ID
    • 子进程返回0
      在这里插入图片描述

获得pid,进程id,获得当前进程

  • pid_t getpid(void);

获得当前进程父进程的id

  • pid_t getppid(void);
②父子进程的联系和区别

(1)相同的地方

​ A) 实际 UID 和 GID,以及有效 UID 和 GID。

​ B) 所有环境变量。

​ C) 进程组 ID 和会话 ID。

​ D) 当前工作路径。除非用 chdir()加以修改

​ E) 打开的文件。

​ F) 信号响应函数。

​ G) 整个内存空间,包括栈、堆、数据段、代码段、标准 IO 的缓冲区等等。

(2)不同的地方

​ A) 进程号 PID。PID 是身份证号码,哪怕亲如父子,也要区分开。

​ B) 记录锁。父进程对某文件加了把锁,子进程不会继承这把锁。

​ C) 挂起的信号。这些信号是所谓的“悬而未决”的信号,等待着进程的响应,子进程也不会继承这些信号。

循环创建n个子进程

如果在循环中子进程不中断循环,将出现问题,子进程会在循环中继续创建子进程
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
	int n = 5;

	int i =0;
	pid_t pid = 0;
	for(i = 0; i < 5 ; i ++)
    {						//父进程循环结束
		pid = fork();
		if(pid == 0)
        {
			//son
			printf("I am child ,pid=%d,ppid=%d\n",getpid(),getppid());
			break;//子进程退出循环的接口
		}else if(pid > 0){
			//father
			printf("I am father,pid=%d,ppid=%d\n",getpid(),getppid());
		}
	}
	while(1)
    {
		sleep(1);
	}
	return 0;
}
③exec函数簇

功能:在进程中加载新的程序文件或者脚本,覆盖原有代码,重新运行

在这里插入图片描述

函数原型

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[ ]);
int execle(const char*path, const char *arg, .., char * const envp[ ]);

int execlp(const char *file, const char *arg,..);
int execvp(const char *file, char *const argv[ ]);
int execvpe(const char*file, char *const argv[ ],char *const envp[ ]);

参数

path即将被加载执行的ELF文件或脚本的路径
file即将被加载执行的ELF文件或脚本的名字
arg以列表方式罗列的ELF文件或脚本的参数
argv以数组方式组织的ELF文件或脚本的参数
envp用户自定义的环境变量数组

返回值:成功,不返回;失败,返回-1

备注

1,函数名带字母1意味着其参数以列表(list)的方式提供。
2,函数名带字母v意味着其参数以矢量(vector)数组的方式提供。
3,函数名带字母p意味着会利用环境变量PATH来找寻指定的执行文件。
4,函数名带字母e意味着用户提供自定义的环境变量。

被加载的文件的参数列表必须以自身名字为开始,以 NULL为结尾。

比如要加载执行当前目录下的一个叫做a.out的文件,需要一个参数"abcd",那么正确的调用应该是:

execI(./a.out", "a.out", "abcd",NULL);
或者;
const char *argv[3] = {"a.out", "abcd",NULL};
execv("./a.out", argv);

exec函数簇成功执行后,原有的程序代码都将被指定的文件或脚本覆盖,因此这些函数一旦成功后面的代码是无法执行的,他们也是无法返回的。

④程序退出

return

函数退出,当return放在main函数的时候,表示程序退出

exit()函数

#include <stdlib.h>

void exit(int status);

_exit()函数

#include <unistd.h>

void _exit(int status);

退出处理函数注册

#include <stdlib.h>
int atexit(void (*function)(void));

退出本进程

    # include <unistd.h>
    #include <stdlib.h>
原型
    void _exit(int status);
	void exit(int status);
参数
    status子进程的退出值
返回值	不返回
备注
	1,如果子进程正常退出,则status一般为02,如果子进程异常退出,则statuc一般为非03exit()退出时,会自动冲洗(flush)标准IO总残留的数据到内核,如果进程注册了“退出处理函数”还会自动执行这些函数。而_exit()会直接退出。    
⑤等待子进程退出/状态变化

wait、waitpid

功能
等待子进程
头文件
#include <sys/wait.h>
原型
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
参数
pid:
小于-1:等待组ID的绝对值为pid的进程组中的任一子进程
-1:等待任一子进程
0:等待调用者所在进程组中的任一子进程大于0:等待进程组ID为pid的子进程
stat_loc:	子进程退出状态
option:
WCONTINUED:报告任一从暂停态出来且从未报告过的子进程的状态
wNOHANG:非阻塞等待
wUNTRACED:报告任一当前处于暂停态且从未报告过的子进程的状态成功:



返回值:

wait()
失败: -1
成功:退出的子进程PID

waitpid()
成功:状态发生改变的子进程PID(如果 WNOHANG被设置,且由 pid指定的进程存在但状态尚未发生改变,则返回0)。
失败: -1
    
备注:
如果不需要获取子讲程的很出状态,stat loc可以设置为NULL

相关宏的含义

WIFEXITED(status)如果子进程正常退出,则该宏为真。
WEXITSTATUS(status)如果子进程正常退出,则该宏将获取子进程的退出值。
WIFSIGNALED(status)如果子进程被信号杀死,则该宏为真。
WTERMSIG(status)如果子进程被信号杀死,则该宏将获取导致他死亡的信号值。
WCOREDUMP(status)如果子进程被信号杀死且生成核心转储文件(core dump) ,则该宏为真。
    
WIFSTOPPED(status)如果子进程的被信号暂停,且 option中 WUNTRACED已经被设置时,则该宏为真。

wSTOPSIG(status)如果 WIFSTOPPED(status)为真,则该宏将获取导致子进程暂停的信号值。
WIFCONTINUED(status)如果子进程被信号SIGCONT重新置为就绪态,该宏为真。

实现父子进程同步互斥机制用那两个函数?

什么是同步?什么是互斥?

父进程没有用延时或者wait/waitpid等待子进程,父子进程同时运行

父进程使用延时或者wait/waitpid一直等到子进程结束,父进程才会运行

⑥孤儿进程与僵尸进程

孤儿进程:父亲死了,子进程被init进程领养。
僵尸进程:子进程死了,父进程没有回收子进程的资源(PCB)

在这里插入图片描述

如何回收僵尸进程:杀死父亲,init领养,负责回收。

⑦子进程回收

在这里插入图片描述

test

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

int main(int argc, char const *argv[])
{
	int i,wstatus;
	char *funcdir[3] = {"./f1","./f2","./f3"};
	char *func[3] = {"f1","f2","f3"};
	pid_t _pid[3],w;

	for(i=0;i<3;i++)
	{
		_pid[i] = fork();
		if(_pid[i] == 0)
		{
			execl(funcdir[i],func[i],NULL);
		}
		if(_pid[i] < 0)
		{
			perror("fork()");
			exit(0);
		}
	}
	for (int i = 0; i < 3; ++i)
	{
		do {
			w = waitpid(_pid[i],&wstatus,WUNTRACED | WCONTINUED);
			if (w == -1) 
			{
                perror("waitpid");
                exit(EXIT_FAILURE);
            }
			if (WIFEXITED(wstatus)) {
	           printf("[%d] exited, status=%d\n", w,WEXITSTATUS(wstatus));
	       	} else if (WIFSIGNALED(wstatus)) {
	           printf("[%d] killed by signal %d\n",w, WTERMSIG(wstatus));
	       	} else if (WIFSTOPPED(wstatus)) {
	           printf("[%d] stopped by signal %d\n",w, WSTOPSIG(wstatus));
	       	} else if (WIFCONTINUED(wstatus)) {
	           printf("[%d] continued\n",w);
		    } 
		} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
		// exit(EXIT_SUCCESS);
	}
	printf("父进程结束\n");

	return 0;
}

atexit


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

int *p;
void func()
{
	printf("%s_________\n",__FUNCTION__);
	printf("程序退出处理函数\n");
	free(p);
	return;
}


int main(int argc, char const *argv[])
{
	atexit(func);	//exit前执行退出处理函数

	printf("%s_________\n",__FUNCTION__);

	printf("%d_____________malloc______\n",__LINE__);
	p= malloc(4);
	exit(0);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值