嵌入式Linux _ 进程编辑及守护进程

一、进程的概念(了解)

  • 程序:编译好的可执行文件

        存放在磁盘上的指令和数据的有序集合。

        静态的。

  • 进程

         执行一个程序所分配的资源的总称。

         进程是程序的一次执行过程。

         动态的,包括创建、调度、执行和消亡。

         包括 :正文段 、用户数据段、系统数据段 (前两个称为程序)。

                  

  •  进程控制块(pcb) ------  包括在系统数据段

           进程标识(PID) :正整数。

           进程用户

           进程状态、优先级

          文件描述符表

          PC寄存器:program counter 程序寄存器 ,存放下一条指定的地址,去译码执行。

注:每一个进程有时间片。

二、进程包含的内容(理解)

  • 进程的类型

       交户进程:在shell下运行。可以在前台运行,也可以在后台运行。

       批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行。

       守护进程:和终端无关,一直在后台运行,直到系统关闭。(很重要)

三、进程的状态(理解)

  • 运行态:进程 正在运行,或者准备运行(就绪态)
  • 等待态:进程在等待一个事件的发生或某种系统资源

       分为:可中断  (是否会被某种信号打断)

                  不可中断

  • 停止态:进程被中止,收到信号和后可继续运行。
  • 死亡态:已终止的进程,但pcb没有被释放。(多进程尽量避免此状态的出现)

                                      

四、查看进程信息

  1.         ps         查看系统进程快照   ps –ef|grep test       ps –aux|grep test
  2.         top        查看进程的动态信息  top 
  3.         /proc     查看进程详细信息   cd /proc
  • 前后台进程切换

 运行一个后台进程: ./test  &  加一个取地址符

  • jobs          查看后台进程 
  • Ctrl + z : 将前台进程 放到后台 挂起。
  • bg            将挂起的进程在后台运行
  • fg            把后台运行的进程放到前台运行 fg  1(作业号) 也可把后台挂起的进程放到前台运行。

五、改变进程优先级

  1. nice        按用户指定的优先级运行进程。 nice  -n 2  ./test  将./test的nice值修改为2,普通用户最小修改的nice值为0.
  2. renice     改变正在运行进程的优先级。renice –n 2 29070  将正在运行的nice值为29070进程的nice修改为2 ,普通用户只能降                  低优先级,不能提高(nice值只能改大)。
  3. 小结

   pos    :

   /proc  :           一个放入进程详细信息的目录。

   nice /renice :

六、创建子进程(熟悉)

    #include <unistd.h>

    pid_t fork(void);

  1. 创建新的进程,失败是返回-1;
  2. 成功时父进程返回子进程的进程号,子进程返回0;
  3. 通过fork的返回值区分父进程和子进程;

示例:

                      

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


int main(int argc, const char *argv[])
{
	pid_t pid;

	if((pid = fork()) < 0)   //子进程返回0 ,父进程返回 子进程PID
	{
		perror("fork");
		return -1;
	}else if(pid == 0)
	{
		printf("child process: my pid is %d \n",getpid());
		printf("chilie");
		exit(0);
	}else
	{
		printf("parent process :my pid is %d \n",getpid());
	}

	return 0;
}

七、父子进程(熟悉)

  • 子进程继承了父进程的内容。

              继承了父进程的 代码,数据,以及大部分系统数据

              两者的PID、PPID不一样

  • 父子进程有独立的地址空间,互不影响。
  • 若父进程先结束

           子进程成为孤儿进程,被init进程收养。 (子进程必须由父进程来回收,且子进程号不会改变,但子进程号的父进程号可能会改变)

           子进程变成后台进程

  • 若子进程先结束

         父进程如果没有及时回收,子进程变成僵尸进程。(pcb由父进程来收回,释放)

  • 思考

       1、子进程从何处开始运行?

              子进程从fork的下一条指令开始执行。

       2、父子进程谁先执行?

              由系统调度,不确定谁先执行。

        3、父进程中能否多次调用fork?子进程呢?

              父进程可多次调用fork,多次创建子进程。

             子进程中也可调用fork,相当于子进程创建了子进程,称为孙进程。

