版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章进程间的通信2本章重点进程通信中信号概念及信号处理进程间的管道通信编程进程间的内存共享编程3Linux中进程间通信(IPC)概述**管道、FIFO、信号、消息队列、信号灯、共享内存共享内存进程1进程2管道pipesignal消息队列4Linux中进程间通信概述**管道(Pipe)及有名管道(namedpipe)
管道是一种在进程之间单向流动数据的结构。源进程向管道写数据,而内核会自动将这些数据引导向目标进程。
管道:用于具有亲缘关系的进程间的通信有名管道:除具有管道所具有的功能外,还允许无亲缘关系的进程间的通信5Linux中进程间通信概述**Linux系统信号(signal)
信号主要用于通知进程异步事件的发生。 在Linux中可以识别29种不同的信号,这些信号中的大部分都有了预先定义好的意义。6Linux中进程间通信概述**消息队列消息队列是由内核创建并维护的一个数据结构,它是有标识的。任何具有足够权限的进程都可以向消息队列中放置一个消息,同样,任何具有足够权限的进程都可以从中读取一个消息。不同的进程通过访问相同的消息队列便可实现进程间通信。7Linux中进程间通信概述**共享内存共享内存区是这几种进程间通信方式中最快的一种。它的特点除了速度快外,而且可传递的信息量大。通过将一段内存区映射到一个进程的地址空间来实现。因此,这种进程间通信就不再涉及到内核(即进程不是通过执行任何进入内核的系统调用来传递数据的。内核必须建立允许各个进程共享该内存区的内存映射关系,然后一直管理该内存区)。但同时,也要有效地保证它能同步、有序且没有死锁。8Linux中进程间通信概述**信号量信号量并不是一种IPC机制,它是用于提供不同进程间或一给定进程的不同线程间同步的一种手段。97.1.1信号及其使用
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。信号主要用于通知进程异步事件的发生。在Linux中可以识别29种不同的信号,这些信号中的大部分都有了预先定义好的意义。107.1.1信号及其使用信号事件的发生有两个来源:硬件来源,如按下了键盘Delete键或者鼠标单击,通常产生中断信号(SIGINT)或者其它硬件故障。软件来源,如使用系统调用或者是命令发出信号。最常用发送信号的系统函数是kill、raise、alarm、setitimer、sigation和sigqueue函数,软件来源还包括一些非法运算等操作。117.1.1信号及其使用**一旦有信号产生,用户进程对信号的响应有3种方式:执行默认操作。Linux对每种信号都规定了默认操作。捕捉信号。定义信号处理函数,当信号发生时,执行相应的处理函数。忽略信号。不希望接收到的信号对进程的执行产生影响,而让进程继续进行时,可以忽略该信号,即不对信号进程任何处理。12信号与信号处理**进程A信号产生进程B信号处理(信号接收)13常见信号的含义及其默认操作*14信号处理常用函数**发送信号函数Kill(pid_tpid,sig);给指定的进程发送信号。raise(intsig);给当前进程发送信号。Alarm(unsignedintseconds);给当前进程发送时钟报警信号。pause(void);等待信号到来处理信号函数-信号捕捉Signal(sig,voidsigfunc(int));信号处理15信号处理常见函数信号发送函数(1)***
#include<sys/types.h>
#include<signal.h>
intkill(pid_tpid,intsig);
说明:
kill()可以用来送参数sig指定的信号给参数pid指定的进程。
参数pid有几种情况:
pid>0将信号传给进程识别码为pid的进程。
pid=0将信号传给和目前进程相同进程组的所有进程
pid=-1将信号广播传送给系统内所有的进程
返回值: 成功:返回0,错误:返回-1。
16信号处理常见函数//范例**#include<unistd.h>#include<signal.h>#include<sys/types.h>#include<sys/wait.h>intmain(){ pid_tpid; intstatus; if(!(pid=fork())){ printf(“HiIamchildprocess!\n”); sleep(10); return; } else{ printf(“sendsignaltochildprocess(%d)\n”,pid); sleep(1); kill(pid,SIGKILL); wait(&status); }}
17信号处理常见
函数信号发送函数(2)***函数格式
#include<unistd.h>
定义函数unsignedintalarm(unsignedintseconds);函数说明
alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。参数 若seconds=0,则之前设置的闹钟会被取消,并将剩下的时间返回。返回值 返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0。18//范例2***#include<unistd.h>#include<signal.h>voidhandler(){ printf(“hello\n”);}intmain(){ inti; signal(SIGALRM,handler); alarm(5); for(i=1;i<7;i++){ printf(“sleep%d...\n”,i); sleep(1); }}
19信号处理常见函数信号捕获函数(1)***
#include<signal.h>
void(*signal(intsignum,void(*handler)(int)))(int);
voidsignal(int信号名,信号处理函数名)信号处理函数void函数名(int信号名)函数说明
signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。
20//范例***
#include<unistd.h>#include<signal.h>voidhandler(){ printf(“hello\n”);}intmain(){ inti; signal(SIGALRM,handler); alarm(5); for(i=1;i<7;i++){ printf(“sleep%d...\n”,i); sleep(1); }}21#include<signal.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>voidoutDate(intn);voidexitProc(intn);intexitSig=0;intmain(){
(void)signal(SIGALRM,outDate); (void)signal(SIGINT,exitProc); while(1){ alarm(1); /*时钟报警函数*/ pause(); if(exitSig) exit(0); }}/*************
SIGALRM信号处理函数*************/voidoutDate(intn){ system("date&");/*执行外部命令 */}/*************
SIGINT信号处理函数*************/voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1;}例:信号的捕捉***227.2.1信号操作的相关函数***237.2.1信号操作的相关函数24信号操作小结信号信号的发送信号的捕捉及处理信号的忽略、阻塞25信号操作小结信号26信号操作小结信号的发送Kill(pid_tpid,sig);给指定的进程发送信号。raise(intsig);给当前进程发送信号。Alarm(unsignedintseconds);给当前进程发送时钟报警信号。pause(void);等待信号到来27信号操作小结信号的捕捉及处理Signal(sig,voidsigfunc(int));信号处理28信号的忽略与阻塞信号的忽略Signal()选项:29信号的忽略与阻塞信号的阻塞有时既不希望进程在接收到信号时立刻中断进程的执行,也不希望此信号完全被忽略掉,而是延迟一段时间再去调用信号处理函数,这个时候就需要信号阻塞来完成。步骤清空待处理的信号集追加待处理信号到信号集设置/解除信号掩码(设置屏蔽)307.2.1信号操作的相关函数§317.2.1信号操作的相关函数327.2.1信号操作的相关函数33信号操作小结信号的忽略、阻塞*信号的忽略Signal(sig,voidsigfunc(int));选项(第2个参数)SIG_IGN:忽略指定的信号SIG_DFL:信号处理方式重新设为内核预设方式阻塞步骤清空待处理的信号集:sigempty()追加待处理信号到信号集:sigaddset()设置/解除信号掩码(设置屏蔽):sigprocmask()347.2管道**无名管道(PIPE)有名管道(FIFO)进程x进程y管道pipe357.2管道**在Linux系统中,管道用于两个进程间的通信,这两个进程要有同源性,即它们必须是最终由同一个进程所生成的进程。管道通信采用的是半双工方式,即同一时间只允许单方向传输数据。进程x进程y管道pipe367.2管道**在Linux中,管道是一种特殊的文件,对一个进程来说,管道的写入和读取与一个普通文件没有区别。管道是Linux支持的最初UnixIPC形式之一,具有以下特点:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。37管道***387.2.1无名管道操作无名管道操作时,建立管道用pipe函数建立管道后Linux系统会同时为该进程建立2个文件描述符pipe_fd[0]和pipe_fd[1]。pipe_fd[0]用来从管道读取数据pipe_fd[1]用来把数据写入管道39无名管道操作**40无名管道操作步骤**创建管道创建进程读/写写进程关闭管道的读端读进程关闭管道的写端无名管道操作读管道进程的有关规则如果进程读一个写端关闭的管道read()返回0,表示结束。如果进程读一个写端仍打开的空管道该进程休眠,直到管道中有新的输入。如果进程试图从管道中读多于现有的字节返回当前的所有内容,read()返回实际读取的字节。写端读端fd[0]fd[1]无名管道操作写管道进程的有关规则如果进程写一个读端关闭的管道写操作失败,将一个SIGPIPE信号发送给写进程。缺省操作为终止进程。如果进程写入管道的字节数少于管道能保存的数write()保证是原子操作,即写进程将完成它的系统调用,不会被另一个进程抢占。写端读端fd[0]fd[1]43管道的局限性*
管道的主要局限性正体现在它的特点上:只支持单向数据流;只能用于具有亲缘关系的进程之间;没有名字;管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;44范例***/*父进程借管道将字符串“hello!\n”传给子进程并显示*/#include<unistd.h>intmain(){ intfiledes[2]; charbuffer[80]; pipe(filedes); if(fork()>0){ /*父进程*/ chars[]=“hello!\n”; write(filedes[1],s,sizeof(s)); } else{ /*子进程*/ read(filedes[0],buffer,80); printf(“%s”,buffer); } exit(0);}457.2.1无名管道操作例7.5***:设计一个程序,要求创建一个管道,父进程往管道中写入字符串,子进程从管道中读取前输出字符串。源程序代码:487.2.1无名管道操作**497.2.3命名管道*若要在两个不相关的进程之间用管道通信,需要用到命名管道FIFO。命名管道FIFO是通过Linux系统中的文件进行通信。命名管道的创建一般用mkfifo函数,创建成功后,就使用open、read、write等函数传输数据。有名管道FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。FIFO严格遵循先进先出(firstinfirstout),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。50有名管道的操作步骤**创建(仅创建一次)~mkfifo()打开(以阻塞形式)~open()省略~阻塞O_NONBLOCK~非阻塞读/写~read()/write()关闭~close()517.2.3命名管道**mkfifo函数说明说明:如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等。52PGO所有者用户组其他用户组RWX读写执行
0000101001101110:无任何权限2:只写4:只读6:可读可写7:可读可写可执行文件权限说明*文件权限由3位8进制数表示,分别代表:掩码:访问权限位的屏蔽字与文件访问权限mode&~掩码如,002表示屏蔽W假设:mode=(111)2掩码:010则访问权限:111^101 101 RWX53有名管道读写规则从FIFO中读取数据如果有进程写打开FIFO,且当前FIFO内没有数据对于设置了阻塞标志的读操作来说,一直阻塞;对于没有设置了阻塞标志的读操作来说,则返回-1,当前errno值为EAGAIN,以提醒以后再试。阻塞原因当前FIFO内有数据,但有其他进程在读数据该FIFO当前FIFO内没有数据没有进程写打开54有名管道读写规则向FIFO中写入数据当要写入的数据的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到空闲缓冲区缓冲区足以写入时,才开始写操作当写入的数据量大于PIPE_BUF时,Linux将不再保证原子性。FIFO缓冲区一有空闲区域,写进程试图写入数据,写操作在写完所有请求写的数据后返回/usr/include/linux/limits.h中(RedHAT)# define
PIPE_BUF
409655范例**#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>intmain(){ charbuffer[80]; intfd; unlink(“myfifo”);/*FIFO文件必须是不存在*/ if((mkfifo("myfifo",0666)<0)&&(errno!=EEXIST))//读写权限
printf("cannotcreatefifoserver\n"); if(fork()>0){ chars[]="hello!\n"; fd=open("myfifo",O_WRONLY); write(fd,s,sizeof(s)); close(fd); } else{ fd=open("myfifo",O_RDONLY); read(fd,buffer,80); printf("%s",buffer); close(fd); } exit(0);}//fifo——execl应用56//fiforead.c**#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>intmain(){ charbuffer[80]; intfd; fd=open("myfifo",O_RDONLY); read(fd,buffer,80); printf("%s\n",buffer); close(fd); exit(0);}57//fifowrite.c**#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>intmain(){ charbuffer[80]; intfd; chars[]="hello!\n"; fd=open("myfifo",O_WRONLY); write(fd,s,sizeof(s)); close(fd); exit(0);}58//利用execl函数调用有名管道读写程序***#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>intmain(){ unlink("myfifo"); if((mkfifo("myfifo",0666)<0)&&(errno!=EEXIST))//读写权限
printf("cannotcreatefifoserver\n"); printf("errono:%d\n",errno"); } if(fork()==0) if(execl("fifowrite",NULL)<0) perror("Erronexecl"); if(fork()==0) if(execl("fiforead",NULL)<0) perror("Erronexecl"); exit(0); return(0);}597.2.3命名管道例7.7***:设计两个程序,要求用命名管道FIFO,实现简单的聊天功能。60#include<stdio.h>#include<fcntl.h>#include<string.h>#include<stdlib.h>#include<sys/select.h>#include<sys/types.h>#include<sys/stat.h>#include<errno.h>intmain(){ inti,rfd,wfd,len=0,fd_in; charstr[32]; intflag,stdinflag; fd_setwrite_fd,read_fd; structtimevalnet_timer; umask(0111); if(mkfifo(“/home/2009/yjfifo1“,0666)<0) /*mkfifo函数创建命名管道*/ perror("mkfifo"); if(mkfifo("/home/2009/yjfifo2",0666)<0) /*mkfifo函数创建命名管道*/ perror("mkfifo"); rfd=open("/home/2009/yjfifo1",O_RDONLY); /*以只读方式打开管道文件*/ wfd=open("/home/2009/yjfifo2",O_WRONLY); /*以写方式打开管道文件*/ if(rfd<=0||wfd<=0) return0; printf("ThisisLiSi!\n"); FD_ZERO(&read_fd); /*清除一个文件描述符集*/
实例***61while(1) {
FD_SET(rfd,&read_fd);/*将文件描述符rfd加入文件描述符集read_fd*/ FD_SET(fileno(stdin),&read_fd); net_timer.tv_sec=5; net_timer.tv_usec=0; memset(str,0,sizeof(str));/*memset函数初始化清空*/ if(i=select(rfd+1,&read_fd,NULL,NULL,&net_timer)<=0) continue; if( FD_ISSET(rfd,&read_fd)) { read(rfd,str,sizeof(str));/*读取管道,将管道内容存入str变量*/ printf("\n"); printf("ZhangSan:%s\n",str); /*打印输出str变量内容*/ } if( FD_ISSET(fileno(stdin),&read_fd)) { printf("\n"); fgets(str,sizeof(str),stdin); len=write(wfd,str,strlen(str)); /*写入管道*/ } } close(rfd); close(wfd);}62I/O处理的多工机制
select()函数*监视多个文件/设备等待读写(阻塞)异常情况I/O(0)输出设备输入设备文件读/写输入设备输入设备输出设备输出设备63Selec()函数*#include<sys/select.h>#include<sys/time.h>intselect(intmaxfd,/*指定测试的描述符最大值*/
fd_set*readfds,/*被监视的读文件描述符集*/ fd_set*writefds,/*被监视的写文件描述符集*/ fd_set*exceptfds,/*被监视的异常处理文件描述符集*/ conststructtimeval*timeout);/*等待时间*/
64返回值: 成功:文件描述符状态已改变的个数 如果为0:timeout(超时) 失败:-1,错误代码errno功能用来监视多个文件描述符(filedescrīptor)的状态(可读、可写或异常)变化的。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。
Selec()函数*65timeval的结构定义如下:structtimeval{ longtv_sec; //表示几秒
longtv_usec;//表示几微妙}timeout取不同的值,该调用就表现不同的性质:1.timeout为0,调用立即返回;2.timeout为NULL,select()调用就阻塞,直到知道有文件描述符就绪;3.timeout为正整数,就是一般的定时器。
66文件描述符集的处理(宏)*FD_ZERO(fd_set*fdset):清除文件描述符集fdset中的所有位(既把所有位都设置为0)FD_SET(intfd,fd_set*fdset):设置文件描述符集fdset中对应于文件描述符fd的位(设置为1)FD_CLR(intfd,fd_set*fdset):清除文件描述符集fdset中对应于文件描述符fd的位(设置为0)
FD_ISSET(intfd,fdset*fdset):检测文件描述符集fdset中对应于文件描述符fd的位是否被设置
Fd_set:一个位图类型的数据集67文件描述符集描述符集通常用整数数组中的位域表示,数组元素的每一位对应一个文件描述符。例如,一个整数占32位,那么整数数组的第一个元素代表文件描述符0到31,数组的第二个元素代表文件描述符32到63,以此类推。68文件描述符集文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。69文件描述符集#include<sys/select.h>#include<sys/time.h>fd_setreadset;FD_ZERO(&readset);FD_SET(5,&readset);FD_SET(33,&readset);FD_CLR(5,&readset);70范例:检测有键盘输入(标准输入文件描述符:0)*#include<stdio.h>#include<unistd.h>#include<sys/select.h>#include<sys/time.h>Intmain(){ charbuf[80]; fd_setrdfds; structtimevaltv; intret; while(1){ FD_ZERO(&rdfds); FD_SET(0,&rdfds); tv.tv_sec=1; tv.tv_usec=0; ret=select(1,&rdfds,NULL,NULL,&tv);/*注意最大值还要加1*/ if(ret<0)perror("select"); /*出错*/ if(ret==0)printf("timeout\n"); /*在设定的时间tv内,用户没有按键盘*/ else{ /*用户有按键盘,要读取用户的输入*/ scanf("%s",buf); printf("%s\n",buf); } } return(0);}71课堂练习***试编写独立的3个程序,要求分别完成如下功能:第一个程序每隔一秒产生1~100之间的随机数(可用sleep函数);将数据写入有名管道;第二个程序从有名管道读取数据,并显示;采用select函数限定阻塞等待时间为2秒。第三个程序创建有名管道;分别创建两个子进程,并分别调用execl函数执行程序1和程序2;接收CTL_C信号后向前两个进程发送结束信号;等待2个子进程结束后退出。7.3消息队列消息队列,就是一个消息的链表,是一系列保存在内核中的消息的列表。用户进程可以向消息队列尾部添加消息,也可以从消息队列读取消息,与管道通信非常相似。消息~是大小有限的数据块(LINUX)(msg.h)每个消息的最大字节数MSGMAX:8192*消息队列的最大长度MSGMNB:16384消息队列的优势:对每个消息指定特定消息类型,接收的时候不需要按队列次序,而是可以根据自定义条件接收特定类型的消息。可以把消息看作一个记录,具有特定的格式以及特定的优先级。7273消息队列操作*与FIFO类似,但不需要open/close操作创建消息队列标识符(queueID)。ftok()创建一个IPC函数所需的关键字key(某一资源识别代号).Msgget()~创建一个新队列或打开一个存在的队列;Sgsnd()~向队列末端添加一条新消息;Msgrcv()~从队列中读取消息,读取消息是不一定遵循先进先出的,也可以按消息的类型字段取消息.Msgctl()~消息队列控制(删除)
74消息队列相关函数ftok函数#include<sys/types.h>#include<sys/ipc.h>Key_tftok(char*pathname,charproj)Pathname~必须是一个存在的可访问的路径或文件;Proj(子序号)~不得为0。功能:根据pathname和proj来创建一个systemVIPC函数所需的关键字key(代表某一资源的识别代号).75例*:#include<sys/types.h>#include<sys/ipc.h>key_tmykey;mykey=ftok(“/home/usr1/SHMkey”,1);或mykey=ftok(“.”,’a’);//与当前路径结合产生key76创建/打开消息队列*
:
#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>intmsgget(key_tkey,intmsgflg)
功能:创建一个新队列或打开一个存在的队列.返回值:成功则返回消息队列ID,出错则返回-1.参数:key:消息队列的key值.Msgflg标志位~消息队列的访问权限IPC_CREAT~生成新的消息队列IPC_PRIVATE~仅为当前进程所访问77消息队列创建APIintopen_queue(key_tkeyval){ int qid; if((qid=msgget(keyval,IPC_CREAT|0660))==-1) { return(-1); } return(qid);}78向队列添加一条消息*intmsgsnd(intmsqid,structmsgbuf*msgp,intmsgsz,intmsgflg);
功能:向消息队列发送一个消息.参数:Msgid~消息队列IDMsgp~指向即将发送的消息(存储在的msgbuf结构中),Msgze~消息的大小。Msgflg~用来控制消息队列满载时,若设置了IPC_NOWAIT,则在消息队列没有足够空间时立即返回,否则等待直到满足条件。79structmsgbuf{ longmtype;/*typeofmessage*/ charmtext[];/*messagetext*/
…
…};80添加消息队列APIintsend_message(intqid,structmsgbuf*qbuf){ int result,length; length=sizeof(structmsgbuf)-sizeof(long); if((result=msgsnd(qid,qbuf,length,0))==-1) return(-1); return(result);}81从消息队列中读取一个消息*intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmsgtyp,intmsgflg);
功能:从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。返回:成功~返回实际读到的信息数据长度。 失败~返回-1主要参数Msqid~消息队列描述字;Msgsz~指定msgbuf的长度(即消息内容的长度)82从消息队列中读取一个消息Msgtyp~请求读取的消息类型;Msgtyp=0~接收第一个到来的消息;Msgtyp〉0~接收第一个到来的与此类型相同的消息;Msgtyp<0~接收第一个到来的等于或小于此类型绝对值的消息;Msgflg~消息标志。可以为以下几个常值的或:IPC_NOWAIT~如果没有满足条件的消息,调用立即返回;IPC_EXCEPT~与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息;IPC_NOERROR~如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。Msgflg=0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式83从消息队列中读取一个消息APIintread_message(intqid,longtype,structmymsgbuf*qbuf){intresult,length;length=sizeof(structmymsgbuf)-sizeof(long);if((result=msgrcv(qid,qbuf,length,type,0))==-1) return(-1);return(result);}84消息队列控制msgctl()*intmsgctl(intmsqid,intcmd,structmsqid_ds*buf);
调用返回:成功返回0,否则返回-1。cmd操作,共有三种cmd操作IPC_STAT、IPC_SET、IPC_RMID。IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。IPC_RMID:删除msqid标识的消息队列;
85范例:删除消息队列*msgctl(mqid,IPC_RMID,NULL);867.3消息队列例7.8**:设计一个程序,要求创建消息队列,输入的文字添加到消息队列后,读取队列中的消息输出。源程序代码:8788由此例可知,进程间通过消息队列通信,主要是创建或打开消息队列、添加消息、读取消息和控制消息队列这四种操作。89#include<sys/ipc.h>#include<sys/msg.h>#include<sys/stat.h>#include<sys/types.h>#include<stdio.h>#include<fcntl.h>#include<signal.h>#include<stdlib.h>#include<string.h>#definePROJID0xFF#defineLUCY1#definePETER2intmqid;voidterminate_handler(intsigno){ msgctl(mqid,IPC_RMID,NULL); exit(0);}实例**
90intmain(){ charfilenm[]="msg"; key_tmqkey; structmsgbuf{ longmtype; /*messagetype,mustbe>0*/ charmtext[256];/*messagedata*/ }msg; intret; mqkey=ftok(filenm,PROJID); if(mqkey==-1){ perror("ftokerror:"); exit(-1); } mqid=msgget(mqkey,IPC_CREAT|0666); if(mqid==-1){ perror("msggeterror:"); exit(-1); } signal(SIGINT,terminate_handler); signal(SIGTERM,terminate_handler); while(1){ printf("Lucy:"); fgets(msg.mtext,256,stdin); if(strncmp("quit",msg.mtext,4)==0){ msgctl(mqid,IPC_RMID,NULL); exit(0); } msg.mtext[strlen(msg.mtext)-1]='\0'; msg.mtype=LUCY; msgsnd(mqid,&msg,strlen(msg.mtext)+1,0); msgrcv(mqid,&msg,256,0,0); printf("Peter:%s\n",msg.mtext); }}91课堂练习仿照例7-7,采用消息队列操作编写简易聊天程序。作业FIFO和消息队列机制的对比分析7.4共享内存93Linux中进程间通信进程1进程2管道pipesignal消息队列共享内存共享内存Mmap建立共享内存映射Munmap建立共享内存映射文件操作内存操作Memcpystrcpy文件内存95一、系统V共享内存概述共享内存*特定的内存中一段存储区多个进程共享此存储区特点*最快的IPC方式信息量大数据可维持共享内存原理示意图96≈≈二、共享内存操作步骤*≈≈创建共享内存或打开映射共享内存读/写操作共享内存解除共享内存映射控制共享内存(删除)97≈≈二、共享内存操作步骤*≈≈创建共享内存或打开(已有的shm)映射共享内存读/写操作共享内存解除共享内存映射控制共享内存(删除)98三、共享内存API*创建共享内存~shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。映射共享内存~shmat()当前进程与共享内存区连接。撤销共享内存~shmdt()解除当前进程与共享内存区的映射。控制共享内存~shmctl()实现对共享内存区域的控制操作(删除)。99三、共享内存API(1)—创建* #include<sys/ipc.h>#include<sys/shm.h>intshmget(key_tkey,size_tsize,intshmflg)参数:名字(key):ftok()或整形数大小(size):字节为单位访问方式(shmflg):读、写,etc.访问权限:所有者、同组用户、其他用户(UGO)如,0644(同组和其他用户只读)可与IPC_CREAT进行逻辑或,表示创建新的共享内存或读取已有的共享内存IPC_CREATE:表明要创建新的共享内存空间。IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。返回:标识(ID)
100intshmget(key_tkey,size_tsize,intshmflg)成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。EINVAL
参数size小于SHMMIN或大于SHMMAX。EEXIST
预建立key所致的共享内存,但已经存在。EIDRM
参数key所致的共享内存已经删除。ENOSPC
超过了系统允许建立的共享内存的最大值 (SHMALL)。ENOENT
参数key所指的共享内存不存在,参数shmflg 也未设IPC_CREAT位。EACCES
没有权限。ENOMEM
核心内存不足。
注:perror(“操作”)的执行后将以errno(错误代码)的值来决定输出的字符串。101创建共享内存*示例#include<sys/ipc.h>#include<sys/shm.h>….int shmid,shmSize=1024;key_tshmKey;shmKey=ftok(“.”,’a’);shmId=shmget(shmKey,shmSize,IPC_CREAT|0666)102三、共享内存API(2)—映射*#include<sys/types.h>#include<sys/shm.h>void*shmat(intshmid,constvoid*shmaddr,intshmflg);功能:将当前进程与共享内存连接,即获取共享内存首地址。参数:
shmid:shnget的返回值
shmaddr:当前进程的地址空间的具体位置。
设为NULL指针~将由系统指定
shmflg:访问方式。
0~可读写;
SHM_RDONLY~只读(即使已设置了写的权限)103三、共享内存API(2)—映射*shmat()函数返回值成功~返回共享内存的首地址失败~返回-1(错误原因存于errno中);错误代码:EACCES:对于所请求的连上类型,进程没有足够的权限,并且不具有CAP_IPC_OWNER权能EINVAL:参数无效
ENOMEM:内存不足,无法分配描述词或页表104共享内存API(2)—映射*示例char*shmAddr;shmId=shmget(shmKey,shmSize,IPC_CREAT|0666)shmAddr=(char*)shmat(shmId,NULL,0)105共享内存API(3)—解除*intshmdt(char*shmaddr);功能:解除进程对共享内存区域的连接。 *进程结束时,脱离共享内存。Shmaddr~由smat()返回的共享内存地址。返回值0~成功,-1~失败(错误原因存于errno中)106共享内存API(4)—删除*intshmctl(intshmqid,intcmd,structshmid_ds*buf);返回值:0成功,-1错误CmdIPC_RMID~删除共享内存IPC_STAT~把共享内存的shmid_ds结构复制到buf107structshmid_ds{structipc_permshm_perm;/*操作权限*/intshm_segsz;/*段的大小(以字节为单位)*/time_tshm_atime;/*最后一个进程连接到该段的时间*/time_tshm_dtime;/*最后一个进程脱离该段的时间*/time_tshm_ctime;/*最后一次修改这个结构的时间*/unsignedshortshm_cpid;/*创建该段进程的pid*/unsignedshortshm_lpid;/*在该段上操作的最后一个进程的pid*/shortshm_nattch;/*当前连接到该段的进程的个数*/ unsignedshortshm_npages;/*段的大小(以页为单位)*/unsignedlong*shm_pages;/*指向frames->SHMMAX的指针数组*/structvm_area_struct*attaches;/*对共享段的描述*/};structipc_perm{
__kernel_key_t
key;
__kernel_uid_t
uid;
__kernel_gid_t
gid;
__kernel_uid_t
cuid;
__kernel_gid_t
cgid;
__kernel_mode_tmode;
unsignedshort
seq;
};108char*initShrMem(intshmKey,intshmSize){char*shmAddr; /*创建共享内存*/if((shmId=shmget(shmKey,shmSize,IPC_CREAT|0666))==-1) return-1;/*共享内存与目前进程的连接*/if((int)(shmAddr=(char*)shmat(shmId,0,0))==-1){/*共享内存的释放*/shmctl(shmId,IPC_RMID,NULL);shmId=0;return-1;}/*共享内存的初始化*/memset(shmAddr,'\0',shmSize);return(shmAddr);}共享内存初始化范例***109intexitShrMem(char*shmAddr){/*使共享内存脱离进程*/if(shmdt(shmAddr)==-1){ perror("detacherror"); return-1;}/*共享内存释放*/if(shmctl(shmId,IPC_RMID,NULL)==-1){ perror("detacherror"); return-1;}return1;}共享内存撤销范例***110共享内存的读写***#include<string.h>(1)voidmemcpy(void*dest,//目的地址
constvoid*src,//源地址
siz_tn)//拷贝的字节数(2)voidstrcpy(void*dest,//目的地址
constvoid*src,//源地址
siz_tn)//拷贝的字节数≈≈111共享内存操作引例***
创建共享内存创建子进程子进程连接共享内存写共享内存脱离共享内存父进程读共享内存连接共享内存读共享内存状态信息读共享内存数据脱离共享内存删除读共享内存112//shm0.c共享内存操作引例***
#include<string.h>#include<sys/ipc.h>#include<sys/shm.h>#include<sys/types.h>#defineKEY1234#defineSIZE1024intmain(){ intshmid,pid; char*shmaddr; structshmid_dsbuf;
shmid=shmget(KEY,SIZE,IPC_CREAT|0666); if((pid=fork())==0){ printf("Myparent'pid=%d\n",getppid());
shmaddr=(char*)shmat(shmid,NULL,0); strncpy(shmaddr,"Hi!Iamchildprocess!\n“); shmdt(shmaddr); return1; }113else{ printf("childpid=%d\n",pid); sleep(1);
shmctl(shmid,IPC_STAT,&buf);
/*段的大小(以字节为单位)*/ printf("shm_segsz=%d\n",buf.shm_segsz);
/*创建该段进程的pid*/ printf("shm_cpid=%d\n",buf.shm_cpid); /*在该段上操作的最后一个进程的pid*/ printf("shm_lpid=%d\n",buf.shm_lpid);
shmaddr=(char*)shmat(shmid,NULL,0); /*读取共享内存*/ printf("%s",shmaddr); shmdt(shmaddr); shmctl(shmid,IPC_RMID,NULL); }}114共享内存实例**分别编写两个程序,完成共享内存的读与写操作。要求: 待写入的数据为随机浮点数; 每隔一秒采集一次不同通道(共8个),并写入共享内存;
循环写入无限次;
ctl_c退出程序。
通道选择开关1N0IN1IN2IN3IN4IN5IN6IN7ABC收发进程1115共享内存实例**数据结构定义typedefstruct{ intch; /*通道号*/ doublepower;/*被测数据*/}data;116//shm_com.h**/*Acommonheaderfiletodescribethesharedmemorywewishtopassabout.*/#include<unistd.h>#include<string.h>#include<stdlib.h>#include<sys/ipc.h>#include<sys/shm.h>#include<signal.h>#include<sys/types.h>#define NUMBER8typedefstruct{ intch; doublepower;}data;117//shm1.c**#include"shm_com.h"intexitSig=0;voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1; raise(SIGHUP);}intmain(){ intshm_id,i; key_tkey;
data*p_map,*pshmwr,wdata;
(void)signal(SIGINT,exitProc); key=ftok(".",'a'); if(key==-1) perror("ftokerror");
shm_id=shmget(key,sizeof(data)*NUMBER,IPC_CREAT|0666); if(shm_id==-1){ perror("shmgeterror"); return; }118
p_map=(data*)shmat(shm_id,NULL,0); if(p_map==(data*)-1){ perror("shmaterror"); return; } while(!exitSig) { pshmwr=p_map; for(i=0;i<NUMBER;i++) { wdata.ch=i; wdata.power=drand48()*10.0;/*随机浮点数*/
memcpy(pshmwr,&wdata,sizeof(data)); printf("ch=%dpower=%f\n",pshmwr->ch,pshmwr->power); pshmwr++; sleep(1); } } if(shmdt(p_map)==-1) perror("detacherror");}119//shm2.c**#include"shm_com.h"intexitSig=0;voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1; raise(SIGHUP);}intmain(){ intshm_id,i; key_tkey; data*p_map,*pshmrd,rdata;
(void)signal(SIGINT,exitProc); key=ftok(".",'a'); if(key==-1) perror("ftokerror");
shm_id=shmget(key,sizeof(data)*NUMBER,IPC_CREAT|0666); if(shm_id==-1){ perror("shmgeterror"); return; }120
p_map=(data*)shmat(shm_id,NULL,0); if(p_map==(data*)-1){ perror("shmaterror"); return; } while(!exitSig){ pshmrd=p_map; for(i=0;i<NUMBER;i++) {
memcpy(&rdata,pshmrd,sizeof(data));
printf("ch=%dpower=%f\n",rdata.ch,rdata.power); pshmrd++; sleep(1); } }
if(shmdt(p_map)==-1)
perror("detacherror"); if(shmctl(shm_id,IPC_RMID,NULL)==-1)
perror("shmctlerror");}121课外补充(1)信号量**在多用户、多任务系统中,对某一资源排他操作信号量提供对进程间共享资源访问控制机制,相当于内存中的标志。进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。共享资源(M/IO)122信号量**信号量对临界段操作,需要确保只有一个进程独占P(sv)操作若sv>0则sv-1,若sv=0则让进程暂停,等待对共享资源的操作V(sv)操作如果有等待对共享资源的操作的进程,即等待Sv>0,则该进程继续执行。如果没有等待进程,则sv+1临界有效(空闲)时,sv=true(>0),执行P(sv)操作,使得sv变为false,临界忙临界无效(忙)时,V(sv),sv=true123信号量API
**信号量操作UNIX采用信号量数组打开或创建信号量semget()
信号量值操作semop()获得或设置信号量属性semctl()
124信号量的创建*#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>Intsemget(Key_tkey,intnum_sems,intsem_flags)功能:创建一个新的信号量,或者获取一个已存在的信号量ID。类似于文件打开操作,返回类似于文件描述符的值,信号量描述符。返回值: 成功~非零值(>0),失败~-1参数说明:key~标识信号量资源。与文件名类似。用于不同的进程间使用同一个信号量的Key;Num_sems:信号量个数。对于一个资源,常定义为1;Sem_flags:信号量访问权限,如,IPC_CREAT|0666125范例:信号量**intopenSemId(key_tkey){intsemid; semid=semget(key,1,IPC_CREAT|0666);if(semid==-1){ perror("semget");return-1;}returnsemid;}126*信号量控制int
semctl(int
semid,int
semnum,int
cmd,union
semun
arg);功能:对信号量进行一系列的控制。Semid~要操作的信号描述符Semnum~信号的个数(0:代表1个信号量)信号量是数组。Cmd~操作的命令.经常用的两个值是:SETVAL(设置信号量的初值)IPC_RMID(删除信号量).arg是一个给cmd的参数.unionsemun{ intval; structsemid_ds*buf; unsignedshort*arry;}127范例:信号量初始化**intsetSemvalue(intsemid){ unionsemunsemUnion; semUnion.val=1; if(semctl(semid,0,SETVAL,semUnion)==-1)return-1; return1;}128范例:信号量操作—删除操作**intdelSemaphore(intsemid){ if(semctl(semid,0,IPC_RMID)==-1){ perror("semctlIPC_RMID"); return-1; } return1;}129信号量操作(P、V)intsemop(intsemid,structsembuf*semops,unsignednsops);功能: 改变信号量值。参数:semid~semget()的返回值,信号量的描述符;Sops~指向一个结构体数组的指针; structsembuf{ shortsem_num; //=0(代表第一个信号量) shortsem_op; //(-1,1)p,v shortsem_flg; //SEM_UNDO }nsops~sops结构体数组的个数,一般设为1。
130*structsembuf{ shortsem_num;//=0(代表第一个信号量) shortsem_op; //(-1,1)p,v shortsem_flg; //SEM_UNDO }Sem_num:欲处理的信号编号,0代表第一个信号量sem_op:-1:对资源加锁(P操作)+1:对资源解锁(V操作)sem_flg=SEM_UNDO表示进程没有释放信号量而终止的时候,系统自动释放该进程所使用的信号量。131范例:信号量操作—P操作**intlockShrMem(intsemId){structsembufsops;sops.sem_num=0;sops.s
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工业硅捣炉工工艺流程
- 脚手架的搭设和拆除施工方案
- 公路土木工程建设施工可行性研究报告
- 耳塞式耳机购买协议
- 家居装饰补充协议书
- 标准入学保证书模板
- 招标工程材料采购
- 水电站建设招标条件
- 招标文件购买责任本
- 代购服务合同纠纷案例分析
- 北师版八年级数学上册 第四章 一次函数(压轴专练)(十大题型)
- 全国教育科学规划课题申报书:18.《教育强国建设的投入保障研究》
- 第1课时淘气的一天(教学设计)-2024-2025学年北师大版(2024)一年级上册数学
- 2024-2025学年统编版(2024)道德与法治小学一年级上册教学设计
- 医院医技科室与临床科室定期沟通制度
- 期中 (试题) -2024-2025学年人教PEP版(2024)英语三年级上册
- 律师事务所人员管理制度
- 渣土、余土运输服务方案(技术方案)
- 网络安全管理责任制度制度存在的问题(8篇)
- 20以内的加法口算练习题4000题 205
- 《网络系统建设与运维》课件-项目一 5G技术特点和网
评论
0/150
提交评论