c语言多进程多线程编程.pdf
进程是一个具有独立功能的程序关于某个数据集合的一次而可以并发执行的运行活动,是处于活动状态的计算机程序。进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。进程是资源管理的最小单位,线程是程序执行的最小单位。进程管理着资源(比如cpu、内存、文件等),而将线程分配到某个cpu上执行。再操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文件切换开销。进程:代码段(程序代码)堆栈段(局部变量、函数返回地址、函数参数)数据段(全局变量、常数等)在 Linux系统中,系统调用fork后,内核为完成系统调用fork要进行几步操作:第一步,为新进程在进程表中分配一个表项。系统对一个普通用户可以同时运行的进程数是有限制的,对趙纵用户没有该限制,但不能超过进程表的最大表项的数目。第二步,给了进程个唯的进程标识号(PID)。该进程标识号其实就是该衣项在进程表中的索引号。第三步,复制一个父进程的进程表项的副本给子进程。内核初始化子进程的进程表项时,是从父进程处拷贝的。所以子进程拥有与父进程一样的uid、当前目录、当前根、用户文件描述符表等。第四步,把与父进程相连的文件表和索引节点表的引用数加1。这些文件自动地与该子进程相连第五步,内核为子进程创建用户级上下文。内核为子进程的代码段分配内存,并复制父进程的区内容,生成的是进程的静态部分。第六步,生成进程的动态部分,然后对父进稈返回子进稈的pid,对子进稈返回0。从父进程拷贝的内容主要有●用户标识符,包括实际用户号(rea1)和有效用户号( effective)环境变量打开的文件描述符、套接字描述符●信号处理设置堆栈●目录●进程组标志( process ID)●会晤组标志( session id)●正文子进程特有内容●进程号●父进程号●进程执行时间●木处理的信号被处埋为空●不继承异步的输入输出操作简述; fork(调用成功时,分别返回两个整数,对父进程返回〉0的整数,对」进程返回函数执行过程:①)内核在系统进程表中,创建一个新条日②复制父进程内容(已打开的文件描述符、堆栈、正文等);③修改两者的堆栈,给父进程返回子进程号,给子进程返回0(父进程知道每个子进程的标志号,而子进程可根据需要调用 getppid o来获得父进程的标志号)。例子pid t fork(void)#inc lude unistd. hpid t pidif((pid-fork()=0应用程序fork(//子进程代码exit(o)else if(pid>0)父进程了进程了进程2/父进程代码exit(O)elseprintf(" Errorexit(1)22 system()子进程执行指定的命令功能:产生一个新的进程,子进程执行指定的命令#include #include int system(string)char *string说明木调用将参数suig传递给一个命令解释器(一般为sh)执行,即 string被解释为一条命令,由$h执行该命令若参数 string为一个空指针则为检查命令解释器是否存在.该命令可以同命令行命令相同形式,但由于命令做为一个参数放在系统调用中,应注意编译时对特殊意义字符的处理.命令的查找是按PATH环境变量的定义的.命令所生成的后果一般不会对父进程造成影响返回值:当参数为空指针吋,只有当命令解释器有效吋返回值为非零.若参数不为空指针,返回值为该命令的返回状态(同 waitpid的返回值.命令无效或语法错误则返回非零值,所执行的命令被终止.其他情况则返回-1例子: char command[81intfor(1=1;<8;i++){sprintf(command, "ps t tty%021", 1)system(command)23exec0)执行一个文件功能执行一个文件#include unistd. h>int execve( const char*path,char* const米argv,char米cons米envp)int execl(const char* path, char* arg,.)int execp(const char* file, chark arg, ..int execle const chara path, const char* argv ,., char* const* envp)int execv(const char* path, char* const* arg)int execvp( const char* file, char* const* arg)说明:exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件其中只有 CXCCVC是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。与般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程I等一些表面上的信息仍保持原样,颇有些神似”三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往卜执行。fork()和 exec o这两个函数,前者用于并行执行,父、子进程执行相同正文中的不同部分;后者用于调用其他进程,父、了进程执行不同的正文,调用前,般应为了进程创造个干净的环境。fork(以后,父、子进程共亨代码段,并只重新创建数据有改变的页(段页式管理)exec(以后,建立新的代码段,用被调用程序的內容填充。前者的子进稈执行后续的公共代码,后者的子进程不执行后续的公共代码。父、了进程以及各个了进程执行的顺序不定例子prnt(" now this proccss will be ps commandn");execl("/bin/ps", ps","-eT" NULL);24 popen()初始化从/到一个迸程的管道功能:初始化从/到一个进程的管道include FILE *popen( command typechar *command, type说明:本系统调用在调用进程和被执行命令间创建一个管道.参数 command做为被执行的命令行yp做为IO模式,"r"为从被执行命令读,"w"为向被执行命令写返回一个标准流指针,做为管道描述符向被执行命令读或写数据(做为被执行命令的STDN或STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令的输出信息或者向命令输入信息返冋值:不成功则返冋NUL成功则返回管道的文件指针.2 pclose0关闭到一个进程的管道功能关闭到一个进程的管道.语法#include int pclose(strm)FILE strm说明:本系统调用用于关闭由 porco打开的管道,并会等待由 porno激活的命令执行结束后,关闭管道后读取命令返回码返回值:若关闭的文件描述符不是由 popen(打开的,则返回-1Fi f: printf("now this process will call popen system call\n")FILE fdif (( fd=popin("ps-cf",")==NULLprintf("call popen failed\n")return:else ichar str[80]while(fgets(str, 80, fd)!==NULL)printf("%os n", strclose(rd)2.6. wait(等待一个子进程返回并修改状态功能:等待一个子进程返回并修改状态#include #include pid t wait(stat locint *stat loc说明:允许调用进程取得子进程的状态信息调用进程将会挂起直到其个子进程终止返回值:等待到一个子进程返回时,返回值为该子进程号,否则返回值为1.同时 stat loc返回子进程的返回值例子:/父进程*if (forko>0)wail((int *)0)泮*父进程等待子进程的返回*/*子进程处理过程*exit(O)27 waitpid()等待指定进程号的子进程的返回并修改状态功能:等待指定进程号的子进程的返回并修改状态语法#include #include pid t waitpid (pid, stat loc, options)pid t pidint *stat_ loc, options说明:当pid等于-l, options等于0时,该系统调用等同于 waito.否则该系统调用的行为由参数pid和 options决定pid指定了一组父进程要求知道其状态的子进程:l:要求知道任何一个子进程的返回状态>0:要求知道进程号为pid值的子进程的状态-1:要求知道进程组号为pid的绝对值的了进程的状态options参数为以比特方式表小的标志以或运算组成的位图,每个标志以字节中某个比特置1表示WUNTRACED报告任何未知而又凵停止运行的指定进程号的子进程的状态该子进程的状态自停止运行时起就没有被报告过WCONTINUED:报告任何继续运行的指定进程号的了进程的状态,该」进程的状态自继续运行起就没有被报告过WHOHANG:若调用本系统调用时,指定进程号的子进程的状态目前并不是立即有效的(即可被立即读取的)调用进程并被暂停执行WNOWAIT:保持将其状态设置在 stat loc的进程在可等待状态.该进程将等待直到下次被要求其返回状态值.返回值:等待到一个」进程返回时,返回值为该子进程号,否则返回值为1.同时 stat loc返回子进程的返回值例子 pid t piint stat loc;/父进程*if(pid=fork)>0)iwailpid(pid, &stat loc, O)*父进程等待进程号为pid的子进程的返回*else i/*子进程的处理过程*exit(1);泮*父进程printf("stat loc is [%od in", stat loc)泮*字符串" 'stat loc is["将被打印出来*28. setpgrp()设置进程组号和会话号功能设置进程组号和会话号.#include pid t setpgrpo说明若调用进程不是会话首进程将进程组号和会话号都设置为与它的进程号相等并释放调用进程的控制终端.返回值:调用成功后,返回新的进程组号例子:/*父进程处理*(fork(>0){*父进程处理*elsesetpgrpO*子进程的进程组号凵修改成与它的进程号相同*exit(O)29exit0)终止进程功能:终止进程include void exit(status)int status说明:调用进程被该系统调用终止.引起附加的处理在进程被终止前全部结束返回值:无2.10. signa(信号管理功能功能:信号管理功能#include void (*signal(sig, disp))(int)int sIg:void (*disp)(int)void (*sigset(sig, disp))(int)int sig:void (*disp)(int)int sighold(sigint sig:int sigrelse(sig)int sig:int sigignore(sig)int s1g:int sigpause(sig)int sig:说明:这些系统调用提供了应用程序对指定信号的简单的信号处理.signalo和 signet()用于修改信号定位参数sig指定信号(除了SIGKILL和 SIGSTOP这两种信号由系统处理,用户程序不能捕捉到).dip指定新的信号定位,即新的信号处理函数指针可以为SIG IGN, SIG DFL或信号句柄地址.若使用 signalo,disp是信号句柄地址,sig不能为 SIGILL, SIGTRAP或 SIGPWR,收到该信号时,系统首先将重置sig的信号句柄为 SIG DFL,然后执行信号句枘.若使用 signet,disp是信号句柄地址,该信号时,系统首先将该信号加入调用进程的信号掩码中,然后执行信号句柄.当信号句柄运行结東后,系统将恢复调用进程的信号掩码为信号收到前的状态.另外,使用 sigel时,disp为 SIG HOLD,则该信号将会加入调用进程的信号掩码中而信号的定位不变.sigholdo将信号加入调用进程的信号掩码中.sigrelseo将信号从凋用进程的信号掩码中删除sigignoreo将信号的定位设置为 SIG IGNSigpause()将信号从调用进程的信号掩码中刖除,同吋挂起调用进程直到收到信号.若信号 SIGCHLD的信号定位为 SIG IGN,则调用进程的子进程在终止时不会变成僵死进程.调用进程也不用等待子进程返回并做相应处理返回值:调用成功则 signalo返叵最近调用 signalo设置的disp的值.否则返回 SIG ERR.例子一:设置用户自己的信号中断处理函数,以SGNT信号为例int flag=0vold myseprintf("gct signal SIGINT\n);/*若要重新设置 SIGINT信号中断处坦函数为本函数则执行以*下步void(a)0:a=myselfignal(siGiNT, aflag-2mairwhile(1)isleep(2000);/等待中断信号*if (flag==1)printf("skip systcm call slccpin);exit(O)if(flag==2)iprintr"skip system call sleep\n")printf("waiting for ncxt signalin)21k(向一个或一组进程发送一个信号功能:向·个或组进程发送个信号#include #include signal. h>int kill(pid, sig)pid t pid;int sig说眀:本系统调用向一个或一组进程发送一个信号,该信号由参数sig指定,为系统给出的信号表中的个若为0空信号)则检查错误但实际上并没有发送信号,用于检查pid的有效性pid指定将要被发送信号的进程或进程组.pi若大于0,则信号将被发送到进程号等」pid的进程:若pid等于0则信号将被发送到所有的与发送信号进程同在一个进程组的进程(系统的特殊进程除外);若pid小于-1.则信号将被发送到所有进程组号与pid绝对值相同的进程;若pid等于-1,则信号将被发送到所有的进程(特殊系统进程除外)
用户评论