嵌入式系统及应用Linux学习笔记(五)---进程管理

进程的概念

  • 程序是一个包含可以执行代码的文件,是一静态的文件。
  • 进程是一个开始执行但还没有结束的程序的实例,就是可执行文件的具体实现,是一个动态的概念。
  • Linux系统上所有运行的东西都可以称为一个进程。
  • 在一个CPU上,可以存在多个进程;在同一时间内,一个CPU只能有一个进程工作。操作系统通过一定的调度算法管理算有进程。
  • Linux系统至少有一个进程。一个程序可以对应多个进程,一个进程只能对应一个程序。
  • 进程这个概念是针对系统而不是针对用户的
  • 用户面对的概念是程序:当用户敲入命令,执行一个程序; 对系统而言,它将启动一个进程。 和程序不同的是,系统不
  • 仅仅是在运行一个程序,系统为了管理、控制、协调整个计算机,有可能要运行很多个“程序”—这些程序有些在用户看来可能是“同时”在运行着,但实际上系统在后台进行着相应的调度
  • 多进程编程:为了更高效的使用CPU 在同一个时间做多个任务

进程的基本状态三种:运行态、就绪态和阻塞态(或等待态)。

在这里插入图片描述

进程号pid

  • 每个进程有唯一的pid,可以用getpid()函数获取
pid_t getpid(void);
pid_t getppid(void);

说明:
getpid()返回调用该系统调用的进程的pid号;
getppid()返回调用该系统调用的进程的父进程pid号。

#include <stdio.h>

int main()
{
  printf("Current process %d\n",getpid());
  printf("Parent process %d\n",getppid());
  return 0;
}

进程控制-初识fork

  • fork函数:产生子进程
    • fork:分叉
    • 子进程完全复制父进程的资源
    • 子进程的执行独立于父进程
  • 表现形式:调用一次,返回2次
  • 在子进程里:返回0
  • 父进程里:返回子进程的pid
  • 头文件: unistd.h
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
    int   p_id;
    p_id=fork( );
    if (p_id==0)
        printf("child  process\n");
    else
        printf("parent process\n");
}
  • 原有的进程称为父进程,新生的进程称为子进程
    在这里插入图片描述

进程的创建过程

在这里插入图片描述

在这里插入图片描述

  • fork返回值小于0表示创建进程失败
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
    int   p_id;
    p_id=fork( );
    if(p_id < 0) {
         printf("create process error\n");
         exit(-1);
     }
    if (p_id==0)
        printf("child  process\n");
    else{
        printf("parent process\n");
        wait();
      }
}

最简单的进程同步:wait

  • fork产生子进程后,父子进程在系统调度下执行

    • 父子进程的调度顺序是随机的
  • 如果希望父进程在子进程结束后再结束,可以使用wait函数:

    • pid_t wait (int * status); (头文件: wait.h)
    • wait函数会“暂停”目前进程的执行,直到有信号来到或子进程结束
    • 如果在调用 wait()时子进程已经结束,则 wait()会“立即”返回子进程结束状态值,如果有错误发生则返回返回值-1。失败原因存于 errno 中。
    • 子进程的结束状态值(如exit函数中的值)会存放在参数 status 中, 而wait函数的返回值是子进程的进程号
    • waitpid:pid_t waitpid(pid_t pid,int * status,int options);
    • 等待指定的子进程结束

在这里插入图片描述

  • wait :父进程等待“任一”子进程结束

  • waitpid:等待指定的子进程结束

  • 如何等待所有子进程结束?

    • if ( wait() >0 ) 说明有子进程结束,判断一次
    • while ( wait() > 0 ) ;

僵尸进程和孤儿进程

  • 僵尸进程:一个子进程在其父进程还没有调用wait()waitpid()的情况下退出。这个子进程就是僵尸进程。

  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。

    • 孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
  • 僵尸进程将会导致资源浪费

    • 进程结束后仍有一些信息,包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process

僵尸进程

在这里插入图片描述

多个子进程

分析程序有多少个输出?

int main(){
    int   p_id;
    p_id=fork( );
if (p_id==0)
        printf("child  process 1\n");
    else
    {
        wait();
        printf("parent process 1\n");
    }
    p_id=fork( );
    if (p_id==0)
        printf("child  process 2\n");
    else
    {  
        wait();
        printf("parent process 2\n");
    }
}

在这里插入图片描述

进程链

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
    int i;
    pid_t child_pid;
    for(i=1;i<3;i++)
    { 
       child_pid = fork();
        if( child_pid==0 )
            { printf( "do something \n"); 
              return 0;
             }
        else
            { wait(); 
            }
    }
    return 0;

}
  • 程序的输出是怎样的?
  • 运行后,用多进程去解释你看到的输出 程序中红色的return 0 和wait有何意义?
  • return 0使得子进程在执行完自身代码后直接返回(类似函数),不再执行if else之后的代码,即不再参与fork函数的第二次调用。

