Linux开发入门笔记——进程管理

版权声明:本文为Trinity原创文章,未经Trinity允许不得转载 https://blog.csdn.net/Caoyang_He/article/details/84946848

进程环境与进程属性

什么是进程

  • 简单说来,进程就是程序的一次执行过程。
  • 进程至少要有三种基本状态。这三种基本状态是:
  • 运行态
  • 就绪态
  • 封锁态(或等待态)
  • 进程的状态可依据一定的条件和原因而变化

进程的状态

在这里插入图片描述

进程的模式和类型

  • 什么是孤儿进程
    因父亲进程先退出而导致一个子进程被init进程收养的进程为孤儿进程。
    举例:orphan.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
	pid_t pid;
	if((pid=fork())==-1)
		perror("fork");
	else if(pid==0)
	{
		printf("pid=%d,ppid=%d\n",getpid(),getppid());
		sleep(2);
		printf("pid=%d,ppid=%d\n",getpid(),getppid());
	}
	else
		exit(0);
}

在这里插入图片描述

  • 什么是僵死进程
    已经退出但还没有回收资源的进程为僵死进程。
    举例:dead.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
	pid_t pid;
	if((pid=fork())==-1)
		perror("fork");
	else if(pid==0)
	{
		printf("child_pid pid=%d\n",getpid());
		exit(0);
	}
	sleep(3);
	system("ps");
	exit(0);
}

在这里插入图片描述

  • 进程的模式
    在Linux系统中,进程的执行模式划分为用户模式和内核模式

  • 进程的类型
    按照进程的功能和运行的程序来分,进程划分为两大类:一类是系统进程,另一类是用户进程 。

进程的属性

在这里插入图片描述

  • 进程号(PID)
    函数:extern pid_t getpid(void);
    举例:getpid_example.c
#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
	printf("the current program's pid is %d\n",getpid());
	return 0;
}

在这里插入图片描述

  • 父进程号(PPID)
    函数:extern pid_t getppid(void);
    举例:getppid_example.c
#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
	printf("the current program's ppid is %d\n",getppid());
	return 0;
}

在这里插入图片描述

  • 会话:一个或多个进程组的集合
    函数:

    • extern pid_t getsid(pid_t pid)
    • extern pid_t setsid(void)
  • 控制终端
    函数:

    • pid_t tcgetpgrp(int filedes);
    • pid_t tcsetpgrp(int filedes,pid_t pgrpid);
    • pid_t tcgetsid(int filedes);
  • 会话与进程组的特点

    • 一个会话可以由一个控制终端,建立于控制终端连接的会话首进程被称为控制进程;
    • 一个会话中的几个进程组可分为一个前台进程组合几个后台进程组,如果会话有一个控制终端,则有一个前台进程组;
    • 无论何时在控制终端键入或监测,将会有信号发送给前台进程组的所有进程。
  • 进程用户属性

    • 真实用户号(RUID)
      创建该进程的用户UID
      函数:extern uid_t getuid(void)
    • 有效用户号(EUID)
      有效UID一般与UID相同,但是在设置setuid位后,则不同。
      函数:extern uid_t geteuid(void)
    • 进程用户组号(GID)
      创建该进程的用户所在的组号GID。
      函数:extern uid_t getgid(void)
    • 有效进程用户组号(EGID)
      有效GID一般与GID相同,但是在设置setgid位后,则不同。
      函数:extern uid_t getegid(void)

进程的调度

调度算法

  • FIFO先入先出原则:首先请求服务的对象首先得到CPU的处理
  • 最短作业优先原则:需要最少时间的服务首先得到处理
  • 最高优先级优先原则:优先级最高的服务首先得到处理
  • 时间轮片原则:每个任务分配一个系统时间片,轮流执行

调度时机

  • 定义
    在什么情况下要执行调度程序,则称为调度时机。
  • 调度时机
    • 当前进程调用系统调用sleep( )或者exit( )。
    • 进程终止,永久地放弃对CPU的使用。
    • 在时钟中断处理程序执行过程中,发现当前进程连续运行的时间过长。
    • 当唤醒一个睡眠进程时,发现被唤醒的进程比当前进程更有资格运行。
    • 一个进程通过执行系统调用来改变调度策略或者降低自身的优先权(如nice命令),从而引起立即调度。

进程的管理