八、结束进程(熟悉)

  • exit/_exit

      #include <stdio.h>

      #include <unistd.h>

       void exit(int status);

       void _exit(int status);

  • 结束当前的进程 并将 status的低八位返回。
  • exit结束进程时会刷新(流)缓冲区。
  • _exit结束进程时不会刷新(流)缓冲区,缓冲区中的数据将会直接丢弃掉。

 

九、exec函数族(熟悉)   ------ 一般包含六个函数

  • 作用:进程调用extc函数执行某个程序。
  • 进程当前内容被指定的程序替换。
  • 实现让父子进程执行不同的程序

      注:父进程创建子进程,子进程调用exec函数族,父进程不受影响。

  1. execl / execlp

   #include  <unistd.h>

   int execl(const char *path,const char *arg,…);  //后面的都是字符串

    int execlp(const char *file,const char *arg,…);                

  • 成功时执行指定的程序;失败时返回EOF;
  • path 执行的程序名称,包含路径;
  • arg… 传递给执行的程序的参数列表;
  • file 执行的程序的名称,在PATH中查找;

示例:      

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


int main(int argc, const char *argv[])
{
	pid_t pid;

	if(execl("/bin/ls","ls","-a","-l","/etc",NULL) < 0)
	{
		perror("execl");
	}
	if(execlp("ls","ls","-a","-l","/etc",NULL) < 0)
	{
		perror("execl");
	}
	return 0;
}
  1. execv  /execvp

          int execv(const char *path , char *const argv[]);

          int execvp(const char *file , char *const argv[]);

  • 成功时执行指定的程序;失败时返回EOF;
  • arg… 封装成指针数组的形式

十、system(熟悉)

  • 进程  ----  system

      #include <stdlib.h>

       int system(const char *command);

  • 当前进程中,自动创建一个子进程,去执行command里面包含的程序,执行成功了返回命令command的返回值,失败了返回EOF。
  • 父进程等到子进程command中的程序执行完了后,继续执行自己的之后的程序。

小结

  • execl/execlp   :使用字符串
  • execv/execvp  :使用数组
  • system        :直接创建一个子程序执行命令。

十一、主要讲解如何回收子进程(如何回收,什么时候回收)

  • wait                                                                                                                  
  • 子进程结束时由父进程回收。
  • 孤儿进程由init进程回收。
  • 若没有及时回收会出现僵尸进程。

1、进程回收  -----  wait

      #include <stdio.h>

      pid_t  wait(int *status)

  • 成功时返回回收的子进程的进程号;失败时返回EOF;
  • 若子进程没有结束,父进程一直阻塞;
  • 若有多个子进程,哪个先结束就先回收; -----  调用wait 一个一个去回收;
  • status 指定保存子进程返回值 和结束方式的地址;
  • status 为NULL 表示直接释放 子进程 PCB ,不接收返回值;

                                          

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

int main(int argc, const char *argv[])
{
	int status;
	pid_t pid;

	if((pid = fork()) < 0)
	{
		perror("fork");
		return -1;
	}else if(pid == 0)
	{
		printf("zhob \n");
		sleep(1);
		exit(2);
	}else
	{
		wait(&status);
		printf("%x \n",status);
	}

	return 0;
}
  • 该程序说明:程序 fork创建子进程后,进入执行子进程 sleep(1)睡眠一秒后,退出;
  • 子进程退出后,父进程继续执行,wait回收子进程,将子进程的关闭参数返回status中,打印出来。

2、进程返回值和结束方式

  • 子进程通过exit/_exit/return 返回某个值(0-255); 返回的值只有低八位有效。
  • 进程非正常结束 -----  信号接收 进程;
  • 父进程调用wait(&status)回收;
  • WIFEXITED(status) : 判断子进程是否正常结束,正常的话返回非零值
  • WEXITSTATUS(status) : 获取子进程返回值;
  • WIFSIGNALED(status) : 判断子进程是否被信号结束;
  • WTERMSIG(status)   : 判断结束子进程的信号类型;

                  

如上图:status 第0为为0 表示进程正常结束,非0(大于0)表示进程是信号结束的。

第0--6位:是结束进程的类型;

第8-15位:存放子进程的返回值;

3、waitpid ------ 进程回收

   #include  <unistd.h>

     pid_t waitpid(pid_t pid , int *status , int  option);

  • 成功时返回回收的子进程的pid 或0,返回0意味着子进程还没有结束;失败时返回EOF;
  • 第一个参数pid 可用于指定回收哪个子进程或任意子进程;
  • status 指定用于保存子进程返回值和结束方式的地址。
  • option 指定回收方式,0(阻塞)或 WNOHANG(非阻塞)(方式0:子进程还在运行,父进程一直阻塞等待子进程结束后在返回子进程pid;方式WNOHANG:子进程还在运行也返回0,告诉父进程子进程还在运行);

                                             

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


int main(int argc, const char *argv[])
{
	int status;
	pid_t pid;

	if((pid = fork()) < 0)
	{
		perror("fork");
		return -1;
	}else if(pid == 0)
	{
		printf("zhob \n");
		sleep(1);
		exit(2);
	}else
	{
		waitpid(-1,&status,0);
		printf("%x \n",status);
	}

	return 0;
}
  • waitpid(-1,&status,0);  以阻塞的方式 回收任意一个子进程。

十二、 守护进程

三大进程 : 交互进程 、 守护进程、 批处理进程;

  • 守护进程特点(了解)
  • 守护进程(Daemon)是Linux三种进程之一;
  • 通常在系统启动时运行,系统关闭时结束;
  • Linux 系统中大量使用,很多服务程序以守护进程形式运行。

1、守护进程的特点

  •  前台进程能向终端输入也能向终端输出 ;后台进程只能向终端输出;
  •  始终在后台运行;(可以在前台运行其他的进程)守护进程也不能向终端输出
  • 独立于任何终端;
  • 周期性的执行某种任务或等待处理特定事件;

2、会话、控制终端(了解)

—  Linux 以会话(session)、进程组的方式管理进程;

—  每个进程属于一个进程组;如果一个进程创建了一个子进程,则这两个进程属于一个进程组。

  • 会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话。
  • 终端关闭时,所有相关进程会被结束。
  • 为了避免终端关闭时所有相关进程被结束,所有守护进程独立于任何的终端。

3、创建守护进程(熟练)

1、创建守护进程第一步:

  • 创建子进程,父进程退出;

   if(fork() > 0)

  {

           exit(0);此时父进程退出;

   }

  • 子进程变成孤儿进程,被init进程收养;
  • 子进程在后台运行;

2、创建守护进程第二步:

  • 子进程创建新会话:

     if(setsid() < 0)

    {

           exit(-1);

     }

  • 子进程成为新的会话组长;
  • 子进程脱离原先的终端;

3、创建守护进程第三步:

  • 更改当前工作目录:

     chdir(“/”);    可读,可执行,不可写

     chdir(“/tmp”); 所有用户可读可写 可执行

 为啥需要更改当前工作目录:

  • 守护进程一直在后台运行,其工作目录不能被卸载;
  • 重新设定当前工作目录cwd;

4、创建守护进程第四步:

  • 重设文件权限掩码;

      if(umask(0) < 0)  :设置的文件权限掩码  0 ,设置的是什么就是什么,不受掩码影响。

     {

           exit(0);

      }

  • 文件权限掩码设置为0;
  • 只影响当前进程;

5、创建守护进程第五步:

  • 关闭打开的文件描述符

     int I;

     for(i=0 ; i<getdtablesize();i++)

     {

           close(i);

     }

  • 关闭所有从父进程继承的打开文件;
  • 已脱离终端,stdin/stdout/stderr无法再使用;

示例:创建守护进程,每个1秒将系统事件写入文件time.log


		setsid();  //子进程创建新会话
		umask(0);   //重设文件权限掩码
		chdir("/tmp");  // 更改当前工作目录
		for(i = 0;i<getdtablesize();i++)  //关闭打开的文件描述符
		{
			close(i);
		}
		if((fp = fopen("time.log","a")) == NULL)
		{
			perror("fopen");
			exit(-1);
		}

		while(1)
		{
			time(&t);
			fprintf(fp,"%s",ctime(&t));
			printf("%s \n",ctime(&t));
			fflush(fp);
			sleep(1);
		}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值