多进程-执行其他程序

  • fork创建子进程后,子进程执行的是和父进程相同的程序(执行不同的代码分支),子进程通常调用exec函数系列来执行另一个程序。

  • 当进程调用exec函数系列时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。

  • 进程的PID不会改变,依然是fork函数调用时产生的PID。

  • exec函数族

    • execlp(可执行文件名, 参数0,参数1,......,NULL) 函数不会返回,除非出错, 执行失败则直接返回-1,失败原因存于errno 中
    • 系统会在path路径中去找可执行文件
    • 参数从参数0开始(类似于main参数),程序执行的参数从参数1开始, 参数0可以为空,或是可执行文件名
    • exec其他函数
      • execl,execlp,execle,execv,execve,execvp

exec函数族参数

#include <unistd.h>
extern char **environ;
int execve(const char *filename, char *const argv[], char *const envp[]);
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[]);

fork产生进程的缺陷

  • 由于在使用fork时,内核会将父进程拷贝一份给子进程,但是这样的做法相当浪费时间,因为大多数的情形都是程序在调用fork后就立即调用exec,这样刚拷贝来的进程区域又立即被新的代码和数据覆盖掉

vfork特点

  • 创建时,父子共享一块空间
  • 父进程睡眠,子进程运行
  • 父进程醒来时机:
    • 子进程运行结束
    • 子进程执行其它任务

fork:父子进程拷贝数据段;父子进程的执行次序不确定 vfork:父子进程共享数据段;子进程先运行,父进程后运行

vfork使用

头文件:#include<unistd.h>
#include <sys/types.h>
格式: pid_t vfork();
返回值:
0:子进程
子进程ID(大于0的整数):父进程
-1:出错

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
void main()
{	pid_t pid;
	int x,y;
	x=20,y=30;
	pid=vfork();
	if(pid<0)
	{	printf("oh,my god,create a baby failed!\n");
		exit(1);
	}else if(pid==0)
	      {	x=40,y=50;
			sleep(1);
			printf("child:x=%d,y=%d\n",x,y);
			exit(0);	
	       }
	printf("parent:x=%d,y=%d\n",x,y);
}

运行结果:

child:x=40,y=50
parent:x=40,y=50
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int count=0;
pid = fork();
count++;
printf( “count = %d\n", count );
return 0}

输出:

count = 1
count = 1

子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而非共享。

#include <unistd.h>
#include <stdio.h>
int main(void)
{
 pid_t pid;
 int count=0;
 pid = vfork();
 count++;
 printf( “count = %d\n", count );
 return 0}

输出:

count = 1
count = 2

共享!!
非拷贝

进程终止

  • 在Linux环境中,一个进程的结束,可以采用调用函数 或者 接收到某个特定的信号而结束。
  • 调用函数结束进程

exit函数

函数原型

void exit(int status);

作用
用来终止正在运行的进程,使其进入僵死状态,等待父进程进行善后处理,该调用会尽可能释放当前进程占用的资源、清空缓冲区。将关闭所有被该文件打开的文件描述符
status是返回给父进程的一个整数。

#include<stdio.h>
#include<stdlib.h>
main()
{
   printf("This process will exit!\n");
   exit(0);
   printf("Never be displayed!\n");
}

_exit函数

  • 函数原型
    • void _exit(intstatus)
  • 作用
    • _exit()函数也可用于结束一个进程,该调用不会刷新输入输出缓冲区,因此进程结束前必须自己刷新缓冲区,或者改用exit()系统调用。

sleep函数调用

  • 系统调用sleep用来使进程主动进入睡眠状态,该函数的一般形式是:
sleep(秒数);
  • 执行该系统调用后,进程将进入睡眠状态,直到指定的秒数已到。正常情况下,该调用的返回值为0,若是因为被信号所唤醒,则返回值为原始秒数减去已睡眠秒数的差。
ps
进程查看命令
ps  [选项] 
-e:显示所有进程。 
-f:全格式。
-h:不显示标题。
-l:长格式
-w:宽输出
a:显示终端上的所有进程,包括其他用户的进程
r:只显示正在运行的进程
x:显示没有控制终端的进程
u:使用用户格式输出

在这里插入图片描述

进程管理相关命令

kill

当用户需要中断一个前台进程的时候,通常是使用<Ctrl+c>组合键;
对于一个后台进程须求助于kill命令
kill命令的语法格式很简单,大致有以下两种方式:

kill  [-s 信号 | -p ] [ -a ] 进程号
kill  -l [信号] 

在这里插入图片描述

top

显示系统当前的进程和其他状况

top [-dqsiupSc] [-d count] [-s time] [-u username]

常用选项含义如下:
d:指定每两次屏幕信息刷新之间的时间间隔。
q:表示没有任何延迟地进行刷新。
s:表示安全模式下运行。

top

显示系统当前的进程和其他状况

top [-dqsiupSc] [-d count] [-s time] [-u username]

常用选项含义如下:
d:指定每两次屏幕信息刷新之间的时间间隔。
q:表示没有任何延迟地进行刷新。
s:表示安全模式下运行。
在这里插入图片描述

wait

wait命令将实现对一个进程的等待。命令格式为:

wait  [n]

等待进程号为n的一个进程的完成并将报告进程的终止状态。没有参数,则将等待所有后台进程的完成并返回代码0。
示例:等待进程号为13199的进程结束。
在这里插入图片描述
sleep
sleep命令,将进程的执行挂起一段时间。语法格式为:

sleep time 

即使得shell挂起time秒后,再继续执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

iNBC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值