创建进程——fork和vfork

  • 功能:创建进程。
  • 函数原型 pid_t fork(void);
  • 返回值:
    如果调用成功,父进程调用返回新子进程ID,新进程继续执行;在子进程返回0。
    如果调用失败,在父进程返回-1,错误原因存于errno中。
  • 说明:这个系统调用对父进程进行复制,在进程表里创建出一个项目,子进程与父进程几乎一样,执行的是相同的代码,只是新进程有自己的数据空间、环境和文件描述符,而对文件描述符关联的内核文件表项,采用共享方式。
  • 典型用法
	pid_t  new_pid;
	new_pid = fork();
	switch(new_pid)
	{
		case -1:break;
		case 0:break;
		default:break;
	}
  • 举例:资源抢占
    fork_example01.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
	pid_t pid;
	if((pid=fork())==-1)
		printf("fork error");
	printf("bye!\n");
	return 0;
}

在这里插入图片描述

  • 举例:父子进程典型结构
    fork_example02.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
	pid_t pid;
	if((pid=fork())==-1)
		printf("fork error");
	else if(pid==0)
	{
		printf("in the child process\n");
	}
	else
	{
		printf("in the parent process\n");
	}
	return 0;
}

在这里插入图片描述

  • 举例:父子进程的复制
    fork_descriptor.c
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    pid_t pid;
    int fd;
    int i=1;
    int status;
    char *ch1="hello";
    char *ch2="world";
    char *ch3="IN";
    if((fd=open("test.txt",O_RDWR|O_CREAT,0644))==-1)
    {
	perror("parent open");
	exit(EXIT_FAILURE);
    }
    if(write(fd,ch1,strlen(ch1))==-1)
    {	 
	perror("parent write");
	exit(EXIT_FAILURE);
    }
    
    if((pid=fork())==-1)
    {
	perror("fork");
	exit(EXIT_FAILURE);
    }
    else if(pid==0)
    {
	i=2;	
	printf("in child\n");
	printf("i=%d\n",i);
	if(write(fd,ch2,strlen(ch2))==-1)
	    perror("child write");
	return 0;
    }
    else
    {
	sleep(1);
	printf("in parent\n");
	printf("i=%d\n",i);
	if(write(fd,ch3,strlen(ch3))==-1)
	    perror("parent,write");
	wait(&status);
	return 0;
    }
}

在这里插入图片描述
vfork

  • 出现原因
    当派生进程只是执行exec()函数,则使用fork()从父进程复制到子进程的数据空间将不被使用,为了改进这种情况下效率低下的问题,出现了vfork()。
  • vfork函数特点
    与父进程共享数据空间
  • 执行顺序:子先父后
  • 与fork区分
  • 举例:vfork函数的进程创建

fork_example.c

#include<unistd.h>
#include<error.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
int glob=6;
int main()
{
        int var;
        pid_t pid;
        var=88;
        printf("in beginning:\tglob=%d\tvar=%d\n",glob,var);
        if((pid=fork())<0)
        {
                perror("fork");
                exit(EXIT_FAILURE);
        }
        else if(pid==0)
        {
                printf("in child,modify the var:glob++,var++\n");
                glob++;
                var++;
                printf("in child:\tglob=%d\tvar=%d\n",glob,var);
                _exit(0);
        }
        else
        {
                printf("in parent:\tglob=%d\tvar=%d\n",glob,var);
                return 0;
        }
}

在这里插入图片描述

vfork_example.c

#include<unistd.h>
#include<error.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
int glob=6;
int main()
{
	int var;
	pid_t pid;
	var=88;
	printf("in beginning:\tglob=%d\tvar=%d\n",glob,var);
	if((pid=vfork())<0)
	{
		perror("vfork");
		exit(EXIT_FAILURE);
	}
	else if(pid==0)
	{
		printf("in child,modify the var:glob++,var++\n");
		glob++;
		var++;
		printf("in child:\tglob=%d\tvar=%d\n",glob,var);
		_exit(0);
	}
	else
	{	
		printf("in parent:\tglob=%d\tvar=%d\n",glob,var);
		return 0;
	}
}

在这里插入图片描述

  • 举例:vfork在子函数中
    vfork_return.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
void test()
{
    pid_t pid;
    pid=vfork();
    if(pid==-1)
    {
       perror("vfork");
       exit(EXIT_FAILURE);
    }
    else if(pid==0)
    {   
       printf("1:child pid=%d,ppid=%d\n",getpid(),getppid());
       return;
    }
    else
       printf("2:parent pid=%d,ppid=%d\n",getpid(),getppid());
}
void fun()
{  
   int i;
   int buf[100];
   for(i=0;i<100;i++)
       buf[i]=0;
   printf("3:child pid=%d,ppid=%d\n",getpid(),getppid());	
}
int main()
{
   pid_t pid;
   test();
   fun(); 
}

在这里插入图片描述

