﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-月光记忆-文章分类-c进程间通信</title><link>http://www.blogjava.net/sunzhong/category/41975.html</link><description>清泉响露</description><language>zh-cn</language><lastBuildDate>Sat, 24 Oct 2009 17:47:27 GMT</lastBuildDate><pubDate>Sat, 24 Oct 2009 17:47:27 GMT</pubDate><ttl>60</ttl><item><title>linux系统调用之ftok()</title><link>http://www.blogjava.net/sunzhong/articles/297960.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Mon, 12 Oct 2009 12:14:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297960.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297960.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297960.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297960.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297960.html</trackback:ping><description><![CDATA[<p>ftok原型如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;key_t ftok( char * fname, int id )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fname就时你指定的文件(存在,可以访问的)名，id是子序号。<br />
先来简单的叙述一下,如果不太清楚的话,再接着向下看:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000">ftok函数是根据pathname和id来创建一个关键字(类型为 key_t)，此关键字在创建信号量，创建消息队列的时候都需要使用。其中pathname必须是一个存在的可访问的路径或文件，id必须不得为0。</span></p>
<p>失败返回值为-1</p>
<p>ftok应用范围</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #008000">英文是这样描述的;&nbsp;generate&nbsp; a&nbsp; key_t&nbsp; type&nbsp; System V IPC key, suitable for use with msgget(2),semget(2), or shmget(2).<br />
</span><br />
系统建立IPC通讯（如消息队列、共享内存时）必须指定一个ID值。通常情况下，该id值通过ftok函数得到。</p>
<p>在一般的UNIX实现中，是将pathname指定文件的<span style="color: #ff6600"><span style="color: #ff6600">索引节点号</span></span>取出，前面加上子序号得到key_t的返回值。</p>
<p>如指定文件的索引节点号为65538，换算成16进制为0x010002，而你指定的ID值为38，换算成16进制为0x26，则最后的key_t返回值为0x26010002。<br />
&nbsp;&nbsp;&nbsp;&nbsp;查询文件索引节点号可通过命令： ls -i&nbsp;&nbsp;&nbsp;&nbsp;来查看</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;nux Programmer's Manual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FTOK(3)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
NAME<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ftok - convert a pathname and a project identifier to a System V IPC<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
SYNOPSIS<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # include &lt;sys/types.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # include &lt;sys/ipc.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key_t ftok(const char *pathname, int proj_id);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
DESCRIPTION<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The ftok function uses the identity of the file named by the given&nbsp; pathname (which must refer to an existing, accessible file) and the<br />
least significant 8 bits of proj_id (which must be nonzero) to generate a key_t type System V IPC key, suitable for use with msgget(2),<br />
semget(2), or shmget(2).<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The resulting value is the same for all pathnames that name the same&nbsp;file, when the same value of proj_id is used. The value returned should&nbsp;be different when the (simultaneously existing) files or the project&nbsp; IDs differ.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
RETURN VALUE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; On success the generated key_t value is returned. On failure -1 is&nbsp;returned, with errno indicating the error as for the stat(2) system<br />
call.</p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/297960.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-12 20:14 <a href="http://www.blogjava.net/sunzhong/articles/297960.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程间信号量解析(semaphore)</title><link>http://www.blogjava.net/sunzhong/articles/297444.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Thu, 08 Oct 2009 05:05:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297444.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297444.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297444.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297444.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;信号量又称为信号灯，它是用来协调不同进程间的数据对象的，而最主要的应用是前一节的共享内存方式的进程间通信。本质上，信号量是一个计数器，它用来记录对某个资源（如共享内存）的存取状况。一般说来，为了达到安全访问共享资源的目的,就要同步共享资源,那么总要把访问共享资源的过程放在临界代码中，能否进入临界代码正好用信号量来控制, 进程需要执行下列操作： <br />
　　 （1） 测试控制该资源的信号量。 <br />
　　 （2） 若此信号量的值为正，则允许进行使用该资源。进程将信号量减1。 <br />
　　 （3） 若此信号量为0，则该资源目前不可用，进程进入睡眠状态，直至信号量值大于0，进程被唤醒，转入步骤（1）。 <br />
　　 （4） 当进程不再使用一个信号量控制的资源时，信号量值加1。如果此时有进程正在睡眠等待此信号量，则唤醒此进程。 <br />
　 　 维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件/usr/include/linux/sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合，用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget，用以获 得一个信号量集合ID。 </p>
<p>　　 #include &lt;sys/types.h&gt; <br />
　　 #include &lt;sys/ipc.h&gt; <br />
　　 #include &lt;sys/sem.h&gt; <br />
　　 int semget(key_t key, int nsems, int flag); </p>
<p>　&nbsp; key是前面讲过的IPC结构的关键字，flag将来决定是创建新的信号量集合，还是引用一个现有的信号量集合。nsems是该集合中的信号量个数。如果是创建新 集合（一般在服务器中），则必须指定nsems；如果是引用一个现有的信号量集合（一般在客户机中）则将nsems指定为0。 <br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 每个信号量至少包含如下数据结构<br />
&nbsp;&nbsp;&nbsp;&nbsp; struct sem {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;short&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sempid;/* pid of last operaton */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;semval;/* current value */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;semncnt;/* num procs awaiting increase in semval */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;semzcnt;/* num procs awaiting semval = 0 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p><br />
　　 semctl函数用来对信号量进行操作。 <br />
　　 int semctl(int semid, int semnum, int cmd, union semun arg); <br />
　　 不同的操作是通过cmd参数来实现的，在头文件sem.h中定义了7种不同的操作，实际编程时可以参照使用。 <br />
　　 <br />
&nbsp;&nbsp;&nbsp;&nbsp; semop函数自动执行信号量集合上的操作数组。 <br />
　　 int semop(int semid, struct sembuf semoparray[], size_t nops); <br />
　　 semoparray是一个指针，它指向这样一个数组:元素用来描述对semid代表的信号量集合中第几个信号进行怎么样的操作。nops规定该数组中操作的数量。 </p>
<p>　　 下面，我们看一个具体的例子，它创建一个特定的IPC结构的关键字和一个信号量，建立此信号量的索引，修改索引指向的信号量的值，最后我们清除信号量。在下面的代码中，函数ftok生成我们上文所说的唯一的IPC关键字。 </p>
<p>#include &lt;stdio.h&gt; <br />
#include &lt;sys/types.h&gt; <br />
#include &lt;sys/sem.h&gt; <br />
#include &lt;sys/ipc.h&gt; <br />
void main() {&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;key_t unique_key; /* 定义一个IPC关键字*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int id;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;struct sembuf lock_it;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;union semun options;//存放semctl要使用的入参或者代表结果的出参&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int i; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;unique_key = ftok(".", 'a'); /* 生成关键字，字符'a'是一个随机种子*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/* 创建一个新的信号量集合*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("semaphore id=%d\n", id);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;options.val = 1; /*设置变量值*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/ </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;/*打印出信号量的值*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;i = semctl(id, 0, GETVAL, 0);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("value of semaphore at index 0 is %d\n", i); </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;/*下面重新设置信号量*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;lock_it.sem_num = 0; /*设置哪个信号量*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;lock_it.sem_op = -1; /*定义操作*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (semop(id, &amp;lock_it, 1) == -1) {&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("can not lock semaphore.\n");&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;} </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;i = semctl(id, 0, GETVAL, 0);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("value of semaphore at index 0 is %d\n", i); </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;/*清除信号量*/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;semctl(id, 0, IPC_RMID, 0); <br />
} </p>
<p>semget()</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 可以使用系统调用semget()创建一个新的信号量集，或者存取一个已经存在的信号量集： </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;系统调用：semget();<br />
&nbsp;&nbsp;&nbsp;&nbsp;原型：int&nbsp;&nbsp;&nbsp;&nbsp;semget(key_t key,int nsems,int semflg);<br />
&nbsp;&nbsp;&nbsp;&nbsp;返回值：如果成功，则返回信号量集的IPC标识符。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果失败，则返回-1：errno=EACCESS(没有权限)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EEXIST(信号量集已经存在，无法创建)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EIDRM(信号量集已经删除)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENOENT(信号量集不存在，同时没有使用IPC_CREAT)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENOMEM(没有足够的内存创建新的信号量集)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENOSPC(超出限制)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;系统调用semget()的第一个参数是关键字值（一般是由系统调用ftok()返回的）。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在，则创建信号量集。如果单独使用IPC_CREAT，则semget()要么返回新创建的信号量集的标识符，要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用，则要么返回新创建的信号量集的标识符，要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是在linux/sem.h中定义的：<br />
#defineSEMMSL32/*&lt;=512maxnumofsemaphoresperid*/<br />
下面是一个打开和创建信号量集的程序：<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;open_semaphore_set(key_t keyval,int numsems)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;intsid;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!numsems)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(-1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(-1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(sid);<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
==============================================================<br />
semop()</p>
<p>系统调用：semop();<br />
调用原型：int semop(int semid,struct sembuf*sops,unsign ednsops);<br />
返回值：0，如果成功。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-1，如果失败：errno=E2BIG(nsops大于最大的ops数目)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EACCESS(权限不够)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EAGAIN(使用了IPC_NOWAIT，但操作不能继续进行)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EFAULT(sops指向的地址无效)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EIDRM(信号量集已经删除)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EINTR(当睡眠时接收到其他信号)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EINVAL(信号量集不存在,或者semid无效)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENOMEM(使用了SEM_UNDO,但无足够的内存创建所需的数据结构)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERANGE(信号量值超出范围) </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第一个参数是关键字值。第二个参数是指向将要操作的数组的指针。第三个参数是数组中的操作的个数。参数sops指向由sembuf组成的数组。此数组是在linux/sem.h中定义的：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*semop systemcall takes an array of these*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;&nbsp;&nbsp;&nbsp;sembuf{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;sem_num;/*semaphore index in array*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;short&nbsp;&nbsp;&nbsp;&nbsp;sem_op;/*semaphore operation*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;short&nbsp;&nbsp;&nbsp;&nbsp;sem_flg;/*operation flags*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem_num将要处理的信号量的index。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem_op要执行的操作。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem_flg操作标志。<br />
&nbsp;&nbsp;&nbsp; 如果sem_op是负数，那么信号量将减去它的值。这和信号量控制的资源有关。如果没有使用IPC_NOWAIT，那么调用进程将进入睡眠状态，直到信号量控制的资源可以使用为止。如果sem_op是正数，则信号量加上它的值。这也就是进程释放信号量控制的资源。最后，如果sem_op是0，那么调用进程将调用sleep()，直到信号量的值为0。这在一个进程等待完全空闲的资源时使用。<br />
===============================================================<br />
semctl()</p>
<p>系统调用：semctl();<br />
原型：int semctl(int semid,int semnum,int cmd,union semunarg);<br />
返回值： 如果成功，则为一个正数。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果失败，则为-1：errno=EACCESS(权限不够)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EFAULT(arg指向的地址无效)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EIDRM(信号量集已经删除)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EINVAL(信号量集不存在，或者semid无效)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EPERM(EUID没有cmd的权利)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERANGE(信号量值超出范围) </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;系统调用semctl()的第一个参数指定要对哪个信号量集合操作。第二个参数是指定被操作的信号量相对于集合的偏移量。第三个参数是要执行的操作,第四个操作要使用的数据.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参数cmd中可以使用的命令如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;IPC_STAT&nbsp;&nbsp;&nbsp;&nbsp;读取一个semid指定的信号量集合的数据结构 将其存储在semunarg中的buf成员中。因为是copy本身信息,所以参数semnum无效.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;IPC_SET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用semunarg的buf成员来设置信号量集的数据结构。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;IPC_RMID&nbsp;&nbsp;&nbsp;&nbsp;将信号量集从内存中删除。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;GETALL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用于读取信号量集中的所有信号量的值到semunarg的array成员中。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;GETNCNT&nbsp;&nbsp;&nbsp;&nbsp;返回正在等待资源(被该信号阻塞)的进程数目。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;GETPID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;返回该信号量上最后一个执行semop操作的进程的PID。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;GETVAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;返回信号量集中的一个单个的信号量的值。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;GETZCNT&nbsp;&nbsp;&nbsp;&nbsp;返回这在等待该信号量完全空闲的资源的进程数目。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;SETALL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用semumarg的array成员来设置信号量集中的所有的信号量的值。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;SETVAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用semnumarg的val成员设置信号量集中的一个单独的信号量的值。<br />
&nbsp;&nbsp;&nbsp; 参数semnumarg代表一个semun的实例。semun是在linux/sem.h中定义的：<br />
&nbsp;&nbsp;&nbsp;&nbsp;/*arg for semctl systemcalls.*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;union&nbsp;&nbsp;&nbsp;&nbsp;semun{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;val;/*value for SETVAL*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;&nbsp;&nbsp;&nbsp;semid_ds&nbsp;&nbsp;*&nbsp;buf;/*buffer for IPC_STAT&amp;IPC_SET*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ushort * array;/*array for GETALL&amp;SETALL*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;&nbsp;&nbsp;&nbsp;seminfo* __buf;/*buffer for IPC_INFO*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是一个union 变量 ,也就是说任何是否都只能使用它的一个成员,至于使用哪个成员,就要cmd参数指定什么操作了.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针,代表一个数组,是存放要使用的入参,或者是操作结果的出参.<br />
&nbsp;&nbsp;&nbsp; 下面的程序返回信号量的值。当使用GETVAL命令时，调用中的最后一个参数被忽略：<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;get_sem_val(intsid,intsemnum)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(semctl(sid,semnum,GETVAL,0));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp; 下面是一个实际应用的例子：<br />
&nbsp;&nbsp;&nbsp;&nbsp;#define&nbsp;&nbsp;&nbsp;&nbsp;MAX_PRINTERS&nbsp;&nbsp;&nbsp;&nbsp;5<br />
&nbsp;&nbsp;&nbsp;&nbsp;void printer_usage()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int x;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(x=0;x&lt;MAX_PRINTERS;x++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp; 下面的程序可以用来初始化一个新的信号量值：<br />
&nbsp;&nbsp;&nbsp;&nbsp;void init_semaphore(int sid,int semnum,int initval)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;union semun&nbsp;&nbsp;&nbsp;&nbsp;semopts;<br />
&nbsp;&nbsp;&nbsp;&nbsp;semopts.val=initval;<br />
&nbsp;&nbsp;&nbsp;&nbsp;semctl(sid,semnum,SETVAL,semopts);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/297444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-08 13:05 <a href="http://www.blogjava.net/sunzhong/articles/297444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个包含了信号、信号量、共享内存的例子(转)</title><link>http://www.blogjava.net/sunzhong/articles/297436.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Thu, 08 Oct 2009 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297436.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297436.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297436.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297436.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297436.html</trackback:ping><description><![CDATA[<p>#include &lt;stdio.h&gt;<br />
#include &lt;signal.h&gt;<br />
#include &lt;sys/types.h&gt;<br />
#include &lt;sys/ipc.h&gt;<br />
#include &lt;sys/shm.h&gt;<br />
#include &lt;sys/sem.h&gt;<br />
#define SHMKEY1 (key_t)0x10<br />
#define SHMKEY2 (key_t)0x15</p>
<p>#define SEMKEY&nbsp; (key_t)0x20</p>
<p>#define SIZ 5*BUFSIZ</p>
<p><br />
#define IFLAGS (IPC_CREAT|IPC_EXCL)<br />
#define ERR ((struct databuf*)-1)<br />
//p1,v1是针对第一个信号进行的操作<br />
struct sembuf p1 = {0, -1, 0};<br />
struct sembuf v1 = {0, 1, 0};<br />
//p2,v1是针对第二个信号进行的操作<br />
struct sembuf p2 = {1, -1, 0};<br />
struct sembuf v2 = {1, 1, 0};<br />
//表示两个内存共享区和一个信号量集合<br />
static int shmid1, shmid2, semid;<br />
//作为暂时存放数据的结构体<br />
struct databuf<br />
{<br />
&nbsp;&nbsp;&nbsp; int d_nread;<br />
&nbsp;&nbsp;&nbsp; char d_buf[SIZ];<br />
};</p>
<p><br />
void fatal(char *mes)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror(mes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (1);<br />
}<br />
/**创建两个共享内存,并且分别shmat给两个参数*/<br />
void getshm (struct databuf **p1, struct databuf **p2)<br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建共享内存<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((shmid1=shmget(SHMKEY1, sizeof(struct databuf), 0600|IFLAGS)) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal ("shmget");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((shmid2=shmget(SHMKEY2, sizeof(struct databuf), 0600|IFLAGS)) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal ("shmget");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //映射<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((*p1=(struct databuf*)(shmat(shmid1, 0, 0))) == ERR)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("shmat");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((*p2=(struct databuf*)(shmat(shmid2, 0, 0))) == ERR)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("shmat");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>int getsem()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获取一个长度为2的信号量集合;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((semid=semget(SEMKEY, 2, 0600|IFLAGS)) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("segmet");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化信号量,假定第一个信号灯没有资源<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (semctl(semid, 0, SETVAL, 0) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal ("semctl");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //第二个有信息,如果两个信号都没有资源,将会出现死锁<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //书上该处有问题<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (semctl(semid, 1, SETVAL, 1) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal ("semctl");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (semid);<br />
}<br />
//回收共享两个内存区和一个信号量集合<br />
void myremove()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (shmctl(shmid1, IPC_RMID, NULL) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("shmctl");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (shmctl(shmid2, IPC_RMID, NULL) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("shmctl");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (semctl(semid, 0, IPC_RMID, NULL) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("semctl");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />
}</p>
<p>void reader(int semid, struct databuf *buf1, struct databuf *buf2)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short val= semctl(semid,0,GETVAL,null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(val&lt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid,&amp;v1,1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //申请第一个信号灯资源<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;p1, 1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //从键盘读入数据,放在buf1-&gt;d_buf，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //read第一个参数是文件描述符，其中0表示从终端输入：如键盘<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf1-&gt;d_nread = read(0, buf1-&gt;d_buf, SIZ);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //v操作,释放第一个信号灯资源&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;v1, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //p操作,申请第二个信号灯资源,若没有资源,则阻塞进程直到有资源.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;p2, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf2-&gt;d_nread = read(0, buf2-&gt;d_buf, SIZ);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //v操作,释放第二个信号灯资源<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;v2, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}</p>
<p>void writer(int semid, struct databuf *buf1, struct databuf *buf2)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //p操作,等待第一个信号的资源&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;p1, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //1代表输出到屏幕<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(1, buf1-&gt;d_buf, buf1-&gt;d_nread);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放第一个信号灯资源<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;v1, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //p操作,申请第二个信号灯资源,若没有资源,则阻塞进程直到有资源.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;p2, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(1, buf2-&gt;d_buf, buf2-&gt;d_nread);<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semop(semid, &amp;v2, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
}</p>
<p>int main()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int semid, pid;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct databuf *buf1, *buf2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; semid = getsem();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getshm (&amp;buf1, &amp;buf2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch(pid=fork())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case -1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fatal("fork");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 0:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer(semid, buf1, buf2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在主进程中把中断程序的ctrl+c和myremove函数建立联系,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //来回收系统资源:共享内存区,信号量.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal (SIGINT, myremove);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reader(semid, buf1, buf2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />
}</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/297436.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-08 11:19 <a href="http://www.blogjava.net/sunzhong/articles/297436.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 进程间通信 - 共享内存shmget方式(转)</title><link>http://www.blogjava.net/sunzhong/articles/297435.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Thu, 08 Oct 2009 02:52:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297435.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297435.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297435.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297435.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297435.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间，则这些进程就都可以直接访问该共享内存区域，从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最快的方法，一个进程向共享内存区域写入了数据，共享这个内存区域的所有进程就可以立刻看到其中的内容。这块共享虚拟内存的页面，出现在每一个共享该页面的进程的页表中。但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址。 <br />
&nbsp; <br />
&nbsp;<br />
图 共享内存映射图&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img height="176" alt="" src="http://www.blogjava.net/images/blogjava_net/sunzhong/200802171203246536364.gif" width="532" border="0" /><br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;象所有的 System V IPC对象一样，对于共享内存对象的获取是由key控制。内存共享之后，对进程如何使用这块内存就不再做检查。它们必须依赖于其它机制，比如System V的信号灯来同步对于共享内存区域的访问(信号灯如何控制对临界代码的访问另起一篇说话)。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每一个新创建的共享内存对象都用一个shmid_kernel数据结构来表达。系统中所有的shmid_kernel数据结构都保存在shm_segs向量表中，该向量表的每一个元素都是一个指向shmid_kernel数据结构的指针。<br />
<span style="color: #ff6600">shm_segs向量表的定义如下：<br />
struct shmid_kernel *shm_segs[SHMMNI];</span><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; SHMMNI为128，表示系统中最多可以有128个共享内存对象。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; <span style="color: #ff6600">数据结构shmid_kernel的定义如下：<br />
</span><span style="color: #ff6600">&nbsp;&nbsp;&nbsp;&nbsp;struct shmid_kernel<br />
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct shmid_ds u;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* the following are private */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned long shm_npages;&nbsp; /* size of segment (pages) */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned long *shm_pages;&nbsp; /* array of ptrs to frames -&gt; SHMMAX */&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct vm_area_struct *attaches;&nbsp; /* descriptors for attaches */<br />
&nbsp;&nbsp;&nbsp;&nbsp;};</span><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 其中：<br />
&nbsp;&nbsp;&nbsp;&nbsp;shm_pages代表该共享内存对象的所占据的内存页面数组，数组里面的每个元素当然是每个内存页面的起始地址.<br />
&nbsp;&nbsp;&nbsp; shm_npages则是该共享内存对象占用内存页面的个数，以页为单位。这个数量当然涵盖了申请空间的最小整数倍.<br />
&nbsp;&nbsp;&nbsp;<span style="color: #00ff00">&nbsp;(A new shared memory segment,&nbsp; with size&nbsp; equal to the value of size rounded up to a multiple of PAGE_SIZE)</span><br />
&nbsp;&nbsp;&nbsp; shmid_ds是一个数据结构，它描述了这个共享内存区的认证信息，字节大小，最后一次粘附时间、分离时间、改变时间，创建该共享区域的进程，最后一次对它操作的进程，当前有多少个进程在使用它等信息。<br />
<span style="color: #ff6600">&nbsp;&nbsp;&nbsp;&nbsp;其定义如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;struct shmid_ds {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct ipc_perm shm_perm;&nbsp;&nbsp; /* operation perms */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int shm_segsz;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* size of segment (bytes) */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__kernel_time_t shm_atime;&nbsp; /* last attach time */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__kernel_time_t shm_dtime;&nbsp; /* last detach time */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__kernel_time_t shm_ctime;&nbsp; /* last change time */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__kernel_ipc_pid_t shm_cpid; /* pid of creator */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__kernel_ipc_pid_t shm_lpid; /* pid of last operator */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned short shm_nattch;&nbsp;&nbsp; /* no. of current attaches */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned short shm_unused;&nbsp;&nbsp; /* compatibility */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void *shm_unused2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* ditto - used by DIPC */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void *shm_unused3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* unused */<br />
&nbsp;&nbsp;&nbsp;&nbsp;};</span><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; attaches描述被共享的物理内存对象所映射的各进程的虚拟内存区域。每一个希望共享这块内存的进程都必须通过系统调用将其关联（attach）到它的虚拟内存中。这一过程将为该进程创建了一个新的描述这块共享内存的vm_area_struct数据结构。创建时可以指定共享内存在它的虚拟地址空间的位置，也可以让Linux自己为它选择一块足够的空闲区域。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个新的vm_area_struct结构是维系共享内存和使用它的进程之间的关系的,所以除了要关联进程信息外，还要指明这个共享内存数据结构shmid_kernel所在位置; 另外,便于管理这些经常变化的vm_area_struct,所以采取了链表形式组织这些数据结构,链表由attaches指向,同时 vm_area_struct数据结构中专门提供了两个指针：vm_next_shared和 vm_prev_shared，用于连接该共享区域在使用它的各进程中所对应的vm_area_struct数据结构。&nbsp;<br />
<br />
图 System V IPC 机制 - 共享内存&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; <img style="width: 489px; height: 305px" height="305" alt="" src="http://www.blogjava.net/images/blogjava_net/sunzhong/200802171203246581068.gif" width="489" border="0" /></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Linux为共享内存提供了四种操作。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. 共享内存对象的创建或获得。与其它两种IPC机制一样，进程在使用共享内存区域以前，必须通过系统调用sys_ipc （call值为SHMGET）创建一个键值为key的共享内存对象，或获得已经存在的键值为key的某共享内存对象的引用标识符。以后对共享内存对象的访问都通过该引用标识符进行。对共享内存对象的创建或获得由函数sys_shmget完成，其定义如下：<br />
int sys_shmget (key_t key, int size, int shmflg)<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 这里key是表示该共享内存对象的键值，size是该共享内存区域的大小（以字节为单位），shmflg是标志（对该共享内存对象的特殊要求）。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 它所做的工作如下：<br />
&nbsp;&nbsp;&nbsp; 1) 如果key == IPC_PRIVATE，则总是会创建一个新的共享内存对象。<br />
&nbsp;<span style="color: #00ff00">但是&nbsp; (The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more&nbsp;clearly show its function)<br />
</span>&nbsp;&nbsp;&nbsp; * 算出size要占用的页数，检查其合法性。<br />
&nbsp;&nbsp;&nbsp; * 申请一块内存用于建立shmid_kernel数据结构，注意这里申请的内存区域大小不包括真正的共享内存区，实际上，要等到第一个进程试图访问它的时候才真正创建共享内存区。<br />
&nbsp;&nbsp;&nbsp; * 根据该共享内存区所占用的页数，为其申请一块空间用于建立页表（每页4个字节），将页表清0。<br />
&nbsp;&nbsp;&nbsp; * 搜索向量表shm_segs，为新创建的共享内存对象找一个空位置。<br />
&nbsp;&nbsp;&nbsp; * 填写shmid_kernel数据结构，将其加入到向量表shm_segs中为其找到的空位置。<br />
&nbsp;&nbsp;&nbsp; * 返回该共享内存对象的引用标识符。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 2) 在向量表shm_segs中查找键值为key的共享内存对象，结果有三：<br />
&nbsp;&nbsp;&nbsp; * 如果没有找到，而且在操作标志shmflg中没有指明要创建新共享内存，则错误返回，否则创建一个新的共享内存对象。<br />
&nbsp;&nbsp;&nbsp; * 如果找到了，但该次操作要求必须创建一个键值为key的新对象，那么错误返回。<br />
&nbsp;&nbsp;&nbsp; * 否则，合法性、认证检查，如有错，则错误返回；否则，返回该内存对象的引用标识符。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 共享内存对象的创建者可以控制对于这块内存的访问权限和它的key是公开还是私有。如果有足够的权限，它也可以把共享内存锁定在物理内存中。<br />
&nbsp;&nbsp;&nbsp; 参见include/linux/shm.h<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 2. 关联。在创建或获得某个共享内存区域的引用标识符后，还必须将共享内存区域映射（粘附）到进程的虚拟地址空间，然后才能使用该共享内存区域。系统调用 sys_ipc（call值为SHMAT）用于共享内存区到进程虚拟地址空间的映射，而真正完成粘附动作的是函数sys_shmat，<br />
&nbsp;<br />
<span style="color: #ff6600">其定义如下：&nbsp;&nbsp;&nbsp; </span></p>
<p><span style="color: #ff6600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;sys/types.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/shm.h&gt;</span></p>
<p><span style="color: #ff6600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *shmat(int shmid, const void *shmaddr, int shmflg);</span><br />
</p>
<p><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 其中：<br />
&nbsp;&nbsp;&nbsp;&nbsp; shmid是shmget返回的共享内存对象的引用标识符；<br />
&nbsp;&nbsp;&nbsp; shmaddr用来指定该共享内存区域在进程的虚拟地址空间对应的虚拟地址；<br />
&nbsp;&nbsp;&nbsp; shmflg是映射标志；<br />
&nbsp;&nbsp;&nbsp;&nbsp;返回的是 在进程中的虚拟地址<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 该函数所做的工作如下：<br />
&nbsp;&nbsp;&nbsp; 1) 根据shmid找到共享内存对象。<br />
&nbsp;&nbsp;&nbsp; 2) 如果shmaddr为0，即用户没有指定该共享内存区域在它的虚拟空间中的位置，则由系统在进程的虚拟地址空间中为其找一块区域（从1G开始）；否则，就用shmaddr作为映射的虚拟地址。<br />
&nbsp;&nbsp; <span style="color: #00ff00">(If&nbsp; shmaddr&nbsp; is NULL, the system chooses a suitable (unused) address a他 which to attach the segment)</span><br />
&nbsp;&nbsp;&nbsp; 3) 检查虚拟地址的合法性（不能超过进程的最大虚拟空间大小—3G，不能太接近堆栈栈顶）。<br />
&nbsp;&nbsp;&nbsp; 4) 认证检查。<br />
&nbsp;&nbsp;&nbsp; 5) 申请一块内存用于建立数据结构vm_area_struct，填写该结构。<br />
&nbsp;&nbsp;&nbsp; 6) 检查该内存区域，将其加入到进程的mm结构和该共享内存对象的vm_area_struct队列中。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 共享内存的粘附只是创建一个vm_area_struct数据结构，并将其加入到相应的队列中，此时并没有创建真正的共享内存页。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 当进程第一次访问共享虚拟内存的某页时，因为所有的共享内存页还都没有分配，所以会发生一个page fault异常。当Linux处理这个page fault的时候，它找到发生异常的虚拟地址所在的vm_area_struct数据结构。在该数据结构中包含有这类共享虚拟内存的一组处理程序，其中的 nopage操作用来处理虚拟页对应的物理页不存在的情况。对共享内存，该操作是shm_nopage（定义在ipc/shm.c中）。该操作在描述这个共享内存的shmid_kernel数据结构的页表shm_pages中查找发生page fault异常的虚拟地址所对应的页表条目，看共享页是否存在（页表条目为0，表示共享页是第一次使用）。如果不存在，它就分配一个物理页，并为它创建一个页表条目。这个条目不但进入当前进程的页表，同时也存到shmid_kernel数据结构的页表shm_pages中。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 当下一个进程试图访问这块内存并得到一个page fault的时候，经过同样的路径，也会走到函数shm_nopage。此时，该函数查看shmid_kernel数据结构的页表shm_pages时，发现共享页已经存在，它只需把这里的页表项填到进程页表的相应位置即可，而不需要重新创建物理页。所以，是第一个访问共享内存页的进程使得这一页被创建，而随后访问它的其它进程仅把此页加到它们的虚拟地址空间。<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; 3. 分离。当进程不再需要共享虚拟内存的时候，它们与之分离（detach）。只要仍旧有其它进程在使用这块内存，这种分离就只会影响当前的进程，而不会影响其它进程。当前进程的vm_area_struct数据结构被从shmid_ds中删除，并被释放。当前进程的页表也被更新，共享内存对应的虚拟内存页被标记为无效。当共享这块内存的最后一个进程与之分离时，共享内存页被释放，同时，这块共享内存的shmid_kernel数据结构也被释放。<br />
&nbsp;<br />
&nbsp; 系统调用sys_ipc (call值为SHMDT) 用于共享内存区与进程虚拟地址空间的分离，而真正完成分离动作的是函数<span style="color: #ff6600">&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp; sys_shmdt，其定义如下：<br />
&nbsp;&nbsp;&nbsp; int sys_shmdt (char *shmaddr)<br />
</span>&nbsp;<br />
&nbsp;&nbsp;&nbsp; 其中shmaddr是进程要分离的共享页的开始虚拟地址。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 该函数搜索进程的内存结构中的所有vm_area_struct数据结构，找到地址shmaddr对应的一个，调用函数do_munmap将其释放。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 在函数do_munmap中，将要释放的vm_area_struct数据结构从进程的虚拟内存中摘下，清除它在进程页表中对应的页表项（可能占多个页表项）.&nbsp;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 如果共享的虚拟内存没有被锁定在物理内存中，分离会更加复杂。因为在这种情况下，共享内存的页可能在系统大量使用内存的时候被交换到系统的交换磁盘。为了避免这种情况，可以通过下面的控制操作，将某共享内存页锁定在物理内存不允许向外交换。共享内存的换出和换入，已在第3章中讨论。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 4. 控制。Linux在共享内存上实现的第四种操作是共享内存的控制（call值为SHMCTL的sys_ipc调用），它由函数sys_shmctl实现。控制操作包括获得共享内存对象的状态，设置共享内存对象的参数（如uid、gid、mode、ctime等），将共享内存对象在内存中锁定和释放（在对象的mode上增加或去除SHM_LOCKED标志），释放共享内存对象资源等。<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 共享内存提供了一种快速灵活的机制，它允许进程之间直接共享大量的数据，而无须使用拷贝或系统调用。共享内存的主要局限性是它不能提供同步，如果两个进程企图修改相同的共享内存区域，由于内核不能串行化这些动作，因此写的数据可能任意地互相混合。所以使用共享内存的进程必须设计它们自己的同步协议，如用信号灯等。 </p>
<p><span style="color: #ff6600">以下是使用共享内存机制进行进程间通信的基本操作：</span></p>
<p><span style="color: #ff6600">需要包含的头文件：</span></p>
<p><span style="color: #ff6600">#include &lt;sys/types.h&gt;</span></p>
<p><span style="color: #ff6600">#include &lt;sys/ipc.h&gt;</span></p>
<p><span style="color: #ff6600">#include &lt;sys/shm.h&gt;</span></p>
<p><span style="color: #ff6600">1.创建共享内存：</span></p>
<p><span style="color: #ff6600">&nbsp;int shmget(key_t key,int size,int shmflg);</span></p>
<p><span style="color: #ff6600">参数说明：</span></p>
<p><span style="color: #ff6600">key:用来表示新建或者已经存在的共享内存去的关键字。</span></p>
<p><span style="color: #ff6600">size：创建共享内存的大小。</span></p>
<p><span style="color: #ff6600">shmflg：可以指定的特殊标志。IPC_CREATE,IPC_EXCL以及低九位的权限。</span></p>
<p><span style="color: #ff6600">eg：</span></p>
<p><span style="color: #ff6600">int shmid;</span></p>
<p><span style="color: #ff6600">shmid=shmget(IPC_PRIVATE,4096,IPC_CREATE|IPC_EXCL|0660);</span></p>
<p><span style="color: #ff6600">if(shmid==-1)</span></p>
<p><span style="color: #ff6600">perror("shmget()");</span></p>
<p><span style="color: #ff6600">&nbsp;</span></p>
<p><span style="color: #ff6600">2.连接共享内存</span></p>
<p><span style="color: #ff6600">char *shmat(int shmid,char *shmaddr,int shmflg);</span></p>
<p><span style="color: #ff6600">参数说明</span></p>
<p><span style="color: #ff6600">shmid：共享内存的关键字</span></p>
<p><span style="color: #ff6600">shmaddr：指定共享内存出现在进程内存地址的什么位置，通常我们让内核自己决定一个合适的地址位置，用的时候设为0。</span></p>
<p><span style="color: #ff6600">shmflg：制定特殊的标志位。</span></p>
<p><span style="color: #ff6600">eg：</span></p>
<p><span style="color: #ff6600">int shmid;</span></p>
<p><span style="color: #ff6600">char *shmp;</span></p>
<p><span style="color: #ff6600">shmp=shmat(shmid,0,0);</span></p>
<p><span style="color: #ff6600">if(shmp==(char *)(-1))</span></p>
<p><span style="color: #ff6600">perror("shmat()\n");</span></p>
<p><span style="color: #ff6600">3.使用共享内存</span></p>
<p><span style="color: #ff6600">在使用共享内存是需要注意的是，为防止内存访问冲突，我们一般与信号量结合使用。</span></p>
<p><span style="color: #ff6600">4.分离共享内存：当程序不再需要共享内后，我们需要将共享内存分离以便对其进行释放，分离共享内存的函数原形如下：</span></p>
<p><span style="color: #ff6600">int shmdt(char *shmaddr);</span></p>
<p><br />
<span style="color: #ff6600">&nbsp;</span></p>
<p><span style="color: #ff6600">5. 释放共享内存</span></p>
<p><span style="color: #ff6600">int shmctl(int shmid,int cmd,struct shmid_ds *buf);</span></p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/297435.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-08 10:52 <a href="http://www.blogjava.net/sunzhong/articles/297435.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文件io的原子操作(转)</title><link>http://www.blogjava.net/sunzhong/articles/297308.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Tue, 06 Oct 2009 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297308.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297308.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297308.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297308.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297308.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 添加至一个文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (lseek(fd, 0L, 2) &lt; 0) /*position to EOF*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err_sys("lseek error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (write(fd, buff, 100) != 100) /*and write*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err_sys("write error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对单个进程而言，这段程序能正常工作，但若有多个进程时，则会产生问题。(如果此程序由多个进程同时执行，各自将消息添加到一个日记文件中，就会产生这种情况。)<br />
&nbsp;&nbsp;&nbsp;&nbsp;假定有两个独立的进程A和B，都对同一文件进行追尾的添加操作。每个进程都已打开了该文件，但未使用O _ A P P E N D标志。每个进程都有它自己的文件表项，但是共享一个v节点表项。假定进程A调用了l s e e k，它将对于进程A的该文件<br />
的当前位移量设置为1 5 0 0字节(当前文件尾端处)。然后内核切换进程使进程B运行。进程B执行l s e e k，也将其对该文件的当前位移量设置为1 5 0 0字节(当前文件尾端处)。然后B调用w r i t e，它将B的该文件的当前文件位移量增至1 6 0 0。因为该文件的长度已经增加了，所以内核对v节点中的当前文件长度更新为1 6 0 0。然后，内核又进行进程切换使进程A恢复运行。当A调用w r i t e时，就从其当前文件位移量( 1 5 0 0 )处将数据写到文件中去。这样也就代换了进程B刚写到该文件中的数据。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里的问题出在逻辑操作&#8220;定位游标到了文件尾端处，然后写&#8221;使用了两个分开的函数调用。解决问题的方法是使这两个操作(定位 , 写 )对于其他进程而言成为一个原子操作,任何时候不能被打断成分成两步执行.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为此, U N I X 提供了一种方法使这种操作成为原子操作，其方法就是在打开文件时设置O _ A P P E N D标志。正如前一节中所述，这就使内核每次对这种文件进行写之前，都将进程的当前位移量设置为该文件的最新的尾端处，于是在每次写之前就不再需要人为的调用l s e e k。<br />
<br />
&nbsp;&nbsp;&nbsp; 创建一个文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在对o p e n函数的O _ C R E AT和O _ E X C L选择项进行说明时，我们已见到了另一个有关原子操作的例子。当同时指定这两个选择项，而该文件又已经存在时， o p e n将失败。我们曾提及检查该文件是否存在以及创建该文件这两个操作是作为一个原子操作执行的。如果没有这样一个原子操作，那么可能会编写下列程序段：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((fd = open(pathname, O_WRONLY)) &lt;0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (errno == ENOENT) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((fd = creat(pathname, mode)) &lt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err_sys("creat error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err_sys("open error");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果在打开和创建之间，另一个进程创建了该文件，那么就会发生问题。如果在这两个函数调用之间，另一个进程创建了该文件，而且又向该文件写进了一些数据，那么执行这段程序中的c r e a t 时，刚写上去的数据就会被擦去。将这两者合并在一个原子操作中，此种问题也就不会产生。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一般而言，原子操作（atomic operation）指的是由多步组成的操作。如果该操作原子地执行，则或者执行完所有步，或者一步也不执行，不可能只执行所有步的一个子集.
<img src ="http://www.blogjava.net/sunzhong/aggbug/297308.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-06 14:07 <a href="http://www.blogjava.net/sunzhong/articles/297308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核用于所有I / O的数据结构(转)</title><link>http://www.blogjava.net/sunzhong/articles/297303.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Tue, 06 Oct 2009 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297303.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297303.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297303.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297303.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297303.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内核使用了三种数据结构，它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<!--startfragment -->&nbsp;&nbsp;&nbsp;&nbsp;<img style="width: 751px; height: 367px" height="367" alt="" src="http://www.blogjava.net/images/blogjava_net/sunzhong/tu1.jpg" width="751" border="0" /><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1) 每个进程在进程表中都有一个记录项，每个记录项中用fd 标志标识，可将其视为一个矢量。与每个文件描述符相关联的是：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(a) 文件描述符标志。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(b) 指向一个文件表项的指针。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(2) 内核为所有打开文件(open操作成功后)建立一个当前文件使用的记录,称为文件表项,顾名思义是放在一个文件表中的。每个文件表项包含：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(a) 文件状态标志(读、写、增写、同步、非阻塞等)。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(b) 当前文件位移量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(c) 指向该文件v节点表项的指针。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(3) 文件（或设备）（只要被某个进程打开了）都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针信息。对于大多数文件， v节点还包含了该文件的i节点（索引节点，在一篇专门介绍）。这些信息是在打开文件时从盘上读入内存的，所以所有关于文件的信息都是快速可供使用的。例如， i节点包含了文件的所有者、文件长度、文件所在的设备、指向文件在盘上所使用的实际数据块的指针等等<br />
&nbsp;&nbsp;&nbsp;&nbsp; 上面的图显示了进程的三张表之间的关系。该进程有两个不同的打开文件，一个文件打开为标准输入（文件描述符0），另一个打开为标准输出（文件描述符为1）。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果两个独立进程各自打开了同一文件，则有下面图所示的安排。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img alt="" src="http://www.blogjava.net/images/blogjava_net/sunzhong/tu2.jpg" border="0" /><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们假定第一个进程使该文件在文件描述符3上打开，而另一个进程则使此文件在文件描述符4上打开。打开此文件的每个进程都得到一个文件表项，但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是：这种安排使每个进程都有它自己的对该文件的当前位移量来记录对文件的操作情况。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;给出了这些数据结构后，现在对前面所述的操作作进一步说明。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8226; 在完成每个w r i t e后，在文件表项中的当前文件位移量即增加所写的字节数。如果这使当前文件位移量超过了当前文件长度，则在i节点表项中的当前文件长度更新成当前文件位移量（也就是该文件加长了）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8226; 如果用O _ A P P E N D标志打开了一个文件，则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有添写标志的文件执行写操作时，在文件表项中的当前文件位移量首先被更新到对应i节点表项中的文件长度。这就使得每次写的数据都添加到文件的当前尾端处。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8226; lseek函数只修改文件表项中的当前文件位移量，没有进行任何I / O操作。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8226; 若一个文件用l s e e k被定位到文件当前的尾端，则文件表项中的当前文件位移量被设置为i节点表项中的当前文件长度。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可能有多个文件描述符项指向同一文件表项。注意，文件描述符标志和文件状态标志在作用范围方面的区别，前者只用于一个进程的一个描述符，而后者则适用于指向该给定文件表项的任何进程中的所有描述符。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个进程都有它自己的文件表项，其中也有它自己的当前文件位移量。但是，当多个进程写同一文件时，则可能产生预期不到的结果。为了说明如何避免这种情况，需要理解原子操作的概念。 
<img src ="http://www.blogjava.net/sunzhong/aggbug/297303.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-06 12:34 <a href="http://www.blogjava.net/sunzhong/articles/297303.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>