初识进程这家伙

Linux进程概述:

       什么叫进程呢?进程是一个程序的一次执行过程。它和程序是有本质区别的,程序是静态的,是及其代码指令和数据的集合,这些指令和数据存储在磁盘上的一个可执行映像中,没有任何执行的概念;而进程是动态的,它是执行的过程,包括了动态创建、调度和消亡的整个过程。

       程是程序执行和资源管理的最小单位。因此,对系统而言,当用户在系统中敲入命令执行一个程序的时候,它将启动一个进程。

       Linux是多任务多用户的操作系统,也就是是可以多个程序同时装入内存并运行,操作系统为每个程序建立一个运行环境即创建进程。从逻辑上讲,每个进程拥有自己的虚拟CPU。当然,实际上真正的CPU在各进程之间快速的来回切换。

进程结构:

       在Linux系统中,每一个进程都拥有独立的虚拟地址空间。进程间是分离的任务,拥有各自的权力和责任。Linux中的进程包括了3个段,分别为“数据段”,“代码段”,和“堆栈段”。

       数据段: 存放的数据为全局变量、常数及动态数据分配的数据空间(如 malloc函数获取的虚拟空间)等。

       代码段:顾名思义就是存放程序代码数据。

       堆栈段:存放的是子程序返回地址、子程序的参数及程序的局部变量。

进程属性:

       进程最主要的属性就是进程号(PID,Process ID),和它的父进程号,(PPID,parent process ID)。其中PID唯一的标识一个进程。PID和PPID都是一些非零的正整数,在 Linux中获得当前进程的PID和PPID的系统调用函数为getpid(),和getppid(),通常程序获得当前进程的PID和PPID可以将其写入日志文件以做备份。

       进程是Linux系统的基本调度单位,那么从系统的角度看如何描述并表示它的变化呢?在这里,是通过进程控制块来描述的。进程控制块包含了进程的描述信息、控制信息以及资源信息,它是进程的一个静态描述。在Linux中,进程控制块中的每一项都是一个task_struct结构。

       进程的基本状态有三种:

(1)、运行态:进程占有CPU,并在CPU上运行

(2)、就绪态:进程已经具备运行条件,仅仅缺少占有CPU的机会。(万事具备,只欠东风)

(3)、阻塞态:进程因等待某种事件的发生而暂时不能运行。即使CPU有空,它也不会理你。

       调度只会选择就绪态的其中一个最合适的进程来占有CPU,而不会从阻塞态、运行态中取选择。

它们之间的转换关系如图所示:

进程创建:

1、fork()

        在进程中使用fork函数,则会创建一个新进程,新进程则成为子进程,原进程则称为父进程。恰巧,这个fork函数执行一次却返回二个值。其中父进程中返回值是子进程的进程号(大于0的一个整数),子进程则返回0,因此,可以通过返回值来判断哪个是子进程哪个是父进程。

        使用fork函数得到的子进程是父进程的一个复制品,它从父进程那里继承了整个进程的地址空间。包括进程上下文,进程堆栈、内存信息、打开文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等,而子进程所独有的只有它的进程号、资源使用和计时器等。因此可以知道,使用fork函数的代价是非常大的。它复制了父进程中的代码段、数据段和堆栈段里的大部分内容,使得fork函数的执行速度有限。

fork函数语法:

所需头文件:  #include<sys/types.h>       //提供类型pid_t  的定义

                       #include<unistd.h>

函数原型:pid_t fork(void)

函数返回值:在子进程中返回0;在父进程中返回子进程的进程号PID;如果失败则返回 -1

使用注意点:fork函数使用一次就创建一个进程。

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 execve(const char *path,char *const argv[ ],char *const envp[ ])

                  int execlp(const char *file,const char *arg,...)

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

函数返回值: -1,出错;成功执行时,没有返回值。

l:参数传递为逐个列举方式,如:execl、execle、execlp

v:参数传递为构造指针数组方式,如:execv、execve、execvp

