Linux第5章进程、信号和消息队列_第1页
Linux第5章进程、信号和消息队列_第2页
Linux第5章进程、信号和消息队列_第3页
Linux第5章进程、信号和消息队列_第4页
Linux第5章进程、信号和消息队列_第5页
已阅读5页,还剩34页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第五章进程、信号和消息队列12§5.1什么是进程“一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源。”“正在运行的程序。”进程由程序代码、数据、变量(占用着系统内存)、打开的文件(文件描述符)和环境组成。3§5.2进程的结构多进程的管理方式示例:两个用户同时运行grep程序搜索结束之前运行ps命令PID:2~32768代码区:共享函数库:共享数据区、栈空间、环境变量等:不共享4§5.2进程的结构多进程的管理方式示例:两个用户同时运行grep程序搜索结束之前运行ps命令PID:2~32768代码区:共享函数库:共享数据区、栈空间、环境变量等:不共享5§5.2进程的结构进程表Linux进程表是一个数据结构,其中保存了当前所有进程的相关信息,包括PID、状态、命令字符串等(参见ps命令输出项)。操作系统通过PID对进程进行管理,PID是进程表的索引。系统可同时运行的进程数只与可用于建立进程表项的内存容量有关。父进程与子进程Linux启动时运行一个名为init的进程,该进程是系统运行的第一个进程,它的PID为1。一般进程(除init外)都是由一个“父进程”创建的,被父进程创建的进程称为“子进程”,init进程是其他所有进程的祖先进程。UIDPIDPPIDCSTIMETTYTIMECMDroot10020:41?00:00:00/sbin/initmolin19331932020:45pts/100:00:00-bashmolin20471020:47?00:00:01gnome-terminal6§5.2进程的结构进程状态$psaxPIDTTYSTATTIMECOMMAND1?Ss0:00/sbin/init37?SN0:00[ksmd]892?Ssl0:00gdm-binary2049pts/0Ss+0:00bash8204?S<0:00udevd--daemon12682pts/1R+0:00psaxSTAT代码说明S睡眠。通常是在等待某个事件的发生。R运行/可运行。即在运行队列中,处于正在运行或即将运行状态。D不可中断的睡眠(等待)。通常是在等待输入或输出完成。T停止。通常是被shell作业控制所停止,或处于调试器控制下。Z僵尸(zombie)进程。N低优先级任务。s进程是会话期首进程。+进程属于前台进程组。l进程是多线程的。<高优先级任务。7system函数可以调用标准库函数system在程序内启动另一个程序,从而创建一个新进程。#include<stdlib.h>intsystem(constchar*command);说明command 待运行的程序(命令字符串)。返回值 无法启动shell时,返回127;其它错误时返回-1;否 则,返回命令的退出码。system函数运行command命令并等待该命令完成,本质是调用:/bin/sh–ccommand。注:使用system函数并非启动其他进程的理想手段,因其必须先启动一个shell,再使用该shell执行相应的命令。§5.3创建进程8§5.3创建进程system函数示例#include<stdlib.h>#include<stdio.h>intmain(){printf("Runningpswithsystem\n");system("ps-ef");printf("Done.\n");exit(0);}$./exam09_01RunningpswithsystemUIDPIDPPIDCSTIMETTYTIMECMDroot10020:41?00:00:00/sbin/init....molin1214311937022:20pts/100:00:00./exam09_01molin12144

