c语言多进程多线程编程Word文档格式.docx
- 文档编号:1145127
- 上传时间:2023-04-30
- 格式:DOCX
- 页数:102
- 大小:94KB
c语言多进程多线程编程Word文档格式.docx
《c语言多进程多线程编程Word文档格式.docx》由会员分享,可在线阅读,更多相关《c语言多进程多线程编程Word文档格式.docx(102页珍藏版)》请在冰点文库上搜索。
1.4.事务化
比如在一个数据电话查询系统中,将程序设计成一个进程只处理一次查询即可,即完成一个事务.当电话查询开始时,产生这样一个进程对付这次查询;
另一个电话进来时,主控程序又产生一个这样的进程对付,每个进程完成查询任务后消失.这样的编程多简单,只要做一次查询的程序就可以了.
二.常用的多进程编程的系统调用
2.1.fork()创建一个新的进程.
功能:
创建一个新的进程.
语法:
#include<
unistd.h>
sys/types.h>
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)
应用程序
pid_tpid;
fork()
if((pid=fork())==0)
{
//子进程代码
exit(0);
子进程2
子进程1
父进程
}
elseif(pid>
0)
//父进程代码
else
printf("
Error"
);
exit
(1);
2.2.system()子进程执行指定的命令
产生一个新的进程,子进程执行指定的命令.
stdio.h>
stdlib.h>
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()执行一个文件
执行一个文件
语法
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()初始化从/到一个进程的管道
初始化从/到一个进程的管道.
FILE*popen(command,type)
char*command,type;
本系统调用在调用进程和被执行命令间创建一个管道.
参数command做为被执行的命令行.type做为I/O模式,"
r"
为从被
执行命令读,"
w"
为向被执行命令写.返回一个标准流指针,做为管
道描述符,向被执行命令读或写数据(做为被执行命令的STDIN或
STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令
的输出信息或者向命令输入信息.
不成功则返回NULL,成功则返回管道的文件指针.
2.5.pclose()关闭到一个进程的管道
关闭到一个进程的管道.
intpclose(strm)
FILE*strm;
本系统调用用于关闭由popen()打开的管道,并会等待由popen()
激活的命令执行结束后,关闭管道后读取命令返回码.
若关闭的文件描述符不是由popen()打开的,则返回-1.
nowthisprocesswillcallpopensystemcall\n"
FILE*fd;
if((fd=popen("
ps-ef"
))==NULL){
callpopenfailed\n"
return;
else{
charstr[80];
while(fgets(str,80,fd)!
=NULL)
%s\n"
str);
pclose(fd);
2.6.wait()等待一个子进程返回并修改状态
等待一个子进程返回并修改状态
sys/wait.h>
pid_twait(stat_loc)
int*stat_loc;
允许调用进程取得子进程的状态信息.调用进程将会挂起直到其
一个子进程终止.
等待到一个子进程返回时,返回值为该子进程号,否则返回值为
-1.同时stat_loc返回子进程的返回值.
/*父进程*/
if(fork()>
0){
wait((int*)0);
/*父进程等待子进程的返回*/
/*子进程处理过程*/
exit(0);
2.7.waitpid()等待指定进程号的子进程的返回并修改状态
等待指定进程号的子进程的返回并修改状态
pid_twaitpid(pid,stat_loc,options)
int*stat_loc,options;
当pid等于-1,options等于0时,该系统调用等同于wait().否则该
系统调用的行为由参数pid和options决定.
pid指定了一组父进程要求知道其状态的子进程:
-1:
要求知道任何一个子进程的返回状态.
>
0:
要求知道进程号为pid值的子进程的状态.
<
要求知道进程组号为pid的绝对值的子进程的状态.
options参数为以比特方式表示的标志以或运算组成的位图,每个
标志以字节中某个比特置1表示:
WUNTRACED:
报告任何未知而又已停止运行的指定进程号的子进
程的状态.该子进程的状态自停止运行时起就没有被报告过.
WCONTINUED:
报告任何继续运行的指定进程号的子进程的状态,
该子进程的状态自继续运行起就没有被报告过.
WHOHANG:
若调用本系统调用时,指定进程号的子进程的状态目
前并不是立即有效的(即可被立即读取的),调用进程并被暂停执行.
WNOWAIT:
保持将其状态设置在stat_loc的进程在可等待状态.
该进程将等待直到下次被要求其返回状态值.
等待到一个子进程返回时,返回值为该子进程号,否则返回值为1.
同时stat_loc返回子进程的返回值.
intstat_loc;
/*父进程*/
if((pid="
fork())"
waitpid(pid,&
stat_loc,0);
/*父进程等待进程号为pid的子进程的返回*/
/*子进程的处理过程*/
exit
(1);
stat_locis[%d]\n"
stat_loc);
/*字符串"
stat_locis[1]"
将被打印出来*/
2.8.setpgrp()设置进程组号和会话号
设置进程组号和会话号.
pid_tsetpgrp()
若调用进程不是会话首进程.将进程组号和会话号都设置为与它
的进程号相等.并释放调用进程的控制终端.
调用成功后,返回新的进程组号.
/*父进程处理*/
setpgrp();
/*子进程的进程组号已修改成与它的进程号相同*/
2.9.exit()终止进程
终止进程.
voidexit(status)
intstatus;
调用进程被该系统调用终止.引起附加的处理在进程被终止前全
部结束.
无
2.10.signal()信号管理功能
信号管理功能
signal.h>
void(*signal(sig,disp))(int)
intsig;
void(*disp)(int);
void(*sigset(sig,disp))(int)
intsighold(sig)
intsigrelse(sig)
intsigignore(sig)
intsigpause(sig)
这些系统调用提供了应用程序对指定信号的简单的信号处理.
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;
getsignalSIGINT\n"
/*若要重新设置SIGINT信号中断处理函数为本函数则执行以
*下步骤*/
void(*a)();
a=myself;
signal(SIGINT,a);
flag=2;
main()
while
(1){
sleep(2000);
/*等待中断信号*/
if(flag==1){
skipsystemcallsleep\n"
if(flag==2){
waitingfornextsignal\n"
2.11.kill()向一个或一组进程发送一个信号
向一个或一组进程发送一个信号.
intkill(pid,sig);
本系统调用向一个或一组进程发送一个信号,该信号由参数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()设置一个进程的超时时钟
设置一个进程的超时时钟.
unistd.h<
unsignedintalarm(sec)
unsignedintsec;
指示调用进程的超时时钟在指定的时间后向调用进程发送一个
SIGALRM信号.设置超时时钟时时间值不会被放入堆栈中,后一次
设置会把前一次(还未到超时时间)冲掉.
若sec为0,则取消任何以前设置的超时时钟.
fork()会将新进程的超时时钟初始化为0.而当一个进程用exec()
族系统调用新的执行文件时,调用前设置的超时时钟在调用后仍
有效.
返回上次设置超时时钟后到调用时还剩余的时间秒数.
getsignalSIGALRM\n"
/*若要重新设置SIGALRM信号中断处理函数为本函数则执行
*以下步骤*/
signal(SIGALRM,a);
alarm(100);
/*100秒后发超时中断信号*/
s
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 进程 多线程 编程