e:可传递新进程环境变量,如:execle、execve

p:可执行文件查找方式为文件名,如:execlp、execvp

下面为fork,getpid,getppid,execlp,execl的简单综合利用程序:

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

int main()
{
    pid_t pid;
    pid=fork();
    if(pid<0)
    {
         perror("fork error");
         exit(1);
    }
    if(pid==0)
    {
        printf("This is a child process,PID is %d.\n",getpid());
        if(execlp("ls","ls","-l",NULL)<0)
        {
              perror("execlp error");
              exit(1);
        }
    }
    printf("---------------------------------------------------\n");
    if(pid>0)
    {
            printf("This is a parent process,PPID is %d.\n",getppid());
            if(execl("ls","ls","-l",NULL)<0)
            {
                     perror("execl error");
                     printf("*****************************************************\n");
            }
           printf("Now it will run execl()\n");
           printf("*****************************************************\n");
           if(execl("/bin/ls","ls","-l",NULL)<0)
           {
                    perror("execl error");
           }
    }
    return 0;
}

执行结果为:

使用exec族函数应该注意的地方,一定要加上错误判断语句。因为exec很容易执行失败,其中最常见的错误有:

(1)、找不到文件或路径,此时errno被设置为ENOENT;

(2)、数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;

(3)、没有对应可执行文件的运行权限,此时errno被设置为EACCES。

system函数语法:

所需头文件:#include<stdlib.h>

函数原型:int  system(const char *string)

函数返回值:执行成功则返回执行shell命令后的返回值,调用/bin/sh失败时则返回127,其他失败原因则返回-1

/*system.c*/

#include<stdio.h>

#include<stdlib.h>

int main( )

{

       int  result;

       result=system("ls -l");

       return 0;    

}

wait和waitpid函数:

      wait函数是用于使父进程阻塞,直到子进程终止或者该进程接到了一个指定的信号为止。如果该父进程没有子进程或者他的子进程已经终止,则wait就会立即返回。

      waitpid函数的作用和wait一样,但是它并不一定要等待第一个终止的子进程,它还有若干项,如可提供一个非阻塞版本的wait功能。

wait()函数语法:

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

                     #include<sys/wait.h>

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

函数传入值:status若为空,则代表任意状态结束的子进程;

                      status若不为空,则代表指定状态结束的子进程;

函数返回值:成功:已结束运行的子进程的进程号;失败:-1

 

waitpid()函数语法:

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

                     #include<sys/wait.h>

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

函数传入值:

pid,

      如果pid>0,只等待进程ID等于PID的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有退出,waitpid就会一直等下去。

      如果pid=1,等待任何一个子进程退出,此时和wait作用一样。

      如果pid=0,等待其组ID等于调用进程的组ID的任一子进程。

      如果pid<1,等待其组ID等于pid的绝对值的任一子进程。

status,同wait的status

options,

WNOHANG:若由pid指定的子进程不立即可用,则waitpid不阻塞,此时返回值为0.

WUNTRACED:若实现某支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停一来还未报告过,则返回其状态。

0:同wait,阻塞父进程,等待子进程退出。

函数返回值:正常:子进程的进程号;

                      options使用WNOHANG且没有子进程退出则返回0;

                      调用出错:-1

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

int main()
{
      pid_t pid1,pid_return;
      pid1=fork();
      if(pid1<0)
      {
             perror("fork error");
      }
     else if(pid1==0)
     {
           printf("sleep 5s in child process.\n");
           sleep(5);
           exit(0);
     }
     else
     {
           do
           {
                  pid_return=waitpid(pid1,NULL,WNOHANG);
                  if(pid_return==0)
                  {
                          printf("The child process has not exited.\n");
                          sleep(1);
                  }
           }while(pid_return==0);
          if(pid_return==pid1)
          {
                      printf("The child process has exited.\n");  
          }
    }
    return 0;
}

运行结果:

这节就讲到这里。^_^,^_^

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值