LINUX环境编程-进程编程.ppt

上传人:tia****nde 文档编号:12707123 上传时间:2020-05-14 格式:PPT 页数:35 大小:708KB
返回 下载 相关 举报
LINUX环境编程-进程编程.ppt_第1页
第1页 / 共35页
LINUX环境编程-进程编程.ppt_第2页
第2页 / 共35页
LINUX环境编程-进程编程.ppt_第3页
第3页 / 共35页
点击查看更多>>
资源描述
LINUX应用编程进程篇,2009年12月22日,2,LINUX应用编程-进程篇,HTTP协议HTML语言CGI编程,3,进程编程进程与程序,程序是一个包含可执行代码的文件,它放在磁盘等介质上。当程序被操作系统装载到内存并分配给它一定资源后,此时可称为进程。为方便操作系统管理,每个进程都会有一个唯一的非负整数编号。程序是一个静态概念,进程是一个动态概念。,4,进程编程内存空间,Linux的虚拟地址空间也为04G,Linux将整个4G线性地址空间分为用户空间和内核空间两部分,最高的1G字节(从虚拟地址0 xC0000000到0 xFFFFFFFF),供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址0 x00000000到0 xBFFFFFFF),供各个进程使用,称为“用户空间)。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。当进程陷入内核时,内核代表进程运行。,5,进程编程进程描述,进程描述符:当进程产生时有Linux操作系统分配。内存:用来存放进程要执行的代码和使用的数据。文件描述符:进程运行时打开的文件。认证信息:用户和组ID进程执行环境:各种环境变量资源安排:CPU时间进程状态,6,进程编程进程状态,用户状态:进程在用户状态下运行的状态。内核状态:进程在内核状态下运行的状态。内存中就绪:进程没有执行,但处于就绪状态,只要内核调度它,就可以执行。内存中睡眠:进程正在睡眠并且进程存储在内存中,没有被交换到SWAP设备。就绪且换出:进程处于就绪状态,但是必须把它换入内存,内核才能再次调度它进行运行。睡眠且换出:进程正在睡眠,且被换出内存。被抢先:进程从内核状态返回用户状态时,内核抢先于它,做了上下文切换,调度了另一个进程。原先这个进程就处于被抢先状态。创建状态:进程刚被创建。该进程存在,但既不是就绪状态,也不是睡眠状态。这个状态是除了进程0以外的所有进程的最初状态。僵死状态(zombie):进程调用exit结束,进程不再存在,但在进程表项中仍有纪录,该纪录可由父进程收集。,7,进程编程进程状态及转换,图如下:,8,进程调度原理图,9,进程编程进程的布局,栈用来存放局部变量和函数的返回地址。地址从高到低生长。堆是一块连续的内存,有低地址向高地址生长。需要程序在运行时动态申请和释放。数据段存放了程序运行时的各种数据。代码段存放了可执行指令,一般为只读。,10,进程编程进程的环境变量,环境变量和命令行参数都放在进程的高地址。环境变量可用environ来引用。以name=string的形式存放。,#include#include#includeexternchar*environ;intmain(intargc,char*argv)inti=0;for(i=0;environi;i+)printf(%sn,environi);return0;,11,进程编程EXIT系统调用,系统调用exit的功能是终止发出调用的进程。它的声明格式如下:#includevoidexit(intstatus);#includevoid_exit(intstatus);系统调用_exit立即终止发出调用的进程。所有属于该进程的文件描述符都关闭。该进程的所有子进程由进程1(进程init)接收,并对该进程的父进程发出一个SIGCHLD(子进程僵死)的信号。参数status作为退出的状态值返回父进程,该值可以通过系统调用wait来收集。返回状态码status只有最低一个字节有效。如果进程是一个控制终端进程,则SIGHUP信号将被送往该控制终端的前台进程。系统调用_exit从不返回任何值给发出调用的进程;也不刷新I/O缓冲,如果要自动完成刷新,可以用函数调用exit。exit为glibc库函数,它会先运行注册函数,也有可能会进行文件流的关闭操作,之后再调用_exit系统调用。,12,进程编程RETURN退出进程,return的返回值就是进程终止时的返回状态。如果return没有返回任何值,进程的返回状态是什么?在gcc加std=c99编译选项下,进程返回状态又是什么?C99:是ISO组织在99年新制定的c标准,使C更可靠。分别用gccmain.c和gccmain.cstd=c99编译,观察返回值。,13,进程编程进程终止处理函数,Atexit()函数用于注册一个进程正常终止时要调用的函数,一个进程最多可注册32个终止处理函数。#includeintatexit(void(*function)(void);成功返回0,失败返回非0。参数为函数指针,此函数会在进程调用exit时调用。在main函数返回出,分别用return、exit(0)_exit(0),看其中的区别。_exit函数是不会调用注册函数的。voidexit_handler(void)printf(Iminexithandlern);intmain(intargc,char*argv)if(0!=atexit(exit_handler)printf(registerexitfunctionhandlerfailed!n);return-1;printf(beforeexitn);exit(0);/_exit(0);return0;,14,进程编程getenv、setenv函数,getenv用来获取指定环境变量。#includechar*getenv(constchar*name);参数name为环境变量的名称,如PATH,SHELL等。如果环境变量存在那么返回该环境变量的值,否则为NULLsetenv用来设置环境变量,会把value拷贝到环境变量所在的内存区域。#includeintsetenv(constchar*name,constchar*value,intoverwrite);参数name为环境变量的名称,value为该环境变量的值。如果环境变量已经存在,overwrite非0时,改变该环境变量的值。overwrite为0时,什么都不做直接返回。成功返回0,失败返回-1(一般为放置环境变量的内存空间不够)。环境变量都是以name=string的形式存放。,15,进程编程putenv、clearenv函数,putenv无论环境变量是否存在,都会使设置值生效。#includeintputenv(char*string);参数string的格式为name=string成功返回0,失败返回非0putenv()函数并不拷贝环境变量字符串到进程环境表,只是存放环境变量数值的指针,而setenv()函数则完全拷贝环境变量字符串到进程环境表。分别用gccputenv.c和gccputenv.cDLOCALg编译,产看运行结果。把putenv.c中的putenv换成setenv看结果。clearenv清除所有的环境变量,并且把environ设置为NULL。unsetenv清除名字为name的环境变量。#includeintclearenv(void);intunsetenv(constchar*name);,16,进程编程跨函数跳转,goto只能在函数中跳转,如果要求从一个函数跳到另外一个函数?用setjmp和longjmp。跨函数的跳转主要用在调用函数层次较深时,为了节约函数的返回时间。如右假设我们从a函数调用到了z函数。,a函数,b函数,r函数,z函数,17,进程编程setjmp、longjmp函数,setjmp用来设置返回点,保存当前的寄存器值。#includeintsetjmp(jmp_bufenv);voidlongjmp(jmp_bufenv,intval);参数jmp_bufenv用来保存当前寄存器值。longjmp会根据env跳转到setjmp处。一个setjmp可以对应n个longjmp,可以用setjmp的返回值来区分。如果成功setjmp返回0,如果是从longjmp返回的,那么返回值有longjmp的第二个参数决定。所以longjmp的第二个参数不可以为0,否则无法判断setjmp是如何返回的。longjmp的第一个参数是setjmp返回的,第二个参数是给setjmp的返回值。当longjmp返回后,setjmp所在函数中的自动变量恢复到调用setjmp时的值。如果变量是保存在内存中的,那么它的值仍然是调用longjmp的时候的值。当你希望变量值在setjmp和longjmp时仍然保持其值,但必须用volatile说明该变量,并且需打开-O优化选项。gccsetjmp.c和gccsetjmp.cO观察运行结果。,18,进程编程getpid、getppid函数,getpid得到自己的进程ID,getppid得到自己父进程ID#include#includepid_tgetpid(void);pid_tgetppid(void);返回值为进程ID,进程ID一般为一非负整数值。,19,进程编程fork:进程的创建,我们可以在shell里敲入命令的方式来创建进程,也可以在在程序中通过调用fork系统调用来生成新的进程。新产生的进程我们称他为子进程。init进程的进程ID为1,是一个特殊的用户进程。它会收集孤儿进程(父进程已退出的子进程),并结束它们。fork会产生一个新的进程。#includepid_tfork(void);返回值为-1时,创建子进程失败。返回0时,子进程开始执行。返回0时,父进程开始执行。父子进程都是从fork之后开始执行。到底是父进程还是子进程先开始执行,看操作系统的调度算法。当你的程序执行到fork语句时:操作系统会复制一个与父进程完全相同的子进程。新进程和原有进程共享代码空间,可执行程序是同一个程序。Linux操作系统会为新进程产生一个ID和进程控制块。当子进程或者父进程不进行写操作时,父子进程共享一分数据,当有写操作时,数据会分离(称为写时复制Copy-On-Write),互不干涉。子进程/父进程对数据所做的任何修改,都不会影响另一方。,20,进程编程写时复制,1.父子进程均无写操作时:,2.父进程或者子进程有些操作时,子进程或父进程将新产生一份进程的数据拷贝,然后再修改。,21,进程编程子进程的继承,知道子进程自父进程继承什么或未继承什么将有助于我们。下面这个名单会因为不同Unix的实现而发生变化,所以或许准确性有了水份。请注意子进程得到的是这些东西的拷贝,不是它们本身。由子进程自父进程继承到:进程的资格(真实(real)/有效(effective)/已保存(saved)用户号(UIDs)和组号(GIDs)环境(environment)堆栈内存打开文件的描述符(注意对应的文件的位置由父子进程共享,这会引起含糊情况)执行时关闭(close-on-exec)标志(close-on-exec标志可通过fcntl()对文件描述符设置,POSIX.1要求所有目录流都必须在exec函数调用时关闭。更详细说明,参见UNIX环境高级编程W.R.Stevens,1993,尤晋元等译(以下简称高级编程),3.13节和8.9节)信号(signal)控制设定nice值(nice值由nice函数设定,该值表示进程的优先级,数值越小,优先级越高),22,进程编程子进程的继承,进程调度类别(schedulerclass)(进程调度类别指进程在系统中被调度时所属的类别,不同类别有不同优先级,根据进程调度类别和nice值,进程调度程序可计算出每个进程的全局优先级(Globalprocessprority),优先级高的进程优先执行)进程组号对话期ID(SessionID)(指:进程所属的对话期(session)ID,一个对话期包括一个或多个进程组,更详细说明参见高级编程9.5节)当前工作目录根目录(译者注:根目录不一定是“/”,它可由chroot函数改变)文件方式创建屏蔽字(filemodecreationmask(umask)(指:创建新文件的缺省屏蔽字)资源限制控制终端,23,进程编程-子进程独有的,子进程所独有:进程号:不同的父进程号(译者注:即子进程的父进程号与父进程的父进程号不同,父进程号可由getppid函数得到)自己的文件描述符和目录流的拷贝(译者注:目录流由opendir函数创建,因其为顺序读取,顾称“目录流”)子进程不继承父进程的进程,正文(text),数据和其它锁定内存(memorylocks)(锁定内存指被锁定的虚拟内存页,锁定后,不允许内核将其在必要时换出(pageout),详细说明参见TheGNUCLibraryReferenceManual2.2版,1999,3.4.2节)在tms结构中的系统时间(tms结构可由times函数获得,它保存四个数据用于记录进程使用中央处理器(CPU:CentralProcessingUnit)的时间,包括:用户时间,系统时间,用户各子进程合计时间,系统各子进程合计时间)资源使用(resourceutilizations)设定为0阻塞信号集初始化为空集不继承由timer_create函数创建的计时器不继承异步输入和输出,24,进程编程继承的测试题,glibc库的I/O有三个类型的缓冲无缓冲:不管你写入的数据有多少,立即写往内核。行缓冲:当遇到换行符时,才写往内核。全缓冲:需要写到一定字节的数据,才写往内核。在fork_bufio.c中,在fork之前的printf的格式化字符串中加上换行符或者去掉换行符,看输出有什么不同。,25,进程编程vfork,vfork和fork一样都是创建一个子进程,但子进程不会从父进程复制任何东西。子进程完全和父进程共享数据和堆栈,子进程对数据的修改不会触发写时复制,也就是说子进程所作的修改会出现在父进程中。#include#includepid_tvfork(void);返回值为-1时,创建子进程失败。返回0时,子进程开始执行。返回0时,父进程开始执行。Vfork与fork的区别vfork产生的子进程必须以exit或者exec返回,否则会出现未定义错误。vfork一定保证子进程先运行,在子进程调用exit或者exec之前父进程是不可能运行的。vfork的使用场合是子进程不会用到父进程任何资源的情况下。vfork只产生一个进程控制块,然后再通过exec产生子进程所需要的资源和代码。在vfork.c中,当vfork的子进程用return返回时,看有什么结果?,26,进程编程wait和waitpid,父子进程谁先终止时未定义的,如果父进程先于子进程终止。那么子进程会被init进程“领养”,子进程的父进程id变为1。如果父进程不去查询子进程的状态,那么子进程一直会处于“僵尸”状态。这也是init进程存在的原因之一。父进程可以通过wait和waitpid函数来等待子进程终止并查询子进程的状态。wait和waitpid可查询子进程的结束状态,如果子进程还处于运行状态,那么父进程则会阻塞。#include#includepid_twait(int*status);pid_twaitpid(pid_tpid,int*status,intoptions);wait函数只要任意一个子进程结束,就会返回该子进程ID,如果出错则返回-1。status为子进程返回的状态,需要通过宏WIFEXITED和WEXITSTATUS最终获得返回状态。waitpid可以指定某个进程id或者进程组id,参数pid说明如下:pid=-1等待任意一个进程结束,此时与wait等效。pid0等待与pid相符的子进程结束。pid=0等待组ID等于调用者进程组ID的任意进程结束。pid-1等待组ID等于|pid|的任意进程结束。,27,进程编程wait和waitpid,对于调用waitpid中的参数options的取值及其含义如下:WNOHANG:该选项要求如果没有子进程退出就立即返回。WUNTRACED:对已经停止但未报告状态的子进程,该调用也从等待中返回和报告状态。如果status不是空,调用将使status指向该信息。下面的宏可以用来检查子进程的返回状态。前面三个用来判断退出的原因,后面三个是对应不同的原因返回状态值:WIFEXITED(status):如果进程通过系统调用_exit或函数调用exit正常退出,该宏的值为真。WIFSIGNALED(status):如果子进程由于得到的信号(signal)没有被捕捉而导致退出时,该宏的值为真。WIFSTOPPED(status):如果子进程没有终止,但停止了并可以重新执行时,该宏返回真。这种情况仅出现在waitpid调用中使用了WUNTRACED选项。WEXITSTATUS(status):如果WIFEXITED(status)返回真,该宏返回由子进程调用_exit(status)或exit(status)时设置的调用参数status值。WTERMSIG(status):如果WIFSIGNALED(status)返回为真,该宏返回导致子进程退出的信号(signal)的值。WSTOPSIG(status):如果WIFSTOPPED(status)返回真,该宏返回导致子进程停止的信号(signal)值。,28,进程编程wait和waitpid,该调用返回退出的子进程的PID;或者发生错误时返回-1;或者设置了WNOHANG选项没有子进程退出就返回0;发生错误时,可能设置的错误代码如下:ECHILD:该调用指定的子进程pid不存在,或者不是发出调用进程的子进程。EINVAL:参数options无效。ERESTARTSYS:WNOHANG没有设置并且捕获到SIGCHLD或其它未屏蔽信号。关于wait调用的例子,前面在介绍fork调用时,就有了简单的应用。此处不再举例。注意:子进程退出(SIGCHLD)信号设置不同的处理方法,会导致该调用不同的行为,详细情况见Linux信号处理机制。,29,进程编程exec函数,系统调用exec是用来执行一个可执行文件来代替当前进程的执行映像。需要注意的是,该调用并没有生成新的进程,而是在原有进程的基础上,替换原有进程的正文(即exec用一个全新的程序替换当前进程的正文,数据,堆和栈),调用前后是同一个进程,进程号PID不变。但执行的程序变了(执行的指令序列改变了)。它有六种调用的形式,随着系统的不同并不完全与以下介绍的相同。它们的声明格式如下:#includeintexecl(constchar*path,constchar*arg,.);intexeclp(constchar*file,constchar*arg,.);intexecle(constchar*path,constchar*arg,.,char*constenvp);intexecv(constchar*path,char*constargv);intexecve(constchar*filename,char*constargv,char*constenvp);intexecvp(constchar*file,char*constargv);,30,进程编程EXEC的函数关系,31,进程编程execl和execv,execl和execv,这两个函数的区别在于程序的命令行参数如何传递。l代表list,意味着execl的每个命令行参数都是单独传入。v代表vector,所有命令行参数打包成char*argvp的方式传给execv。其实execl所做的是把单独的命令行参数打包后传给execv。execlp和execvp,p代表path,也就是说你只要在第一个参数中指明可执行文件的名字,系统便会从PATH指定的路径中寻找那个可执行文件并执行。不设置environ,观察程序运行区别。execle和execve,函数的最后一个参数为环境变量。例:execv.cexecvp.cexecve.c,32,进程编程system函数,system执行外部一个命令或者程序,相当于fork,exec,waitpid。#includeintsystem(constchar*command);返回-1表示失败,成功返回命令的返回状态。如果command中没有路径符/,则从PATH环境变量指定的目录中寻找该命令。,33,作业,1.下面的程序一共会创建多少个进程?intmain(intargc,char*argv)inti;for(i=0;i3;i+)if(fork()=0)printf(“thechild%dn”,i);elseprintf(“theparent%dn”,i);2.用fork、exec、waitpid实现system函数功能。3.编写一段程序,创建一个“僵尸”进程,然后调用system执行ps命令验证该进程是“僵尸”进程。验证完后父进程用wait或者waitpid对该“僵尸”进程“收尸”。,34,作业,4.pp.c文件内容如下pp.cintmain()printf(test);则下面的程序执行的输出结果是什么?externchar*environ;intmain(intargc,char*argv)printf(close-on-execis%d,fcntl(1,F_GETFD);fcntl(1,F_SETFD,16);printf(close-on-execis%d,fcntl(1,F_GETFD);execve(pp,argv,environ);printf(AH!);,35,作业,5.写一个程序,创建10个进程,每个进程分别打印出自己的进程号及父进程号6.写三个程序,分别执行如下功能:程序一:打印“Iamprocess1”,然后睡眠3秒,退出程序二:打印“Iamprocess2”,然后睡眠3秒,退出程序三:程序执行起来后创建两个子进程,此两个子进程分别使用exec运行程序一和程序二,当主进程检测到任何一个子进程退出时,打印出退出的子进程,并重新启动相应的子进程。,
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!