⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 进程和线程编程.htm

📁 进程与线程编程模式是进行程序开发的基础,次文案详细描述了此项内容
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I241 name=I241></A>
<CENTER><B><FONT 
size=+2>popen()和pclose()</FONT></B></CENTER><BR>如果你认为上面创建和使用管道的方法过于繁琐的话,你也可以使用下面的简单的方法:
<P>库函数:popen()和pclose();<BR>原型:FILE*popen(char*command,char*type);<BR>返回值:如果成功,返回一个新的文件流。<BR>如果无法创建进程或者管道,返回NULL。<BR>&nbsp;&nbsp;&nbsp; 
此标准的库函数通过在系统内部调用pipe()来创建一个半双工的管道,然后它创建一个子进程,启动shell,最后在shell上执行command参数中的命令。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或者w,分别代表读或写。但不能同时为读和写。在Linux系统下,管道将会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。
<P>&nbsp;&nbsp;&nbsp; 
虽然此库函数的用法很简单,但也有一些不利的地方。例如它失去了使用系统调用pipe()时可以有的对系统的控制。尽管这样,因为可以直接地使用shell命令,所以shell中的一些通配符和其他的一些扩展符号都可以在command参数中使用。<BR>使用popen()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。
<P><BR>库函数:pclose();<BR>原型:intpclose(FILE*stream);<BR>返回值:返回系统调用wait4()的状态。<BR>如果stream无效,或者系统调用wait4()失败,则返回-1。<BR>&nbsp;&nbsp;&nbsp; 
注意此库函数等待管道进程运行结束,然后关闭文件流。库函数pclose()在使用popen()创建的进程上执行wait4()函数。当它返回时,它将破坏管道和文件系统。<BR>&nbsp;&nbsp;&nbsp; 
在下面的例子中,用sort命令打开了一个管道,然后对一个字符数组排序:
<P>#include&lt;stdio.h&gt;<BR>#defineMAXSTRS5<BR>intmain(void)<BR>{<BR>intcntr;<BR>FILE*pipe_fp;<BR>char*strings[MAXSTRS]={"echo","bravo","alpha",<BR>"charlie","delta"};<BR>/*Createonewaypipelinewithcalltopopen()*/<BR>if((pipe_fp=popen("sort","w"))==NULL)<BR>{<BR>perror("popen");<BR>exit(1);<BR>}<BR>/*Processingloop*/<BR>for(cntr=0;cntr&lt;MAXSTRS;cntr++){<BR>fputs(strings[cntr],pipe_fp);<BR>fputc('\n',pipe_fp);<BR>}<BR>/*Closethepipe*/<BR>pclose(pipe_fp);<BR>return(0);<BR>}<BR>因为popen()使用shell执行命令,所以所有的shell扩展符和通配符都可以使用。此外,它还可以和popen()一起使用重定向和输出管道函数。再看下面的例子:<BR>popen("ls~scottb","r");<BR>popen("sort&gt;/tmp/foo","w");<BR>popen("sort|uniq|more","w");<BR>下面的程序是另一个使用popen()的例子,它打开两个管道(一个用于ls命令,另一个用于<BR>sort命令):<BR>#include&lt;stdio.h&gt;<BR>intmain(void)<BR>{<BR>FILE*pipein_fp,*pipeout_fp;<BR>charreadbuf[80];<BR>/*Createonewaypipelinewithcalltopopen()*/<BR>if((pipein_fp=popen("ls","r"))==NULL)<BR>{<BR>perror("popen");<BR>exit(1);<BR>}<BR>/*Createonewaypipelinewithcalltopopen()*/<BR>if((pipeout_fp=popen("sort","w"))==NULL)<BR>{<BR>perror("popen");<BR>exit(1);<BR>}<BR>/*Processingloop*/<BR>while(fgets(readbuf,80,pipein_fp))<BR>fputs(readbuf,pipeout_fp);<BR>/*Closethepipes*/<BR>pclose(pipein_fp);<BR>pclose(pipeout_fp);<BR>return(0);<BR>}<BR>最后,我们再看一个使用popen()的例子。此程序用于创建一个命令和文件之间的管道:<BR>#include&lt;stdio.h&gt;<BR>intmain(intargc,char*argv[])<BR>{<BR>FILE*pipe_fp,*infile;<BR>charreadbuf[80];<BR>if(argc!=3){<BR>fprintf(stderr,"USAGE:popen3[command][filename]\n");<BR>exit(1);<BR>}<BR>/*Open 
up input 
file*/<BR>if((infile=fopen(argv[2],"rt"))==NULL)<BR>{<BR>perror("fopen");<BR>exit(1);<BR>}<BR>/*Create 
one way pipe line with call 
topopen()*/<BR>if((pipe_fp=popen(argv[1],"w"))==NULL)<BR>{<BR>perror("popen");<BR>exit(1);<BR>}<BR>/*Processingloop*/<BR>do{<BR>fgets(readbuf,80,infile);<BR>if(feof(infile))break;<BR>fputs(readbuf,pipe_fp);<BR>}while(!feof(infile));<BR>fclose(infile);<BR>pclose(pipe_fp);<BR>return(0);<BR>}<BR>下面是使用此程序的例子:<BR>popen3sortpopen3.c<BR>popen3catpopen3.c<BR>popen3morepopen3.c<BR>popen3catpopen3.c|grepmain
<P>
<P>
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I242 name=I242></A>
<CENTER><B><FONT size=+2>命名管道</FONT></B></CENTER><BR>&nbsp;&nbsp;&nbsp; 
命名管道和一般的管道基本相同,但也有一些显著的不同:
<P>*命名管道是在文件系统中作为一个特殊的设备文件而存在的。<BR>*不同祖先的进程之间可以通过管道共享数据。<BR>*当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。
<P>&nbsp;&nbsp;&nbsp; 
一个管道必须既有读取进程,也要有写入进程。如果一个进程试图写入到一个没有读取进程的管道中,那么系统内核将会产生SIGPIPE信号。当两个以上的进程同时使用管道时,这一点尤其重要。
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I243 name=I243></A>
<CENTER><B><FONT size=+2>创建FIFO</FONT></B></CENTER><BR>&nbsp;&nbsp;&nbsp; 
可以有几种方法创建一个命名管道。头两种方法可以使用shell。
<P>mknodMYFIFOp<BR>mkfifoa=rwMYFIFO<BR>&nbsp;&nbsp;&nbsp; 
上面的两个命名执行同样的操作,但其中有一点不同。命令mkfifo提供一个在创建之后直接改变FIFO文件存取权限的途径,而命令mknod需要调用命令chmod。<BR>&nbsp;&nbsp;&nbsp; 
一个物理文件系统可以通过p指示器十分容易地分辨出一个FIFO文件。
<P>$ls-lMYFIFO<BR>prw-r--r--1rootroot0Dec1422:15MYFIFO|
<P>&nbsp;&nbsp;&nbsp; 请注意在文件名后面的管道符号“|”。<BR>&nbsp;&nbsp;&nbsp; 
我们可以使用系统调用mknod()来创建一个FIFO管道:
<P>库函数:mknod();<BR>原型:intmknod(char*pathname,mode_tmode,dev_tdev);<BR>返回值:如果成功,返回0<BR>如果失败,返回-1:errno=EFAULT(无效路径名)<BR>EACCES(无存取权限)<BR>ENAMETOOLONG(路径名太长)<BR>ENOENT(无效路径名)<BR>ENOTDIR(无效路径名)
<P>&nbsp;&nbsp;&nbsp; 下面看一个使用C语言创建FIFO管道的例子:
<P>mknod("/tmp/MYFIFO",S_IFIFO|0666,0);
<P>&nbsp;&nbsp;&nbsp; 
在这个例子中,文件/tmp/MYFIFO是要创建的FIFO文件。它的存取权限是0666。存取权限<BR>也可以使用umask修改:
<P>final_umask=requested_permissions&amp;~original_umask
<P>&nbsp;&nbsp;&nbsp; 
一个常用的使用系统调用umask()的方法就是临时地清除umask的值:<BR>umask(0);<BR>mknod("/tmp/MYFIFO",S_IFIFO|0666,0);
<P>&nbsp;&nbsp;&nbsp; 
另外,mknod()中的第三个参数只有在创建一个设备文件时才能用到。它包括设备文件的<BR>主设备号和从设备号。<BR>}<BR>}
<P>
<P>
<P><BR>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I244 name=I244></A>
<CENTER><B><FONT size=+2>操作FIFO</FONT></B></CENTER><BR>&nbsp;&nbsp;&nbsp; 
FIFO上的I/O操作和正常管道上的I/O操作基本一样,只有一个主要的不同。系统调用open用来在物理上打开一个管道。在半双工的管道中,这是不必要的。因为管道在系统内核中,而不是在一个物理的文件系统中。在我们的例子中,我们将像使用一个文件流一样使用管道,也就是使用fopen()打开管道,使用fclose()关闭它。<BR>&nbsp;&nbsp;&nbsp; 
请看下面的简单的服务程序进程:<BR>#include&lt;stdio.h&gt;<BR>#include&lt;stdlib.h&gt;<BR>#include&lt;sys/stat.h&gt;<BR>#include&lt;unistd.h&gt;<BR>#include&lt;linux/stat.h&gt;<BR>#defineFIFO_FILE"MYFIFO"<BR>intmain(void)<BR>{<BR>FILE*fp;<BR>charreadbuf[80];<BR>/*CreatetheFIFOifitdoesnotexist*/<BR>umask(0);<BR>mknod(FIFO_FILE,S_IFIFO|0666,0);<BR>while(1)<BR>{<BR>fp=fopen(FIFO_FILE,"r");<BR>fgets(readbuf,80,fp);<BR>printf("Receivedstring:%s\n",readbuf);<BR>fclose(fp);<BR>return(0);<BR>&nbsp;&nbsp;&nbsp; 
因为FIFO管道缺省时有阻塞的函数,所以你可以在后台运行此程序:<BR>$fifoserver&amp;<BR>&nbsp;&nbsp;&nbsp; 
再来看一下下面的简单的客户端程序:<BR>#include&lt;stdio.h&gt;<BR>#include&lt;stdlib.h&gt;<BR>#defineFIFO_FILE"MYFIFO"<BR>intmain(int 
argc,char* 
argv[])<BR>{<BR>FILE*fp;<BR>if(argc!=2){<BR>printf("USAGE:fifoclient[string]\n");<BR>exit(1);<BR>}<BR>if((fp=fopen(FIFO_FILE,"w"))==NULL){<BR>perror("fopen");<BR>exit(1);<BR>}<BR>fputs(argv[1],fp);<BR>fclose(fp);<BR>return(0);<BR>}
<P>
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I245 name=I245></A>
<CENTER><B><FONT size=+2>阻塞FIFO</FONT></B></CENTER><BR>&nbsp;&nbsp;&nbsp; 
一般情况下,FIFO管道上将会有阻塞的情况发生。也就是说,如果一个FIFO管道打开供读取的话,它将一直阻塞,直到其他的进程打开管道写入信息。这种过程反过来也一样。如果你不需要阻塞函数的话,你可以在系统调用open()中设置O_NONBLOCK标志,这样可以取消缺省的阻塞函数。
<P>
<P>
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I246 name=I246></A>
<CENTER><B><FONT size=+2>消息队列</FONT></B></CENTER><BR>&nbsp;&nbsp;&nbsp; 
在UNIX的SystemV版本,AT&amp;T引进了三种新形式的IPC功能(消息队列、信号量、以及共享内存)。但BSD版本的UNIX使用套接口作为主要的IPC形式。Linux系统同时支持这两个版本。<BR>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I247 name=I247></A>
<CENTER><B><FONT size=+2>msgget()</FONT></B></CENTER><BR>系统调用msgget()
<P>&nbsp;&nbsp;&nbsp; 如果希望创建一个新的消息队列,或者希望存取一个已经存在的消息队列,你可以使用系统调用msgget()。
<P>系统调用:msgget();<BR>原型:intmsgget(key_t key,int 
msgflg);<BR>返回值:如果成功,返回消息队列标识符<BR>如果失败,则返回-1:errno=EACCESS(权限不允许)<BR>EEXIST(队列已经存在,无法创建)<BR>EIDRM(队列标志为删除)<BR>ENOENT(队列不存在)<BR>ENOMEM(创建队列时内存不够)<BR>ENOSPC(超出最大队列限制)
<P>&nbsp;&nbsp;&nbsp; 
系统调用msgget()中的第一个参数是关键字值(通常是由ftok()返回的)。然后此关键字值将会和其他已经存在于系统内核中的关键字值比较。这时,打开和存取操作是和参数msgflg中的内容相关的。<BR>IPC_CREAT如果内核中没有此队列,则创建它。<BR>IPC_EXCL当和IPC_CREAT一起使用时,如果队列已经存在,则失败。
<P>&nbsp;&nbsp;&nbsp; 
如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。<BR>下面看一个打开和创建一个消息队列的例子:<BR>intopen_queue(key_t 
keyval)<BR>{<BR>intqid;<BR>if((qid=msgget(keyval,IPC_CREAT|0660))==-1)<BR>{<BR>return(-1);<BR>}<BR>return(qid);<BR>}
<P>
<P>
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I248 name=I248></A>
<CENTER><B><FONT size=+2>msgsnd()</FONT></B></CENTER><BR>系统调用msgsnd()
<P>&nbsp;&nbsp;&nbsp; 
一旦我们得到了队列标识符,我们就可以在队列上执行我们希望的操作了。如果想要往队列中发送一条消息,你可以使用系统调用msgsnd():
<P>系统调用:msgsnd();<BR>原型:intmsgsnd(int msqid,struct msgbuf*msgp,int msgsz,int 
msgflg);<BR>返回值:如果成功,0。<BR>如果失败,-1:errno=EAGAIN(队列已满,并且使用了IPC_NOWAIT)<BR>EACCES(没有写的权限)<BR>EFAULT(msgp地址无效)<BR>EIDRM(消息队列已经删除)<BR>EINTR(当等待写操作时,收到一个信号)<BR>EINVAL(无效的消息队列标识符,非正数的消息类型,或<BR>者无效的消息长度)<BR>ENOMEM(没有足够的内存复制消息缓冲区)
<P>&nbsp;&nbsp;&nbsp; 
系统调用msgsnd()的第一个参数是消息队列标识符,它是由系统调用msgget返回的。第二个参数是msgp,是指向消息缓冲区的指针。参数msgsz中包含的是消息的字节大小,但不包括消息类型的长度(4个字节)。<BR>&nbsp;&nbsp;&nbsp; 
参数msgflg可以设置为0(此时为忽略此参数),或者使用IPC_NOWAIT。
<P>&nbsp;&nbsp;&nbsp; 
如果消息队列已满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。如果没有指明,调用进程将会挂起,直到消息可以写入到队列中。<BR>&nbsp;&nbsp;&nbsp; 
下面是一个发送消息的程序:
<P>intsend_message(int qid,struct mymsgbuf 
*qbuf)<BR>{<BR>intresult,length;<BR>/*The length is essentially the size of the 
structure minus 
sizeof(mtype)*/<BR>length=sizeof(structmymsgbuf)-sizeof(long);<BR>if((result=msgsnd(qid,qbuf,length,0))==-1)<BR>{<BR>return(-1);<BR>}<BR>return(result);<BR>}
<P>&nbsp;&nbsp;&nbsp; 这个小程序试图将存储在缓冲区qbuf中的消息发送到消息队列qid中。下面的程序是结合了上面两个程序的一个完整程序:
<P>#include&lt;stdio.h&gt;<BR>#include&lt;stdlib.h&gt;<BR>#include&lt;linux/ipc.h&gt;<BR>#include&lt;linux/msg.h&gt;<BR>main()<BR>{<BR>intqid;<BR>key_t 
msgkey;<BR>struct mymsgbuf{<BR>longmtype;/*Message type*/<BR>intrequest;/*Work 
request number*/<BR>doublesalary;/*Employee's salary*/<BR>}msg;<BR>/*Generateour 
IPC key 
value*/<BR>msgkey=ftok(".",'m');<BR>/*Open/createthequeue*/<BR>if((qid=open_queue(msgkey))==-1){<BR>perror("open_queue");<BR>exit(1);<BR>}<BR>/*Load 
up the message with a r bitrary test 
data*/<BR>msg.mtype=1;/*Messagetypemustbeapositivenumber!*/<BR>msg.request=1;/*Dataelement#1*/<BR>msg.salary=1000.00;/*Data 
element #2(my yearly 
salary!)*/<BR>/*Bombsaway!*/<BR>if((send_message(qid,&amp;msg))==-1){<BR>perror("send_message");<BR>exit(1);<BR>}<BR>}<BR>&nbsp;&nbsp;&nbsp; 
在创建和打开消息队列以后,我们将测试数据装入到消息缓冲区中。最后调用send_messag把消息发送到消息队列中。现在在消息队列中有了一条消息,我们可以使用ipcs命令来查看队列的状态。下面讨论如何从队列中获取消息。可以使用系统调用msgrcv():
<P>
<P>
<CENTER><A 
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I249 name=I249></A>
<CENTER><B><FONT 
size=+2>msgrcv()</FONT></B></CENTER><BR>系统调用:msgrcv();<BR>原型:intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);<BR>返回值:如果成功,则返回复制到消息缓冲区的字节数。<BR>如果失败,则返回-1:errno=E2BIG(消息的长度大于msgsz,没有MSG_NOERROR)<BR>EACCES(没有读的权限)<BR>EFAULT(msgp指向的地址是无效的)<BR>EIDRM(队列已经被删除)<BR>EINTR(被信号中断)<BR>EINVAL(msgqid无效,或者msgsz小于0)<BR>ENOMSG(使用IPC_NOWAIT,同时队列中的消息无法满足要求)
<P>&nbsp;&nbsp;&nbsp; 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -