linux学习笔记-进程控制程序设计

来源:互联网 发布:io后缀域名 编辑:程序博客网 时间:2024/06/06 10:15

获取ID

需包含的头文件

#include <sys/types.h>

#include<unistd.h>

Pid_t getpid(void)

获取本进程ID

Pid_t getppid(void)

获取父进程ID

 

进程创建-fork

#include <unistd.h>

Pid_t fork(void)

功能:创建子进程

Fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值。

1、在父进程中,fork返回新创建子进程的ID

2、在子进程中,fork返回为0

3、如果出现错误,fork返回一个负值。

        fork的返回值这样设计是有原因的,fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程ID,也可以调用getppid函数得到父进程的进程ID。在父进程中使用getpid函数可以得到自己的进程ID,然而要想得到子进程的进程ID,只有将fork的返回值记录下来,别无它法。

       因为系统中表示一个进程的实体是进程控制块,当调用fork时,实际上就是创建一个新控制块,而创建一个新控制块最简单的方法就是“复制”。当然“复制”并不是完全复制,因为父进程控制块中的某些内容还需按照子进程的特点来修改,例如进程的标识、状态等。另外,子进程控制块还必须有自己的私有空间,如数据空间,用户堆栈。

父进程进行fork系统调用时完成的操作

      假设id=fork(),父进程进行fork系统调用时,fork所做工作如下:

①    为新进程分配task_struct任务结构体内存空间。

②    把父进程task_struct任务结构体复制到子进程task_struct任务结构体。

③    为新进程在其内存上建立内核堆栈。

④    对子进程task_struct任务结构体中部分变量进行初始化设置。

⑤    把父进程的有关信息复制给子进程,建立共享关系。

⑥    把子进程加入到可运行队列中。

⑦    结束fork()函数,返回子进程ID值给父进程中栈段变量id

⑧    当子进程开始运行时,操作系统返回0给子进程中栈段变量id

调用fork前的父进程在内存中的映像和调用fork之后父子进程在内存中的映像。


因此下列代码的运行结果是:

count = 1

count = 1   

   

进程创建-vfork

包含的头文件

#include <unistd.h>

#include<sys/types.h>

Pid_t fork(void)

功能:创建子进程

Vforkfork的区别

    1、fork:子进程拷贝父进程的数据段

       vfork :子进程和父进程共享数据段,父子进程资源共享。

    2、fork:父,子进程的执行顺序不确定

       Vfork:子进程先运行,父进程后运行

Exec函数族

Exec函数族的作用是根据指定的文件名找到可执行文件,并将其关联到调用exec族函数的进程,从而使进程执行该可执行文件。简单的说,就是用exec族函数加载的程序文件替换该进程原来的程序文件。

与一般函数不同,exec函数执行成功后一般不会返回调用点,因为它运行了一个新的程序,进程的代码段,数据段和堆栈等都已经被新的数据所取代,只留下进程ID等一些表面信息仍保存原样。

函数execv()

为了运行中能够加载并运行一个可执行文件,linux提供了系统调用execv()。

Int execvconst char * path,char *const argv[])

其中,参数path为可执行文件路径,argv[]为命令行参数。

如果一个进程调用execv(),那么该函数变回把函数参数path所指定的可执行文件加载到进程的用户内存空间,并覆盖掉原文件,然后并运行这个新加载的可执行文件。

在实际应用中,调用execv()通常为子进程。人们之所以创建一个子进程,其目的就是执行一个与父进程代码不同的程序,而系统调用execv()就是子进程执行一个新程序的手段。子进程调用execv()后,系统会立即为子进程加载可执行文件分配私有内存空间,从此子进程也成为一个真正的进程。如果说子进程是父进程的儿子,那么子进程在调用execv()前,它所具有的单独用户堆栈和数据区也仅相当于它的私有“衣橱”和“书柜”;但因它还没有自己的“住房”,因此也只能寄住在父亲家,而不能自立门户,尽管它有自己的“户口”(进程控制块)。只有当子进程调用了execv()并有了自己的可执行文件后,他才算娶了媳妇,从而系统才能为其分配”住房”(程序执行空间),这时它才真正的独立了。

调用execv()后,父进程与子进程存储结构示意:



#include<unistd.h>

Main()

{

  Char * argv[]={“ls”,”-al”,”};

}

执行结果


Execl函数

Int execl(const char *path,const char *arg1,...)

参数

Path:被执行程序名(含完整路径)。

Arg1..argn:被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。

运行如下程序

#include<unistd.h>

Main()

{

Execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);

}

该程序的运行结果与

Ls -al /etc/passwd运行结果一致。


 

Execlp函数

Int execlp(const char *path,const char *arg1,..)

参数:

Path:被执行程序名(不含路径,将从path环境变量中查找该程序)

Arg1-argn:被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。

#include <unistd.h>

Main()

{

  Execlp(“ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);

}

该程序的运行结果与

Ls -al /etc/passwd运行结果一致。

 

   

System函数

Int system(const char* string)

功能:

调用fork产生子进程,由子进程来调用/bin/sh -c string来执行参数string所代表的命令。

程序

#include<stdlib.h>
    void main()

{

   System(“ls -al/etc/passwd”);

}

 

进程等待

所需头文件

#include <sys/types.h>

#include<sys/wait.h>

Pid_t wait(int *status)

功能:

虽然子进程调用函数execv()之后拥有自己的内存空间,成为一个真正的进程,但由于子进程毕竟是由父进程所创建,所以按照计算机技术中谁创建谁销毁的惯例,父进程需要在子进程结束之后释放子进程所占有的系统资源。为实现上述目标,当子进程运行结束时,系统会向该子进程的父进程发出一个信息,请求父进程释放子进程所占用的系统资源。当然,如果这时父进程还没有结束,那么父进程就会义不容辞的完成这项任务。如果父进程没有把握结束于子进程之后,那么为了保证完成子进程释放资源的任务,父进程应该调用系统调用wait(). 

  如果一个进程调用了这个系统调用,那么进程就立即进入中止运行的等待状态(阻塞状态),一直等到系统为本进程发出一个消息。在处理父进程与子进程的关系上,那就是在等待某个子进程已经退出的消息;如果父进程得到这个信息,父进程就会在处理子进程的“后事”之后才会继续运行。

实验程序:

#include <unistd.h>

#include <sys/types.h>

main()

{

  char i;

  pid_t pid;

  if(!(pid = fork()))

  {

   execv("/home/sun/test/hello1.c",NULL);

   //execv运行失败将执行下面语句

   printf("pid %d:i am back ,sth is wrong!\n",getppid());

 

  }

  else

  {

    printf("my pid is %d\n",getpid());

    wait4(pid,NULL,0,NULL);//等待子进程退出系统

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

    printf("done\n");

  }

}

程序运行结果

 

如果父进程先于子进程结束,则子进程就会因失去了父进程而成为孤儿进程。在linux中,如果一个进程变成了孤儿进程,那么这个进程将以系统在初始化创建的init进程为父进程。





0 0