等待进程—wait

  • 功能:等待子进程中断或结束
  • 头文件
    #include<sys/types.h>
    #include<sys/wait.h>
  • 函数原型 pid_t wait (int * status);
  • 说明:函数会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会同时返回。如果不在意结束状态值,则参数 status可以设成NULL。子进程的结束状态值请参考宏测试。
  • 返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno中。
  • 函数处理的基本过程
    • 如果父进程没有子进程,则出错返回。
    • 如果发现有一个终止的子进程,则取出子进程的进程号,把子进程的CPU使用时间等加到父进程上,释放子进程占用的task_struct和系统空间堆栈,以供新进程使用。
    • 如果发现有子进程,但都不处于终止态,则父进程睡眠,等待由相应的信号唤醒
  • 举例
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
main()
{	
pid_t pid;	
int status,i;
	if(fork()==0)
	{
		printf("This is the child process .pid =%d\n",getpid());	
		exit(5);
	}
	else{
			sleep(1);
			printf("This is the parent process ,wait for child...\n");
			pid=wait(&status);		
			i=WEXITSTATUS(status);		
			printf("child’s pid =%d .exit status=%d\n",pid,i);
			}
}

在这里插入图片描述

退出进程——on_exit和exit

on_exit

  • 功能:正常结束当前调用函数
  • 举例
    在这里插入图片描述

exit

  • 功能:正常结束进程
  • 头文件:#include<stdlib.h>
  • 函数原型:void _exit(int status)
  • 返回值:无
  • 说明:正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据不会自动写回。

举例:比较exit和_exit的区别

#include <stdlib.h>
int main(int argc,char **argv)
{
printf(“output\n”);
printf(“content in buffer”);
_exit(0);
//exit(0);
}

替换当前进程——execl,execlp,execv ,execvp,execve

execl

  • 头文件:#include<unistd.h>
  • 函数原型
    int execl(const char * path,const char * arg,…);
  • 返回值:成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
  • 说明
    execl()用来执行参数path字符串所代表的文件路径,参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
  • 举例
#include<unistd.h>
main()
{
	execl(/bin/ls”,”ls”,-l”,/etc/passwd”(char * )0);
            }

execlp

  • 头文件:#include<unistd.h>
  • 函数原型
    int execlp(const char * file,const char * arg,……);
  • 返回值:成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
  • 说明
    execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
  • 举例
#include<unistd.h>
main()
{
	execlp(“ls”,”ls”,-l”,/etc/passwd”,
    (char * )0);
 }

execv

  • 头文件:#include<unistd.h>
  • 函数原型
    int execv (const char * path, char * const argv[ ]);
  • 返回值:成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
  • 说明
    execv()用来执行参数path字符串所代表的文件路径,与execl()不同的地方在于execv()只需两个参数,第二个参数利用数组指针来传递给执行文件。
  • 举例
#include<unistd.h>
	main(){	
	char * argv[ ]={“ls”,”l”,/etc/passwd”,(char*)0 };
	execv(/bin/ls”,argv);
		}

execvp

  • 头文件:#include<unistd.h>
  • 函数原型
    int execvp(const char *file ,char * const argv []);
  • 返回值:成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
  • 说明
    execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
  • 举例
#include<unistd.h>
main(){
	char * argv[ ] ={ “ls”,”l”,/etc/passwd”,0};
	execvp(“ls”,argv);
	}

execve

  • 头文件:#include<unistd.h>
  • 函数原型
    int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
  • 返回值:成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
  • 说明
    execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
  • 举例
#include<unistd.h>
main()
{
	char * argv[ ]={“ls”,”l”,/etc/passwd”,(char *)0};
	char * envp[ ]={“PATH=/bin”,0}
	execve(/bin/ls”,argv,envp);
	}

补充

fatherFork.c

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int i;
  for( i= 0; i< 3; i++)
  {
      int pid= fork();
     if(pid== 0)
     {
         printf("son\n");
     }
     else
     {
         printf("father\n");
     }
   }
return 0;
}

在这里插入图片描述

waitpid_example.c

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

extern int errno;
int main(int argc,char *argv[])
{
        pid_t pid_one,pid_wait;
        int status;
        if((pid_one=fork())==-1)
                perror("fork");
        if(pid_one==0)
        {
                printf("my pid is %d\n",getpid());
                sleep(1);
                exit(EXIT_SUCCESS);
        }

        pid_wait=waitpid(pid_one,&status,0);
        if(WIFEXITED(status))
                        printf("wait on pid:%d,return value is:%4x\n",pid_wait,WEXITSTATUS(status));
                else if(WIFSIGNALED(status))
                        printf("wait on pid:%d,return value is:%4x\n",pid_wait,WIFSIGNALED(status));

        return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Caoyang_He/article/details/84946848
今日推荐