12143022:20pts/100:00:00sh-cps-efmolin1214512144022:20pts/100:00:00ps-efDone.9替换进程映像可以调用exec系列函数把当前进程替换为一个新进程。#include<unistd.h>externchar**environ;intexecl(constchar*path,constchar*arg,...);intexeclp(constchar*file,constchar*arg,...);intexecle(constchar*path,constchar*arg,...,char*constenvp[]);intexecv(constchar*path,char*constargv[]);intexecvp(constchar*file,char*constargv[]);intexecve(constchar*filename,char*constargv[],char*constenvp[]);说明path 待运行的程序全路径名(命令字符串)。file 待运行的程序名,通过PATH环境变量搜索其路径。arg 命令参数。argv[] 命令参数。envp 传递给待运行程序的环境变量。返回值 成功时不返回(原程序不再执行);出错时返回-1,并设置 errno变量。§5.3创建进程参数个数可变,以一空指针结束。其他函数通常是用execve函数实现的。10§5.3创建进程替换进程映像用法#include<unistd.h>/*Exampleofanargumentlist*//*Notethatweneedaprogramnameforargv[0]*/char*constps_argv[]= {“ps”,“ax”,0};/*Exampleenvironment,notterriblyuseful*/char*constps_envp[]= {“PATH=/bin:/usr/bin”,“TERM=console”,0};/*Possiblecallstoexecfunctions*/execl(“/bin/ps”,“ps”,“ax”,0); /*assumespsisin/bin*/execlp(“ps”,“ps”,“ax”,0); /*assumes/binisinPATH*/execle(“/bin/ps”,“ps”,“ax”,0,ps_envp); /*passesownenvironment*/execv(“/bin/ps”,ps_argv);execvp(“ps”,ps_argv);execve(“/bin/ps”,ps_argv,ps_envp);11§5.3创建进程替换进程映像示例#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){printf(“Runningpswithexeclp\n”);execlp(“ps”,“ps”,“ax”,0);printf(“Done.\n”);exit(0);}$./exam09_02RunningpswithexeclpPIDTTYSTATTIMECOMMAND1?S0:03init[5]...1262pts/1Ss0:00/bin/bash1514pts/1R+0:00psax没有输出“Done”说明ps命令结束后,不再返回exam09_02程序,所以不会打印出“Done”。新进程的PID、PPID及nice值和原先的完全一样。exec系列函数启动的新进程的参数表和环境变量的总长度是有限制的。上限由ARG_MAX给出,POSIX规定至少为4KB,Linux中为128KB。原进程中已打开的文件描述符在新进程中仍保持打开(除非“执行时关闭标志”被置位)。原进程中打开的目录流在新进程中被关闭。12复制进程映像可以调用fork函数创建一个新进程。#include<unistd.h>pid_tfork(void);说明返回值 成功时,父进程中返回子进程的PID,子进程中返回0;失 败时返回-1,并设置errno变量。fork系统调用复制当前进程,在进程表中添加一个新的表项,新表项的多数属性与当前进程相同。新进程与原进程执行的代码完全相同,但新进程有自己的数据空间、环境和文件描述符。通常,使用fork和exec系列函数一起创建多进程环境。典型的fork代码。§5.3创建进程典型错误为E_AGAIN——子进程超过CHILD_MAX限制ENOMEM——进程表无足够空间pid_tnew_pid;new_pid=fork();switch(new_pid){case-1:/*Error*/break;case0:/*Wearechild*/break;default:/*Weareparent*/break;}13复制进程映像示例§5.3创建进程#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpid;char*message;intn;

printf(“forkprogramstarting\n”);pid=fork();switch(pid){case-1:perror(“forkfailed”);exit(1);case0:message=“Thisisthechild”;n=5;break;default:message=“Thisistheparent”;n=3;break;}

for(;n>0;n--){puts(message);sleep(1);}

exit(0);}$./fork1forkprogramstartingThisisthechildThisistheparentThisistheparentThisisthechildThisistheparentThisisthechild$ThisisthechildThisisthechild运行结果14等待进程在父进程中调用wait函数可以让父进程等待任一子进程的结束。#include<sys/types.h>#include<sys/wait.h>pid_twait(int*status);说明status 输出参数,用于获取子进程的退出状态。返回值 成功时,返回已结束子进程的PID;出错时返回-1,并设置 errno变量。以下宏可用于解释子进程退出状态信息。§5.3创建进程宏说明WIFEXITED(status)

如果子进程正常结束,则取非零值。WEXITSTATUS(status)

如果WIFEXITED非零,则得到子进程的退出码。WIFSIGNALED(status)

如果子进程因未捕获的信号而终止,则取非零值。WTERMSIG(status)

如果WIFSIGNALED非零,则得到引起子进程终止的信号代码。WIFSTOPPED(status)

如果子进程已意外终止,则取非零值。WSTOPSIG(status)

如果WIFSTOPPED非零,则得到引起子进程终止的信号代码。15等待进程在父进程中调用waitpid函数可以让父进程等待指定的子进程的结束。#include<sys/types.h>#include<sys/wait.h>pid_twaitpid(pid_tpid,int*status,intoptions);说明pid 要等待的子进程的PID,值为-1时意为任一子进程。status 输出参数,用于获取子进程的退出状态。options 常见的选项为WNOHANG,意为不挂起调用者进程。返回值 成功时,返回已结束子进程的PID;若指定了WNOHANG选 项,但没有子进程终止,则返回0。出错时返回-1,并设置 errno变量。§5.3创建进程若要周期性地检查某个子进程是否已终止,可以使用如下调用方式:

