📄 进程和线程编程.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0041)http://www.huihoo.com/joyfire.net/11.html -->
<HTML><HEAD><TITLE>进程和线程编程</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META content="MSHTML 6.00.2900.2722" name=GENERATOR></HEAD>
<BODY style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体">
<CENTER>
<TABLE cellSpacing=10 cellPadding=10 width="60%" bgColor=#ffb693>
<TBODY>
<TR>
<TD align=middle><FONT
size=+2><!--标题由此开始-->进程和线程编程</FONT></TD></TR></TBODY></TABLE></CENTER>
<P>
<H3>目 录</H3><!--目录由此开始--><A id=Content name=Content></A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I236">进程和线程编程</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I237">原始管道</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I238">pipe()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I239">dup()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I240">dup2()</A>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I241">popen()和pclose()</A>
</LI></OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I242">命名管道</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I243">创建FIFO</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I244">操作FIFO</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I245">阻塞FIFO</A>
</LI></OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I246">消息队列</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I247">msgget()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I248">msgsnd()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I249">msgrcv()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I250">msgctl()</A>
</LI></OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I251">信号量</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I252">semget()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I253">semop()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I254">semctl()</A>
</LI></OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I255">共享内存</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I256">shmget()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I257">shmat()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I258">shmctl()</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I259">shmdt()</A>
</LI></OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I260">线程</A>
<OL>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I261">线程同步</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I262">使用信号量协调程序</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I263">代码例子</A>
<OL>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I264">newthread</A>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I265">exitthead</A>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I266">getchannel</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I267">def</A>
<LI><A href="http://www.huihoo.com/joyfire.net/11.html#I268">release</A>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I269">redezvous</A>
<LI><A
href="http://www.huihoo.com/joyfire.net/11.html#I270">unbouded</A></LI></OL></LI></OL></LI></OL></LI></OL>
<HR>
<BR><A id=I236 name=I236></A>
<CENTER><B><FONT size=+2>进程和线程编程</FONT></B></CENTER><BR>
看一下UNIX系统中的进程和Mach的任务和线程之间的关系。在UNIX系统中,一个进程包括一个可执行的程序和一系列的资源,例如文件描述符表和地址空间。在Mach中,一个任务仅包括一系列的资源;线程处理所有的可执行代码。一个Mach的任务可以有任意数目的线程和它相关,同时每个线程必须和某个任务相关。和某一个给定的任务相关的所有线程都共享任务的资源。这样,一个线程就是一个程序计数器、一个堆栈和一系列的寄存器。所有需要使用的数据结构都属于任务。一个UNIX系统中的进程在Mach中对应于一个任务和一个单独的线程。<BR>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I237 name=I237></A>
<CENTER><B><FONT size=+2>原始管道</FONT></B></CENTER><BR>
使用C语言创建管道要比在shell下使用管道复杂一些。如果要使用C语言创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。
<P>
可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。<BR>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I238 name=I238></A>
<CENTER><B><FONT
size=+2>pipe()</FONT></B></CENTER><BR>系统调用:pipe();<BR>原型:intpipe(intfd[2]);<BR>返回值:如果系统调用成功,返回0<BR>如果系统调用失败返回-1:errno=EMFILE(没有空闲的文件描述符)<BR>EMFILE(系统文件表已满)<BR>EFAULT(fd数组无效)<BR>注意fd[0]用于读取管道,fd[1]用于写入管道。<BR>#include<stdio.h><BR>#include<unistd.h><BR>#include<sys/types.h><BR>main()<BR>{<BR>intfd[2];<BR>pipe(fd);<BR>..<BR>}<BR>一旦创建了管道,我们就可以创建一个新的子进程:<BR>#include<stdio.h><BR>#include<unistd.h><BR>#include<sys/types.h><BR>main()<BR>{<BR>intfd[2];<BR>pid_t
childpid;<BR>pipe(fd);<BR>if((childpid=fork())==-1)<BR>{<BR>perror("fork");<BR>exit(1);<BR>}..<BR>}
<P>
如果父进程希望从子进程中读取数据,那么它应该关闭fd1,同时子进程关闭fd0。反之,如果父进程希望向子进程中发送数据,那么它应该关闭fd0,同时子进程关闭fd1。因为文件描述符是在父进程和子进程之间共享,所以我们要及时地关闭不需要的管道的那一端。单从技术的角度来说,如果管道的一端没有正确地关闭的话,你将无法得到一个EOF。
<P>#include<stdio.h><BR>#include<unistd.h><BR>#include<sys/types.h><BR>main()<BR>{<BR>intfd[2];<BR>pid_t
childpid;<BR>pipe(fd);<BR>if((childpid=fork())==-1)<BR>{<BR>perror("fork");<BR>exit(1);<BR>}<BR>if(childpid==0)<BR>{<BR>/*Child
process closes up in put side of
pipe*/<BR>close(fd[0]);<BR>}<BR>else<BR>{<BR>/*Parent process closes up out put
side of pipe*/<BR>close(fd[1]);<BR>}..<BR>}
<P> 正如前面提到的,一但创建了管道之后,管道所使用的文件描述符就和正常文件的文件描述符一样了。
<P>#include<stdio.h><BR>#include<unistd.h><BR>#include<sys/types.h><BR>intmain(void)<BR>{<BR>intfd[2],nbytes;<BR>pid_tchildpid;<BR>charstring[]="Hello,world!\n";<BR>charreadbuffer[80];<BR>pipe(fd);<BR>if((childpid=fork())==-1)<BR>{<BR>perror("fork");<BR>exit(1);<BR>}<BR>if(childpid==0)<BR>{<BR>/*Child
process closes up in put side of
pipe*/<BR>close(fd[0]);<BR>/*Send"string"through the out put side of
pipe*/<BR>write(fd[1],string,strlen(string));<BR>exit(0);<BR>}<BR>else<BR>{<BR>/*Parent
process closes up out put side of
pipe*/<BR>close(fd[1]);<BR>/*Readinastringfromthepipe*/<BR>nbytes=read(fd[0],readbuffer,sizeof(readbuffer));<BR>printf("Receivedstring:%s",readbuffer);<BR>}<BR>return(0);<BR>}
<P>
一般情况下,子进程中的文件描述符将会复制到标准的输入和输出中。这样子进程可以使用exec()执行另一个程序,此程序继承了标准的数据流。
<P>
<P>
<P>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I239 name=I239></A>
<CENTER><B><FONT
size=+2>dup()</FONT></B></CENTER><BR>系统调用:dup();<BR>原型:intdup(intoldfd);<BR>返回:如果系统调用成功,返回新的文件描述符<BR>如果系统调用失败,返回-1:errno=EBADF(oldfd不是有效的文件描述符)<BR>EBADF(newfd超出范围)<BR>EMFILE(进程的文件描述符太多)
<P>
注意旧文件描述符oldfd没有关闭。虽然旧文件描述符和新创建的文件描述符可以交换使用,但一般情况下需要首先关闭一个。系统调用dup()使用的是号码最小的空闲的文件描述符。
<P>再看下面的程序:<BR>..<BR>childpid=fork();<BR>if(childpid==0)<BR>{<BR>/*Close up
standard input of the child*/<BR>close(0);<BR>/*Dup licate the input side of
pipe to
stdin*/<BR>dup(fd[0]);<BR>execlp("sort","sort",NULL);<BR>.<BR>}<BR>
因为文件描述符0(stdin)被关闭,所以dup()把管道的输入描述符复制到它的标准输入中。这样我们可以调用execlp(),使用sort程序覆盖子进程的正文段。因为新创建的程序从它的父进程中继承了标准输入/输出流,所以它实际上继承了管道的输入端作为它的标准输入端。现在,最初的父进程送往管道的任何数据都将会直接送往sort函数。
<P>
<P>
<P>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I240 name=I240></A>
<CENTER><B><FONT
size=+2>dup2()</FONT></B></CENTER><BR>系统调用:dup2();<BR>原型:intdup2(intoldfd,intnewfd);<BR>返回值:如果调用成功,返回新的文件描述符<BR>如果调用失败,返回-1:errno=EBADF(oldfd不是有效的文件描述符)<BR>EBADF(newfd超出范围)<BR>EMFILE(进程的文件描述符太多)<BR>注意dup2()将关闭旧文件描述符。
<P>
使用此系统调用,可以将close操作和文件描述符复制操作集成到一个系统调用中。另外,此系统调用保证了操作的自动进行,也就是说操作不能被其他的信号中断。这个操作将会在返回系统内核之前完成。如果使用前一个系统调用dup(),程序员不得不在此之前执行一个close()操作。请看下面的程序:<BR>..
<P>childpid=fork();<BR>if(childpid==0)<BR>{<BR>/*Close stdin,dup licate the
input side of pipe to
stdin*/<BR>dup2(0,fd[0]);<BR>execlp("sort","sort",NULL);<BR>..<BR>}
<P>
<P>
<P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -