c语言多进程多线程编程.pdf
C 语言多进程编程 一一.多进程程序的特点多进程程序的特点 进程进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。进程进程是资源管理的最小单位,线程线程是程序执行的最小单位。进程管理着资源(比如 cpu、内存、文件等等),而将线程分配到某个 cpu 上执行。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文切换开销。进程的状态进程的状态 系统为了充分的利用资源,对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态.新建新建 表示进程正在被创建,运行运行 是进程正在运行,阻塞阻塞 是进程正在等待某一个事件发生,就绪就绪 是表示系统正在等待 CPU 来执行命令,完成完成 表示进程已经结束了系统正在回收资源.由于 UNIX 系统是分时多用户系统,CPU 按时间片分配给各个用户使用,而在实质上应该说 CPU 按时间片分配给各个进程使用,每个进程都有自己的运行环境以使得在 CPU 做进程切换时不会忘记该进程已计算了一半的半成品”.以 DOS 的概念来说,进程的切换都进程的切换都 是一次是一次DOSDOS 中断中断 处理过程处理过程,包括三个层次包括三个层次:1)用户数据的保存:包括正文段(TEXT),数据段(DATA,BSS),栈段(STACK),共享内存段(SHARED MEMORY)的保存.2)寄存器数据的保存:包括 PC(program counter,指向下一条要执行的指 令的地址),PSW(processor status word,处理机状态字),SP(stack pointer,栈指针),PCBP(pointer of process control block,进程控制块指针),FP(frame pointer,指向栈中一个函数的 local变量的首地址),AP(augument pointer,指向栈中函数调用的实参位置),ISP(interrupt stack pointer,中断栈指针),以及其他的通用寄存器等.3)系统层次的保存:包括 proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到 CPU 时间片时能正常运行。既然系统已经处理好所有这些中断处理的过程,我们做程序还有什么要担心 的呢?我们尽可以使用系统提供的多进程的特点,让几个程序精诚合作,简单而又高效地把结果给它搞出来。另外,UNIX 系统本身也是用 C 语言写的多进程程序,多进程编程是 UNIX 的特点,当我们熟悉了多进程?将会对 UNIX 系统机制有一个较深的认识.首先我介绍一下多进程程序的首先我介绍一下多进程程序的一些突出的特点一些突出的特点:1.1 并行化并行化 一件复杂的事件是可以分解成若干个简单事件来解决的,这在程序员的大脑中早就形成了这种概念,首先将问题分解成一个个小问题,将小问题再细分,最后在一个合适的规模上做成一个函数.在软件工程中也是这么说的.如果我们以图的方式来思考,一些小问题的计算是可以互不干扰的,可以同时处理,而在关键点则需要统一在一个地方来处理,这样程序的运行就是并行的,至少从人的时间观念上来说是这样的.而每个小问题的计算又是较简单的.1.2 简单有序简单有序 这样的程序对程序员来说不亚于管理一班人,程序员为每个进程设计好相应的功能,并通过一定的通讯机制将它们有机地结合在一起,对每个进程的设计是简单的,只在总控部分小心应付(其实也是蛮简单的),就可完成整个程序的施工.1.3.互不干扰互不干扰 这个特点是操作系统的特点,各个进程是独立的,不会串位.1.4.事务化事务化 比如在一个数据电话查询系统中,将程序设计成一个进程只处理一次查询即可,即完成一个事务.当电话查询开始时,产生这样一个进程对付这次查询;另一个电话进来时,主控程序又产生一个这样的进程对付,每个进程完成查询任务后消失.这样的编程多简单,只要做一次查询的程序就可以了.二二.常用的多进程编程的系统调用常用的多进程编程的系统调用 2.1.fork()创建一个新的进程创建一个新的进程.功能:创建一个新的进程.语法:#include#include#include#include pid_t fork();pid_t fork();说明:本系统调用产生一个新的进程,叫子进程,是调用进程的一个复制品.调用进程叫父进程,子进程继承了父进程的几乎所有的属性。进程:代码段(程序代码)堆栈段(局部变量、函数返回地址、函数参数)数据段(全局变量、常数等)在 Linux 系统中,系统调用 fork 后,内核为完成系统调用 fork 要进行几步操作:第一步,为新进程在进程表中分配一个表项。系统对一个普通用户可以同时运行的进程数是有限制的,对超级用户没有该限制,但不能超过进程表的最大表项的数目。第二步,给子进程一个唯一的进程标识号(PID)。该进程标识号其实就是该表项在进程表中的索引号。第三步,复制一个父进程的进程表项的副本给子进程。内核初始化子进程的进程表项时,是从父进程处拷贝的。所以子进程拥有与父进程一样的 uid、当前目录、当前根、用户文件描述符表等。第四步,把与父进程相连的文件表和索引节点表的引用数加 1。这些文件自动地与该子进程相连。第五步,内核为子进程创建用户级上下文。内核为子进程的代码段分配内存,并复制父进程的区内容,生成的是进程的静态部分。第六步,生成进程的动态部分,然后对父进程返回子进程的 pid,对子进程返回 0。从父进程拷贝的内容主要有:用户标识符,包括实际用户号(real)和有效用户号(effective);环境变量 打开的文件描述符、套接字描述符 信号处理设置 堆栈 目录 进程组标志(process ID)会晤组标志(session ID)正文 子进程特有内容:进程号 父进程号 进程执行时间 未处理的信号被处理为空 不继承异步的输入输出操作 简述简述:fork()fork()调用成功时,分别返回两个整数,对父进程返回 0 的整数,对子进程返回 0,函数执行过程:内核在系统进程表中,创建一个新条目;复制父进程内容(已打开的文件描述符、堆栈、正文等);修改两者的堆栈,给父进程返回子进程号,给子进程返回 0(父进程知道每个子进程的标志号,而子进程可根据需要调用 getppid()来获得父进程的标志号)。例子:pid_t fork(void)pid_t fork(void)#include pid_t pid;if(pid=fork()=0)/子进程代码 exit(0);else if(pid0)/父进程代码 exit(0);else printf(Error);exit(1);2.2.system()子进程执行指定的命令子进程执行指定的命令 功能:产生一个新的进程,子进程执行指定的命令.语法:#include#include#include#include int system(string)int system(string)char*string;char*string;说明:本调用将参数 string 传递给一个命令解释器(一般为 sh)执行,即 string 被解释为一条命令,由 sh 执行该命令.若参数 string 为一个空指针则为检查命令解释器是否存在.该命令可以同命令行命令相同形式,但由于命令做为一个参数放在系统调用中,应注意编译时对特殊意义字符的处理.命令的查找是按 PATH 环境变量的定义的.命令所生成的后果一般不会对父进程造成影响.返回值:当参数为空指针时,只有当命令解释器有效时返回值为非零.若参数不为空指针,返回值为该命令的返回状态(同 waitpid()的返回值.命令无效或语法错误则返回非零值,所执行的命令被终止.其他情况则返回-1.例子:char command81;int i;for(i=1;i8;i+)sprintf(command,ps t tty%02i,i);system(command);应用程序 fork()父进程 子进程 1 子进程 2 2.3.exec()执行一个文件执行一个文件 功能:执行一个文件 语法#include int execve(const char*path,char*const*argv,char*const*envp);int execve(const char*path,char*const*argv,char*const*envp);int execl(const char*path,char*arg,.);int execl(const char*path,char*arg,.);int execp(conint execp(const char*file,char*arg,.);st char*file,char*arg,.);int execle(const char*path,const char*argv,.,char*const*envp);int execle(const char*path,const char*argv,.,char*const*envp);int execv(const char*path,char*const*arg);int execv(const char*path,char*const*arg);int execvp(const char*file,char*const*arg);int execvp(const char*file,char*const*arg);说明:exec 函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件 其中只有 execve 是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。与一般情况不同,exec 函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程 ID 等一些表面上的信息仍保持原样,颇有些神似三十六计中的金蝉脱壳。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。fork()和 exec()这两个函数,前者用于并行执行,父、子进程执行相同正文中的不同部分;后者用于调用其他进程,父、子进程执行不同的正文,调用前,一般应为子进程创造一个干净的环境。fork()以后,父、子进程共享代码段,并只重新创建数据有改变的页(段页式管理)exec()以后,建立新的代码段,用被调用程序的内容填充。前者的子进程执行后续的公共代码,后者的子进程不执行后续的公共代码。父、子进程以及各个子进程执行的顺序不定。.例子:printf(now this process will be ps commandn);execl(/bin/ps,ps,-ef,NULL);2.4.popen()初始化从初始化从/到一个进程的管道到一个进程的管道 功能:初始化从/到一个进程的管道.语法:#include#include FILE*popen(command,type)FILE*popen(command,type)char*command,type;char*command,type;说明:本系统调用在调用进程和被执行命令间创建一个管道.参数 command 做为被执行的命令行.type 做为 I/O 模式,r为从被 执行命令读,w为向被执行命令写.返回一个标准流指针,做为管 道描述符,向被执行命令读或写数据(做为被执行命令的 STDIN 或 STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令 的输出信息或者向命令输入信息.返回值:不成功则返回 NULL,成功则返回管道的文件指针.2.5.pclose()关闭到一个进程的管道关闭到一个进程的管道 功能:关闭到一个进程的管道.语法:#include#include int pclose(strm)int pclose(strm)FILE*strm;FILE*strm;说明:本系统调用用于关闭由 popen()打开的管道,并会等待由 popen()激活的命令执行结束后,关闭管道后读取命令返回码.返回值:若关闭的文件描述符不是由 popen()打开的,则返回-1.例子:printf(now this process will call popen system calln);FILE*fd;if(fd=popen(ps-ef,r)=NULL)printf(call popen failedn);return;else char str80;while(fgets(str,80,fd)!=NULL)printf(%sn,str);pclose(fd);2.6.wait()等待一个子进程返回并修改状态等待一个子进程返回并修改状态 功能:等待一个子进程返回并修改状态 语法:#include#include#include#include pid_t wait(stat_loc)pid_t wait(stat_loc)int*stat_loc;int*stat_loc;说明:允许调用进程取得子进程的状态信息.调用进程将会挂起直到其 一个子进程终止.返回值:等待到一个子进程返回时,返回值为该子进程号,否则返回值为 -1.同时 stat_loc 返回子进程的返回值.例子:/*父进程*/if(fork()0)wait(int*)0);/*父进程等待子进程的返回*/else /*子进程处理过程*/exit(0);2.7.waitpid()等待指定进程号的子进程的返回并修改状等待指定进程号的子进程的返回并修改状态态 功能:等待指定进程号的子进程的返回并修改状态 语法:#include#include#include#include pid_t waitpid(pid,stat_loc,optionspid_t waitpid(pid,stat_loc,options)pid_t pid;pid_t pid;int*stat_loc,options;int*stat_loc,options;说明:当 pid 等于-1,options 等于 0 时,该系统调用等同于 wait().否则该 系统调用的行为由参数 pid 和 options 决定.pid 指定了一组父进程要求知道其状态的子进程:-1:要求知道任何一个子进程的返回状态.0:要求知道进程号为 pid 值的子进程的状态.0)waitpid(pid,&stat_loc,0);/*父进程等待进程号为 pid 的子进程的返回*/else /*子进程的处理过程*/exit(1);/*父进程*/printf(stat_loc is%dn,stat_loc);/*字符串stat_loc is 1将被打印出来*/2.8.setpgrp()设置进程组号和会话号设置进程组号和会话号 功能:设置进程组号和会话号.语法:#include#include pid_t setpgrp()pid_t setpgrp()说明:若调用进程不是会话首进程.将进程组号和会话号都设置为与它 的进程号相等.并释放调用进程的控制终端.返回值:调用成功后,返回新的进程组号.例子:/*父进程处理*/if(fork()0)/*父进程处理*/else setpgrp();/*子进程的进程组号已修改成与它的进程号相同*/exit(0);2.9.exit()终止进程终止进程 功能:终止进程.语法:#include#include void exit(status)void exit(status)int status;int status;说明:调用进程被该系统调用终止.引起附加的处理在进程被终止前全 部结束.返回值:无 2.10.signal()信号管理功能信号管理功能 功能:信号管理功能 语法:#include#include void(*signal(sivoid(*signal(sig,disp)(int)g,disp)(int)int sig;int sig;void(*disp)(int);void(*disp)(int);void(*sigset(sig,disp)(int)void(*sigset(sig,disp)(int)int sig;int sig;void(*disp)(int);void(*disp)(int);int sighold(sig)int sighold(sig)int sig;int sig;int sigrelse(sig)int sigrelse(sig)int sig;int sig;int sigignore(sig)int sigignore(sig)int sig;int sig;int sigpause(sig)int sigpause(sig)int sig;int 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 信号为例:int flag=0;void myself()flag=1;printf(get signal SIGINTn);/*若要重新设置 SIGINT 信号中断处理函数为本函数则执行以 *下步骤*/void(*a)();a=myself;signal(SIGINT,a);flag=2;main()while(1)sleep(2000);/*等待中断信号*/if(flag=1)printf(skip system call sleepn);exit(0);if(flag=2)printf(skip system call sleepn);printf(waiting for next signaln);2.11.kill()向一个或一组进程发送一个信号向一个或一组进程发送一个信号 功能:向一个或一组进程发送一个信号.语法:#include#include#include#include int kill(pid,sig);int kill(pid,sig);pid_t pid;pid_t pid;int sig;int 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()设置一个进程的超时时钟设置一个进程的超时时钟 功能:设置一个进程的超时时钟.语法:#include unistd.h#include unistd.h unsigned int alarm(sec)unsigned int alarm(sec)unsigned int sec;unsigned int sec;说明:指示调用进程的超时时钟在指定的时间后向调用进程发送一个 SIGALRM 信号.设置超时时钟时时间值不会被放入堆栈中,后一次 设置会把前一次(还未到超时时间)冲掉.若 sec 为 0,则取消任何以前设置的超时时钟.fork()会将新进程的超时时钟初始化为 0.而当一个进程用 exec()族系统调用新的执行文件时,调用前设置的超时时钟在调用后仍 有效.返回值:返回上次设置超时时钟后到调用时还剩余的时间秒数.例子:int flag=0;void myself()flag=1;printf(get signal SIGALRMn);/*若要重新设置 SIGALRM 信号中断处理函数为本函数则执行 *以下步骤*/void(*a)();a=myself;signal(SIGALRM,a);flag=2;main()alarm(100);/*100 秒后发超时中断信号*/while(1)sleep(2000);/*等待中断信号*/if(flag=1)printf(skip system call sleepn);exit(0);if(flag=2)printf(skip system call sleepn);printf(waiting for next signaln);2.13.msgsnd()发送消息到指定的消息队列中发送消息到指定的消息队列中 功能:发送消息到指定的消息队列中.语法:#include#include#include#include#include#include int msgsnd(msqid,msgp,msgsz,msgflg)int msgsnd(msqid,msgp,msgsz,msgflg)int msqid;int msqid;void*msgp;void*msgp;size_t msgsz;size_t msgsz;int msgflg;int msgflg;说明:发送一个消息到由 msqid 指定消息队列标识号的消息队列.参数 msgp 指向一个用户定义的缓冲区,并且缓冲区的第一个域应 为长整型,指定消息类型,其他数据放在缓冲区的消息中其他正文 区内.下面是消息元素定义:long mtype;char mtext;mtype 是一个整数,用于接收进程选择消息类型.mtext 是一个长度为 msgsz 字节的任何正文,参数 msgsz 可从 0 到系 统允许的最大值间变化.msgflg 指定操作行为:.若(msgflg&IPC_NOWAIT)是真的,消息并不是被立即发送而调用 进程会立即返回.若(msgflg&IPC_NOWAIT)不是真的,则调用进程会被挂起直到下 面情况之一发生:*消息被发送出去.*消息队列标志被系统删除.系统调用返回-1.*调用进程接收到一个未被忽略的中断信号,调用进程继续 执行或被终止.调用成功后,对应指定的消息队列的相关结构做如下动作:.消息数(msg_qnum)加 1.消息队列最近发送进程号(msg_lspid)改为调用进程号.消息队列发送时间(msg_stime)改为当前系统时间.以上信息可用命令 ipcs-a 看到.返回值:成功则返回 0,否则返回-1.2.14.msgrcv()从消息队列中取得指定类型从消息队列中取得指定类型的消息的消息 功能:从消息队列中取得指定类型的消息.语法语法:#include#include#include#include#include#include int msgrcv(msqid,msgp,msgsz,msgtyp,msgflg)int msgrcv(msqid,msgp,msgsz,msgtyp,msgflg)int msqid;int msqid;void*msgp;void*msgp;int msgsz;int msgsz;long msgtyp;long msgtyp;int msgflg;int msgflg;说明:本系统调用从由 msqid 指定的消息队列中读取一个由 msgtyp 指定 类型的消息到由 msgp 指向的缓冲区中,同样的,该缓冲区的结构如 前所述,包括消息类型和消息正文.msgsz 为可接收的消息正文的 字节数.若接收到的消息正文的长度大于 msgsz,则会被截短到 msgsz 字节为止(当消息标志 msgflg&MSG_NOERROR 为真时),截掉的 部份将被丢失,而且不通知消息发送进程.msgtyp 指定消息类型:.为 0 则接收消息队列中第一个消息.大于 0 则接收消息队列中第一个类型为 msgtyp 的消息.小于 0 则接收消息队列中第一个类型值不小于 msgtyp 绝对值且 类型值又最小的消息.msgflg 指定操作行为:.若(msgflg&IPC_NOWAIT)是真的,调用进程会立即返回,若没有 接收到消息则返回值为-1,errno 设置为 ENOMSG.若(msgflg&IPC_NOWAIT)不是真的,则调用进程会被挂起直到下 面情况之一发生:*队列中的消息的类型是有效的.*消息队列标志被系统删除.系统调用返回-1.*调用进程接收到一个未被忽略的中断信号,调用进程继续 执行或被终止.调用成功后,对应指定的消息队列的相关结构做如下动作:.消息数(msg_qnum)减 1.消息队列最近接收进程号(msg_lrpid)改为调用进程号.消息队列接收时间(msg_rtime)改为当前系统时间.以上信息可用命令 ipcs-a 看到.返回值:调用成功则返回值等于接收到实际消息正文的字节数.不成功则返回-1.2.15.msgctl()消息控制操作消息控制操作 功能:消息控制操作 语法:#include#include#include#include#include#include int msgcint msgctl(msqid,cmd,buf)tl(msqid,cmd,buf)int msqid,cmd;int msqid,cmd;struct msqid_ds*buf;struct msqid_ds*buf;说明:本系统调用提供一系列消息控制操作,操作动作由 cmd 定义,以下 cmd 定义值表明了各操作动作的定义.IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值放入由 buf 指向的结构中.IPC_SET:将 msqid 相关的数据结构中的下列元素设置为由 buf 指 向的结构中的对应值.msg_perm.uid msg_perm.gid msg_perm.mode msg_qbytes 本命令只能由有效 UID 等于 msg_perm.cuid 或 msg_perm.uid 的 进程或有效 UID 有合适权限的进程操作.只有具有合适权限的 用户才能增加 msg_qbytes 的值.IPC_RMID:删除由 msqid 指示的消息队列.将它从系统中删除并 破坏相关的数据结构.本命令只能由有效 UID 等于 msg_perm.cuid 或 msg_perm.uid 的 进程或有效 UID 有合适权限的进程操作.返回值:调用成功则返回值为 0,否则为-1.2.16.msgget()取得一个消息队列取得一个消息队列 功能:取得一个消息队列.语法:#include#include#include#include#include#include int msgget(key,msgflg)int msgget(key,msgflg)key_t key;key_t key;int msgflg;int msgflg;说明:本系统调用返回与参数 key 相关的消息队列的标识符.若以下事实成立,则与消息队列相关的标识符和数据结构将被创 建出来:.若参数 key 等于 IPC_PRIVATE.若参数 key 没有一个已存在的消息队列标识符与之相关,同时值 (msgflg&IPC_CREAT)为真.创建消息队列时,与新的消息队列标识符相关的数据结构将被初 始化为如下:.msg_perm.cuid 和 msg_perm.uid 设置为调用进程的有效 UID.msg_perm.cgid 和 msg_perm.gid 设置为调用进程的有效 GID.msg_perm.mode 访问权限比特位设置为 msgflg 访问权限比特位.msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime 设置为 0.msg_ctime 设置为当前系统时间.msg_qbytes 设置为系统允许的最大值.返回值:调用成功则返回一非 0 值,称为消息队列标识符;否则返回值为-1.例子:本例将包括上述所有消息队列操作的系统调用:#define RKEY 0 x9001L/*读消息队列的 KEY 值*/#define WKEY 0 x9002L/*写消息队列的 KEY 值*/#define MSGFLG 0666/*消息队列访问权限*/#define IPC_WAIT 0/*等待方式在 include 文件中未定义*/int rmsqid;/*读消息队列标识符*/int wmsqid;/*写消息队列标识符*/struct msgbuf long mtype;char mtext200;buf;/*若读消息队列已存在就取得标识符,否则则创建并取得标识符*/if(rmsqid=msgget(RKEY,MSGFLG|IPC_CREAT)0)printf(get read message queue failedn);exit(1);/*若写消息队列已存在则失败,若不存在则创建并取得标识符*/if(wmsqid=msgget(WKEY,MSGFLG|IPC_CREAT|IPC_TRUNC)0)printf(get%ld type message from queue:%sn,buf.mtype,buf.mtext);else printf(get message failedn);exit(3);buf.mtype=3L;if(msgsnd(wmsqid,&buf,sizeof(struct msgbuf)-sizeof(long),IPC_NOWAIT)0)printf(send message OKn);else printf(send message failedn);exit(4);msgctl(wmsqid,IPC_RMID,(struct msqid*)NULL);2.17.shmat()联接共享内存的操作联接共享内存的操作 功能:联接共享内存的操作.语法:#include#include#include#include#include#include void*shmat(shmid,shmaddr,shmflg)void*shmat(shmid,shmaddr,shmflg)int shmid;int shmid;void*shmaddr;void*shmaddr;int shmid;int shmid;说明:将由 shmid 指示的共享内存联接到调用进程的数据段中.被联接的 段放在地址,该地址由以下准则指定:.若 shmaddr 等于(void*)0,则段联接到由系统选择的第一个可 用的地址上.若 shmaddr 不等于(void*)0 同时(shmflg&SHM_RND)值为真,则 段联接到由(shmaddr-(shmaddr%SHMLBA)给出的地址上.若 shmaddr 不等于(void*)0 同时(shmflg&SHM_RND)值为假,则 段联接到由 shmaddr 指定的地址上.若(shmflg&sSHM_RDONLY)为真并且调用进程有读允许,则被联接 的段为只读;否则,若值不为真且调用进程有读写权限,则被联接 的段为可读写的.返回值:若调用成功则返回被联接的共享内存段在数据段上的启始地址.否则返回值为-1.2.18.shmdt()断开共享内存联接的操作断开共享内存联接的操作 功能:断开共享内存联接的操作.语法:#include#include#include#include#include#include void*shmdt(shmaddr)void*shmdt(shmaddr)void*shmaddr;void*shmaddr;说明:本系统调用将由 shmaddr 指定的共享内存段从调用进程的数据段 脱离出去.返回值:若调用成功则返回值为 0,否则返回值为-1.2.19.shmget()取得共享内存段取得共享内存段 功能:取得共享内存段 语法:#include#include#include#include#include#include int shmget(key,size,shmflg)int shmget(key,size,shmflg)key_t key;key_t key;int size,shmflg;int size,shmflg;说明:本系统调用返回 key 相关的共享内存标识符.共享内存标识符和相关数据结构及至少 size 字节的共享内存段能 正常创建,要求以下事实成立:.参数 key 等于 IPC_PRIVATE.参数 key 没有相关的共享内存标识符,同时(shmflg&IPC_CREAT)值为真.共享内存创建时,新生成的共享内存标识相关的数据结构被初始 化如下:.shm_perm.cuid 和 shm_perm.uid 设置为调用进程的有效 UID.shm_perm.cgid 和 shm_perm.gid 设置为调用进程的有效 GID.shm_perm.mode 访问权限比特位设置为 shmflg 访问权限比特位.shm_lpid,shm_nattch,shm_atime,shm_dtime 设置为 0.shm_ctime 设置为当前系统时间.shm_segsz 设置为 0.返回值:若调用成功则返回一个非 0 值,称为共享内存标识符,否则返回 值为-1.2.20.shmctl()共享内存控制操作共享内存控制操作 功能:共享内存控制操作.语法:#include#include#include#include#include#include int shmctl(shmid,cmd,buf)int shmctl(shmid,cmd,buf)int shmiint shmid,cmd;d,cmd;struct shmid_ds*buf;struct shmid_ds*buf;说明:本系统调用提供一系列共享内存控制操作.操作行为由 cmd 指定.以下