waitpid(child_pid,(int*)0,WNOHANG);16等待进程示例intmain(){pid_tpid;char*message;intn;intexit_code;

printf(“forkprogramstarting\n”);pid=fork();switch(pid){case-1:perror(“forkfailed”);exit(1);case0:message=“Thisisthechild”;n=5;exit_code=37;break;default:message=“Thisistheparent”;n=3;exit_code=0;break;}§5.3创建进程for(;n>0;n--){puts(message);sleep(1);}

if(pid!=0){intstat_val;pid_tchild_pid;

child_pid=wait(&stat_val);

printf(“Childhasfinished:PID =%d\n”, child_pid);if(WIFEXITED(stat_val))printf(“Childexitedwithcode%d\n”, WEXITSTATUS(stat_val));elseprintf(“Childterminatedabnormally\n”);}exit(exit_code);}$./waitforkprogramstartingThisisthechildThisistheparentThisistheparentThisisthechildThisistheparentThisisthechildThisisthechildThisisthechildChildhasfinished:PID=1582Childexitedwithcode37$运行结果17僵尸进程子进程终止时,它与父进程之间的关联还会保持(进程表中代表子进程的表项不会立刻释放,其中含退出码),直到父进程也正常终止或父进程调用了wait或waitpid函数。已结束运行但进程表项仍存在的子进程称为“僵尸进程”。如果一个僵尸进程的父进程因异常而终止,该僵尸进程将由init进程接管,它将一直保持在进程表中直到被init进程发现并释放。应尽量避免产生僵尸进程。§5.3创建进程18概述信号是UNIX和Linux系统为响应某些条件而产生的一个事件。信号一般是因某些错误条件而产生的,如内存段冲突、浮点处理器错误或非法指令等。信号可作为进程间通信的一种机制,由一个进程发送给另一个进程。信号可被生成、捕获或响应,对于一些信号也可被忽略。信号的名称在头文件signal.h中定义,均以SIG开头。§5.4信号19信号种类下表中的信号未安排捕获时,进程会立刻终止,并生成核心转储文件core。§5.4信号信号名称说明SIGABORT*进程异常终止SIGALRM超时。一般由alarm设置的定时器产生。SIGFPE*浮点运算异常SIGHUP终端关闭或断开连接。由处于非连接状态的终端发给控制进程或由控制进程在自身结束时发给每个前台进程。SIGILL*非法指令。通常由一个崩溃的程序或无效的共享内存模块引起。SIGINT程序终止,一般由从终端敲入的中断字符(Ctrl+C)产生。SIGKILL终止进程(此信号不能被捕获或忽略),一般在shell中用它来强制终止异常进程。SIGPIPE向管道写数据时没有与之对应的读进程时产生。SIGQUIT程序退出。一般由终端敲入的退出字符(Ctrl+\)产生。SIGSEGV*无效内存段访问。一般是因为对内存中的无效地址进行读写引起,如数组越界、解引用无效指针。SIGTERMkill命令默认发送的信号,要求进程结束运行。UNIX在关机时也用此信号要求服务停止运行。SIGUSR1用户定义信号1,用于进程间通信SIGUSR2用户定义信号2,用于进程间通信20信号种类其它常见信号。§5.4信号信号名称说明SIGCHLD子进程停止或退出时产生,默认被忽略SIGCONT如果进程被暂停则继续执行。SIGSTOP停止执行(此信号不能被捕获或忽略)SIGTSTP终端挂起。通常因按下Ctrl+Z组合键而产生。SIGTTIN后台进程尝试读操作。shell用以表明后台进程因需要从终端读取输入而暂停运行。SIGTTOU后台进程尝试写操作。shell用以表明后台进程因需要产生输出而暂停运行。在命令行使用:kill-<信号名><PID>,可以向PID进程发送指定信号。如:kill–HUP512在命令行使用:killall-<信号名><命令名>,可以向所有运行<命令名>的进程发送指定信号。如:killall–HUPinetd21捕获信号在程序中可以调用signal函数来捕获信号。§5.4信号说明sig 准备捕获或忽略的信号。func 捕获到信号后将要调用的回调函数。可以设为以下特殊值:SIG_IGN 忽略信号SIG_DFL 恢复默认行为返回值 成功时返回原先定义信号处理函数,如果原先未定义则返回 SIG_ERR,并设置errno为一正数值;如给出的是一个无效的 信号或不可捕获或不可忽略的信号,则返回SIG_ERR,并设 置errno为EINVAL。注意:回调函数中捕获到一次信号后,对相应的信号的处理方式会自动恢复到默认行为。#include<signal.h>void(*signal(intsig,void(*func)(int)))(int);#include<signal.h>typedefvoid(*CALLBACK)(int);CALLBACKsignal(intsig,CALLBACKfunc);22捕获信号示例§5.4信号#include<signal.h>#include<stdio.h>#include<unistd.h>voidouch(intsig){printf(“OUCH!-Igotsignal%d\n”,sig);(void)signal(SIGINT,SIG_DFL);}intmain(){(void)signal(SIGINT,ouch);while(1){printf(“HelloWorld!\n”);sleep(1);}}$./ctrlc1HelloWorld!HelloWorld!HelloWorld!HelloWorld!^COUCH!-Igotsignal2HelloWorld!HelloWorld!HelloWorld!HelloWorld!^C$运行结果23发送信号进程可以通过kill系统调用向包括自身在内的进程发送一个信号。§5.4信号说明pid 接收信号的进程PID。sig 要发送的信号。返回值 成功时返回0;失败时返回-1,并设置errno变量。EINVAL 给定的信号无效EPERM 发送进程权限不够ESRCH 目标进程不存在注:发送信号的进程应具有相应的权限。一般而言,两个进程应拥有相同的UID(即只能发送信号给属于自己的进程),但超级用户可以发送信号给任何进程。#include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsig);24发送信号进程可以通过alarm函数在经过一定时间后发送一个SIGALRM信号,即设置一个闹钟(定时器)。§5.4信号说明seconds 指定几秒后发送SIGALRM信号,0表示取消设置。返回值 成功时返回以前设置的闹钟时间的余留秒数;失败时返回0。注:在接收到SIGALRM信号之前再次调用alarm函数,闹钟会重新开始计时。#include<unistd.h>unsignedalarm(unsignedseconds);25sigaction系统调用在Linux中,提供了一个比signal更健壮的用于捕获信号的编程接口:sigaction。§5.4信号#include<unistd.h>intsigaction(intsig,conststructsigaction*act, structsigaction*oldact);说明sig 准备捕获或忽略的信号。act 将要设置的信号处理动作。oldact 用于取回原先的信号处理动作。返回值 成功时返回0;失败时返回-1,并设置errno变量,如给出的 是一个无效的信号或不可捕获或不可忽略的信号,errno为 EINVAL。注:和signal不一样,由sigaction设置的信号处理函数默认是不被重置的。如果需要自动重置,应在sa_flags成员中包含值SA_RESETHAND。structsigaction{void(*sa_handler)(int);//信号处理函数,同signalsigset_tsa_mask; //回调过程中将被屏蔽的信号集

