c语言多进程多线程编程.docx

上传人:b****2 文档编号:939587 上传时间:2023-04-30 格式:DOCX 页数:102 大小:94KB
下载 相关 举报
c语言多进程多线程编程.docx_第1页
第1页 / 共102页
c语言多进程多线程编程.docx_第2页
第2页 / 共102页
c语言多进程多线程编程.docx_第3页
第3页 / 共102页
c语言多进程多线程编程.docx_第4页
第4页 / 共102页
c语言多进程多线程编程.docx_第5页
第5页 / 共102页
c语言多进程多线程编程.docx_第6页
第6页 / 共102页
c语言多进程多线程编程.docx_第7页
第7页 / 共102页
c语言多进程多线程编程.docx_第8页
第8页 / 共102页
c语言多进程多线程编程.docx_第9页
第9页 / 共102页
c语言多进程多线程编程.docx_第10页
第10页 / 共102页
c语言多进程多线程编程.docx_第11页
第11页 / 共102页
c语言多进程多线程编程.docx_第12页
第12页 / 共102页
c语言多进程多线程编程.docx_第13页
第13页 / 共102页
c语言多进程多线程编程.docx_第14页
第14页 / 共102页
c语言多进程多线程编程.docx_第15页
第15页 / 共102页
c语言多进程多线程编程.docx_第16页
第16页 / 共102页
c语言多进程多线程编程.docx_第17页
第17页 / 共102页
c语言多进程多线程编程.docx_第18页
第18页 / 共102页
c语言多进程多线程编程.docx_第19页
第19页 / 共102页
c语言多进程多线程编程.docx_第20页
第20页 / 共102页
亲,该文档总共102页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

c语言多进程多线程编程.docx

《c语言多进程多线程编程.docx》由会员分享,可在线阅读,更多相关《c语言多进程多线程编程.docx(102页珍藏版)》请在冰点文库上搜索。

c语言多进程多线程编程.docx

c语言多进程多线程编程

C语言多进程编程

一.多进程程序的特点

进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。

进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。

进程是资源管理的最小单位,线程是程序执行的最小单位。

进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。

在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文切换开销。

进程的状态系统为了充分的利用资源,对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态.

新建表示进程正在被创建,

运行是进程正在运行,

阻塞是进程正在等待某一个事件发生,

就绪是表示系统正在等待CPU来执行命令,

完成表示进程已经结束了系统正在回收资源.

由于UNIX系统是分时多用户系统,CPU按时间片分配给各个用户使用,而在实质上应该说CPU按时间片分配给各个进程使用,每个进程都有自己的运行环境以使得在CPU做进程切换时不会"忘记"该进程已计算了一半的"半成品”.以DOS的概念来说,进程的切换都是一次"DOS中断"处理过程,包括三个层次:

1)用户数据的保存:

包括正文段(TEXT),数据段(DATA,BSS),栈段(STACK),共享内存段(SHAREDMEMORY)的保存.

2)寄存器数据的保存:

包括PC(programcounter,指向下一条要执行的指令的地址),PSW(processorstatusword,处理机状态字),SP(stackpointer,栈指针),PCBP(pointerofprocesscontrolblock,进程控制块指针),FP(framepointer,指向栈中一个函数的local变量的首地址),AP(augumentpointer,指向栈中函数调用的实参位置),ISP(interruptstackpointer,中断栈指针),以及其他的通用寄存器等.

3)系统层次的保存:

包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时间片时能正常运行。

既然系统已经处理好所有这些中断处理的过程,我们做程序还有什么要担心的呢?

我们尽可以使用系统提供的多进程的特点,让几个程序精诚合作,简单而又高效地把结果给它搞出来。

另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程?

将会对UNIX系统机制有一个较深的认识.首先我介绍一下多进程程序的一些突出的特点:

1.1并行化

一件复杂的事件是可以分解成若干个简单事件来解决的,这在程序员的大脑中早就形成了这种概念,首先将问题分解成一个个小问题,将小问题再细分,最后在一个合适的规模上做成一个函数.在软件工程中也是这么说的.如果我们以图的方式来思考,一些小问题的计算是可以互不干扰的,可以同时处理,而在关键点则需要统一在一个地方来处理,这样程序的运行就是并行的,至少从人的时间观念上来说是这样的.而每个小问题的计算又是较简单的.

1.2简单有序

这样的程序对程序员来说不亚于管理一班人,程序员为每个进程设计好相应的功能,并通过一定的通讯机制将它们有机地结合在一起,对每个进程的设计是简单的,只在总控部分小心应付(其实也是蛮简单的),就可完成整个程序的施工.

1.3.互不干扰

这个特点是操作系统的特点,各个进程是独立的,不会串位.

1.4.事务化

比如在一个数据电话查询系统中,将程序设计成一个进程只处理一次查询即可,即完成一个事务.当电话查询开始时,产生这样一个进程对付这次查询;另一个电话进来时,主控程序又产生一个这样的进程对付,每个进程完成查询任务后消失.这样的编程多简单,只要做一次查询的程序就可以了.

二.常用的多进程编程的系统调用

2.1.fork()创建一个新的进程.

功能:

创建一个新的进程.

语法:

#include

#include

pid_tfork();

说明:

本系统调用产生一个新的进程,叫子进程,是调用进程的一个复制品.调用进程叫父进程,子进程继承了父进程的几乎所有的属性。

进程:

代码段(程序代码)

堆栈段(局部变量、函数返回地址、函数参数)

数据段(全局变量、常数等)

在Linux系统中,系统调用fork后,内核为完成系统调用fork要进行几步操作:

第一步,为新进程在进程表中分配一个表项。

系统对一个普通用户可以同时运行的进程数是有限制的,对超级用户没有该限制,但不能超过进程表的最大表项的数目。

第二步,给子进程一个唯一的进程标识号(PID)。

该进程标识号其实就是该表项在进程表中的索引号。

第三步,复制一个父进程的进程表项的副本给子进程。

内核初始化子进程的进程表项时,是从父进程处拷贝的。

所以子进程拥有与父进程一样的uid、当前目录、当前根、用户文件描述符表等。

第四步,把与父进程相连的文件表和索引节点表的引用数加1。

这些文件自动地与该子进程相连。

第五步,内核为子进程创建用户级上下文。

内核为子进程的代码段分配内存,并复制父进程的区内容,生成的是进程的静态部分。

第六步,生成进程的动态部分,然后对父进程返回子进程的pid,对子进程返回0。

从父进程拷贝的内容主要有:

●用户标识符,包括实际用户号(real)和有效用户号(effective);

●环境变量

●打开的文件描述符、套接字描述符

●信号处理设置

●堆栈

●目录

●进程组标志(processID)

●会晤组标志(sessionID)

●正文

子进程特有内容:

●进程号

●父进程号

●进程执行时间

●未处理的信号被处理为空

●不继承异步的输入输出操作

简述:

fork()调用成功时,分别返回两个整数,对父进程返回〉0的整数,对子进程返回0,

函数执行过程:

1内核在系统进程表中,创建一个新条目;

2复制父进程内容(已打开的文件描述符、堆栈、正文等);

3修改两者的堆栈,给父进程返回子进程号,给子进程返回0(父进程知道每个子进程的标志号,而子进程可根据需要调用getppid()来获得父进程的标志号)。

例子:

pid_tfork(void)

#include

应用程序

pid_tpid;

fork()

if((pid=fork())==0)

{

//子进程代码

exit(0);

子进程2

子进程1

父进程

}

elseif(pid>0)

{

//父进程代码

exit(0);

}

else

{

printf("Error");

exit

(1);

}

2.2.system()子进程执行指定的命令

功能:

产生一个新的进程,子进程执行指定的命令.

语法:

#include

#include

intsystem(string)

char*string;

说明:

本调用将参数string传递给一个命令解释器(一般为sh)执行,即string被解释为一条命令,由sh执行该命令.若参数string为一个空指针则为检查命令解释器是否存在.

该命令可以同命令行命令相同形式,但由于命令做为一个参数放在系统调用中,应注意编译时对特殊意义字符的处理.命令的查找是按PATH环境变量的定义的.命令所生成的后果一般不会对父进程造成影响.

返回值:

当参数为空指针时,只有当命令解释器有效时返回值为非零.若参数不为空指针,返回值为该命令的返回状态(同waitpid())的返回值.命令无效或语法错误则返回非零值,所执行的命令被终止.其他情况则返回-1.

例子:

charcommand[81];

inti;

for(i=1;i<8;i++){

sprintf(command,"psttty%02i",i);

system(command);

}

2.3.exec()执行一个文件

功能:

执行一个文件

语法

#include

intexecve(constchar*path,char*const*argv,char*const*envp);

intexecl(constchar*path,char*arg,...);

intexecp(constchar*file,char*arg,...);

intexecle(constchar*path,constchar*argv,...,char*const*envp);

intexecv(constchar*path,char*const*arg);

intexecvp(constchar*file,char*const*arg);

说明:

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件

其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。

看上去还是旧的躯壳,却已经注入了新的灵魂。

只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

fork()和exec()这两个函数,前者用于并行执行,父、子进程执行相同正文中的不同部分;后者用于调用其他进程,父、子进程执行不同的正文,调用前,一般应为子进程创造一个干净的环境。

fork()以后,父、子进程共享代码段,并只重新创建数据有改变的页(段页式管理)

exec()以后,建立新的代码段,用被调用程序的内容填充。

前者的子进程执行后续的公共代码,后者的子进程不执行后续的公共代码。

父、子进程以及各个子进程执行的顺序不定。

.

例子:

printf("nowthisprocesswillbepscommand\n");

execl("/bin/ps","ps","-ef",NULL);

2.4.popen()初始化从/到一个进程的管道

功能:

初始化从/到一个进程的管道.

语法:

#include

FILE*popen(command,type)

char*command,type;

说明:

本系统调用在调用进程和被执行命令间创建一个管道.

参数command做为被执行的命令行.type做为I/O模式,"r"为从被

执行命令读,"w"为向被执行命令写.返回一个标准流指针,做为管

道描述符,向被执行命令读或写数据(做为被执行命令的STDIN或

STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令

的输出信息或者向命令输入信息.

返回值:

不成功则返回NULL,成功则返回管道的文件指针.

2.5.pclose()关闭到一个进程的管道

功能:

关闭到一个进程的管道.

语法:

#include

intpclose(strm)

FILE*strm;

说明:

本系统调用用于关闭由popen()打开的管道,并会等待由popen()

激活的命令执行结束后,关闭管道后读取命令返回码.

返回值:

若关闭的文件描述符不是由popen()打开的,则返回-1.

例子:

printf("nowthisprocesswillcallpopensystemcall\n");

FILE*fd;

if((fd=popen("ps-ef","r"))==NULL){

printf("callpopenfailed\n");

return;

}

else{

charstr[80];

while(fgets(str,80,fd)!

=NULL)

printf("%s\n",str);

}

pclose(fd);

2.6.wait()等待一个子进程返回并修改状态

功能:

等待一个子进程返回并修改状态

语法:

#include

#include

pid_twait(stat_loc)

int*stat_loc;

说明:

允许调用进程取得子进程的状态信息.调用进程将会挂起直到其

一个子进程终止.

返回值:

等待到一个子进程返回时,返回值为该子进程号,否则返回值为

-1.同时stat_loc返回子进程的返回值.

例子:

/*父进程*/

if(fork()>0){

wait((int*)0);

/*父进程等待子进程的返回*/

}

else{

/*子进程处理过程*/

exit(0);

}

2.7.waitpid()等待指定进程号的子进程的返回并修改状态

功能:

等待指定进程号的子进程的返回并修改状态

语法:

#include

#include

pid_twaitpid(pid,stat_loc,options)

pid_tpid;

int*stat_loc,options;

说明:

当pid等于-1,options等于0时,该系统调用等同于wait().否则该

系统调用的行为由参数pid和options决定.

pid指定了一组父进程要求知道其状态的子进程:

-1:

要求知道任何一个子进程的返回状态.

>0:

要求知道进程号为pid值的子进程的状态.

<-1:

要求知道进程组号为pid的绝对值的子进程的状态.

options参数为以比特方式表示的标志以或运算组成的位图,每个

标志以字节中某个比特置1表示:

WUNTRACED:

报告任何未知而又已停止运行的指定进程号的子进

程的状态.该子进程的状态自停止运行时起就没有被报告过.

WCONTINUED:

报告任何继续运行的指定进程号的子进程的状态,

该子进程的状态自继续运行起就没有被报告过.

WHOHANG:

若调用本系统调用时,指定进程号的子进程的状态目

前并不是立即有效的(即可被立即读取的),调用进程并被暂停执行.

WNOWAIT:

保持将其状态设置在stat_loc的进程在可等待状态.

该进程将等待直到下次被要求其返回状态值.

返回值:

等待到一个子进程返回时,返回值为该子进程号,否则返回值为1.

同时stat_loc返回子进程的返回值.

例子:

pid_tpid;

intstat_loc;/*父进程*/

if((pid="fork())">0){

waitpid(pid,&stat_loc,0);

/*父进程等待进程号为pid的子进程的返回*/

}

else{

/*子进程的处理过程*/

exit

(1);

}

/*父进程*/

printf("stat_locis[%d]\n",stat_loc);

/*字符串"stat_locis[1]"将被打印出来*/

2.8.setpgrp()设置进程组号和会话号

功能:

设置进程组号和会话号.

语法:

#include

pid_tsetpgrp()

说明:

若调用进程不是会话首进程.将进程组号和会话号都设置为与它

的进程号相等.并释放调用进程的控制终端.

返回值:

调用成功后,返回新的进程组号.

例子:

/*父进程处理*/

if(fork()>0){

/*父进程处理*/

}

else{

setpgrp();

/*子进程的进程组号已修改成与它的进程号相同*/

exit(0);

}

2.9.exit()终止进程

功能:

终止进程.

语法:

#include

voidexit(status)

intstatus;

说明:

调用进程被该系统调用终止.引起附加的处理在进程被终止前全

部结束.

返回值:

2.10.signal()信号管理功能

功能:

信号管理功能

语法:

#include

void(*signal(sig,disp))(int)

intsig;

void(*disp)(int);

void(*sigset(sig,disp))(int)

intsig;

void(*disp)(int);

intsighold(sig)

intsig;

intsigrelse(sig)

intsig;

intsigignore(sig)

intsig;

intsigpause(sig)

intsig;

说明:

这些系统调用提供了应用程序对指定信号的简单的信号处理.

signal()和sigset()用于修改信号定位.参数sig指定信号(除了

SIGKILL和SIGSTOP,这两种信号由系统处理,用户程序不能捕捉到).

disp指定新的信号定位,即新的信号处理函数指针.可以为

SIG_IGN,SIG_DFL或信号句柄地址.

若使用signal(),disp是信号句柄地址,sig不能为SIGILL,SIGTRAP

或SIGPWR,收到该信号时,系统首先将重置sig的信号句柄为SIG_DFL,

然后执行信号句柄.

若使用sigset(),disp是信号句柄地址,该信号时,系统首先将该

信号加入调用进程的信号掩码中,然后执行信号句柄.当信号句柄

运行结束

后,系统将恢复调用进程的信号掩码为信号收到前的状态.另外,

使用sigset()时,disp为SIG_HOLD,则该信号将会加入调用进程的

信号掩码中而信号的定位不变.

sighold()将信号加入调用进程的信号掩码中.

sigrelse()将信号从调用进程的信号掩码中删除.

sigignore()将信号的定位设置为SIG_IGN.

sigpause()将信号从调用进程的信号掩码中删除,同时挂起调用

进程直到收到信号.

若信号SIGCHLD的信号定位为SIG_IGN,则调用进程的子进程在终

止时不会变成僵死进程.调用进程也不用等待子进程返回并做相

应处理.

返回值:

调用成功则signal()返回最近调用signal()设置的disp的值.

否则返回SIG_ERR.

例子一:

设置用户自己的信号中断处理函数,以SIGINT信号为例:

intflag=0;

voidmyself()

{

flag=1;

printf("getsignalSIGINT\n");

/*若要重新设置SIGINT信号中断处理函数为本函数则执行以

*下步骤*/

void(*a)();

a=myself;

signal(SIGINT,a);

flag=2;

}

main()

{

while

(1){

sleep(2000);/*等待中断信号*/

if(flag==1){

printf("skipsystemcallsleep\n");

exit(0);

}

if(flag==2){

printf("skipsystemcallsleep\n");

printf("waitingfornextsignal\n");

}

}

}

2.11.kill()向一个或一组进程发送一个信号

功能:

向一个或一组进程发送一个信号.

语法:

#include

#include

intkill(pid,sig);

pid_tpid;

intsig;

说明:

本系统调用向一个或一组进程发送一个信号,该信号由参数sig指

定,为系统给出的信号表中的一个.若为0(空信号)则检查错误但

实际上并没有发送信号,用于检查pid的有效性.

pid指定将要被发送信号的进程或进程组.pid若大于0,则信号将

被发送到进程号等于pid的进程;若pid等于0则信号将被发送到所

有的与发送信号进程同在一个进程组的进程(系统的特殊进程除

外);若pid小于-1,则信号将被发送到所有进程组号与pid绝对值

相同的进程;若pid等于-1,则信号将被发送到所有的进程(特殊系

统进程除外).

信号要发送到指定的进程,首先调用进程必须有对该进程发送信

号的权限.若调用进程有合适的优先级则具备有权限.若调用进程

的实际或有效的UID等于接收信号的进程的实际UID或用setuid()

系统调用设置的UID,或sig等于SIGCONT同时收发双方进程的会话

号相同,则调用进程也有发送信号的权限.

若进程有发送信号到pid指定的任何一个进程的权限则调用成功,

否则调用失败,没有信号发出.

返回值:

调用成功则返回0,否则返回-1.

例子:

假设前一个例子进程号为324,现向它发一个SIGINT信号,让它做

信号处理:

kill((pid_t)324,SIGINT);

2.12.alarm()设置一个进程的超时时钟

功能:

设置一个进程的超时时钟.

语法:

#include

unsignedintalarm(sec)

unsignedintsec;

说明:

指示调用进程的超时时钟在指定的时间后向调用进程发送一个

SIGALRM信号.设置超时时钟时时间值不会被放入堆栈中,后一次

设置会把前一次(还未到超时时间)冲掉.

若sec为0,则取消任何以前设置的超时时钟.

fork()会将新进程的超时时钟初始化为0.而当一个进程用exec()

族系统调用新的执行文件时,调用前设置的超时时钟在调用后仍

有效.

返回值:

返回上次设置超时时钟后到调用时还剩余的时间秒数.

例子:

intflag=0;

voidmyself()

{

flag=1;

printf("getsignalSIGALRM\n");

/*若要重新设置SIGALRM信号中断处理函数为本函数则执行

*以下步骤*/

void(*a)();

a=myself;

signal(SIGALRM,a);

flag=2;

}

main()

{

alarm(100);/*100秒后发超时中断信号*/

while

(1){

sleep(2000);/*等待中断信号*/

if(flag==1){

printf("skipsystemcallsleep\n");

exit(0);

}

if(flag==2){

printf("s

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2