intsa_flags; //可决定回调行为的位标志值

…}SA_NOCLDSTOP子进程停止时不产生SIGCHLD信号SA_RESETHAND在信号处理函数入口处将对此信号的处理方式重置为SIG_DFL SA_RESTART重启可中断的函数而不是给EINTR错误SA_NODEFER捕获到信号时不将它添加到信号屏蔽字中26sigaction系统调用示例§5.4信号#include<signal.h>#include<stdio.h>#include<unistd.h>voidouch(intsig){printf(“OUCH!-Igotsignal%d\n”,sig);}intmain(){structsigactionact;act.sa_handler=ouch;sigemptyset(&act.sa_mask);act.sa_flags=0;sigaction(SIGINT,&act,0);while(1){printf(“HelloWorld!\n”);sleep(1);}}$./ctrlc2HelloWorld!HelloWorld!HelloWorld!^COUCH!-Igotsignal2HelloWorld!HelloWorld!^COUCH!-Igotsignal2HelloWorld!HelloWorld!^\Quit$运行结果27信号集在头文件signal.h中定义了类型sigset_t和用来处理信号集的函数。§5.4信号#include<signal.h>intsigemptyset(sigset_t*set);intsigfillset(sigset_t*set);intsigaddset(sigset_t*set,intsig);intsigdelset(sigset_t*set,intsig);intsigismember(constsigset_t*set,intsig);说明set 信号集sig 信号。返回值 失败时返回-1,并设置errno变量,只有一个错误代码 EINVAL,表示给定的信号无效。成功时,sigismeber函数 返回1或0;其他函数返回0。typedefstruct{unsignedlongsig[_NSIG_WORDS];}sigset_t;28概述消息队列(messagequeues)是Linux中的一种IPC(Inter-ProcessCommunication)机制。消息队列提供了一种在两个不相关的进程间传递数据的有效方法。消息队列独立于发送和接收进程而存在。使用消息队列可以从一个进程向另一个进程发送数据块。每个数据块有一个最大长度的限制,系统中所有队列所包含的全部数据块的总长度也有一个上限值。Linux中有两个宏定义MSGMAX和MSGMNB,它们分别定义了一条消息的最大长度和一个队列的最大长度。§5.5消息队列29msgget函数可以通过msgget系统调用创建或获取一个消息队列。说明key 一个整数值,用于标识一个消息队列。msgflg 一组标志。低端的九个bit对应九种访问权限,如open系统 调用。此外,可以和IPC_CREAT按位或以创建一个新的消 息队列。返回值 成功时返回一个正整数,即消息队列标识符;失败时返回-1, 并设置errno变量。注:使用IPC_CREAT标志,当消息队列已存在时,只是忽略创建动作,而不会出错。#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>intmsgget(key_tkey,intmsgflg);§5.5消息队列30msgsnd函数可以通过msgsnd系统调用把一条消息添加到消息队列中。说明msgid 由msgget返回的消息队列标识符。msgp 指向要发送的消息的指针。msgsz 消息长度。这个长度不包括长整形成员变量的长度。msgflg 标志。指定IPC_NOWAIT时,表示当队列满或达到系统限制 时,函数立即返回(返回值为-1),不发送消息。返回值 成功时返回0;失败时返回-1,并设置errno变量。注:消息的结构受两方面的约束:长度必须小于系统规定的上限。必须以一个长整型成员变量开始,接收函数以此成员来确定消息的类型。#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>intmsgsnd(intmsqid,constvoid*msgptr,size_tmsgsz,intmsgflg);§5.5消息队列structmy_message{longintmessage_type;/*Thedatayouwishtotransfer*/};31msgrcv函数可以通过msgrcv系统调用从一个消息队列获取消息。说明msgid 由msgget返回的消息队列标识符。msgp 指向准备接收消息的指针。msgsz 消息长度。这个长度不包括长整形成员变量的长度。msgtyp 一个长整数。若值为0,获取队列中的第一个可用消息;若值 大于0,获取具有相同类型的第一个消息;若小于0,获取消息 类型小于或等于其绝对值的第一个消息。msgflg 标志。指定IPC_NOWAIT时,表示当中没有相应类型消息 时,函数立即返回(返回值为-1),不接收消息。返回值 成功时返回0;失败时返回-1,并设置errno变量。#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>ssize_tmsgrcv(intmsqid,void*msgp,size_tmsgsz,longmsgtyp,intmsgflg);§5.5消息队列32msgctl函数可以通过msgctl系统调用直接控制消息队列。说明msgid 由msgget返回的消息队列标识符。cmd 要采取的动作。IPC_WAIT 把msqid_ds结构中的数据设置为消息队列的当前关联值。IPC_SET 如果进程有足够的权限,就把消息队列的当前关联值设置为msgqid_ds 结构中给出的值。IPC_RMID 删除消息队列。buf 缓冲区,作用视cmd而定。返回值 成功时返回0;失败时返回-1,并设置errno变量。如果在进 程正阻塞于msgsnd或msgrcv中等待时删除消息队列,则这 两个函数将失败。#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>intmsgctl(intmsqid,intcmd,structmsqid_ds*buf);§5.5消息队列structmsqid_ds{uid_tmsg_perm.uid;uid_tmsg_perm.gidmode_tmsg_perm.mode;};33/*接收程序.msg1.c*/#include<stdlib.h>#include<stdio.h>#include<string.h>#include<errno.h>#include<unistd.h>#include<sys/msg.h>structmy_msg_st{longintmy_msg_type;charsome_text[BUFSIZ];};intmain(){intrunning=1;intmsgid;structmy_msg_stsome_data;longintmsg_to_receive=0;/*建立消息队列*/msgid=msgget((key_t)1234,0666|IPC_CREAT);

综合示例§5.5消息队列34综合示例§5.5消息队列if(msgid==-1){fprintf(stderr,"msggetfailedwitherror:%d\n",errno);exit(EXIT_FAILURE);}while(running){/*接收消息*/if(msgrcv(msgid,(void*)&some_data,BUFSIZ,msg_to_receive,0)==-1){fprintf(stderr,"msgrcvfailedwitherror:%d\n",errno);exit(EXIT_FAILURE);}printf("Youwrote:%s",some_data.some_text);if(strncmp(some_data.some_text,"end",3)==0){running=0;}}/*删除消息队列*/if(msgctl(msgid,IPC_RMID,0)==-1){fp

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论