📄 进程和线程编程.htm
字号:
很明显,第一个参数用来指定将要读取消息的队列。第二个参数代表要存储消息的消息缓冲区的地址。第三个参数是消息缓冲区的长度,不包括mtype的长度,它可以按照如下的方法计算:<BR>
msgsz=sizeof(structmymsgbuf)-sizeof(long);<BR>
第四个参数是要从消息队列中读取的消息的类型。如果此参数的值为0,那么队列中最长时间的一条消息将返回,而不论其类型是什么。<BR>如果调用中使用了IPC_NOWAIT作为标志,那么当没有数据可以使用时,调用将把ENOMSG返回到调用进程中。否则,调用进程将会挂起,直到队列中的一条消息满足msgrcv()的参数要求。如果当客户端等待一条消息的时候队列为空,将会返回EIDRM。如果进程在等待消息的过程中捕捉到一个信号,则返回EINTR。<BR>
下面就是一个从队列中读取消息的程序:
<P>intread_message(int qid,long type,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=msgrcv(qid,qbuf,length,type,0))==-1)<BR>{<BR>return(-1);<BR>}<BR>return(result);<BR>}<BR>
在成功地读取了一条消息以后,队列中的这条消息的入口将被删除。<BR>
参数msgflg中的MSG_NOERROR位提供一种额外的用途。如果消息的实际长度大于msgsz,同时使用了MSG_NOERROR,那么消息将会被截断,只有与msgsz长度相等的消息返回。一般情况下,系统调用msgrcv()会返回-1,而这条消息将会继续保存在队列中。我们可以利用这个特点编制一个程序,利用这个程序可以查看消息队列的情况,看看符合我们条件的消息是否已经到来:
<P>intpeek_message(int qid,long
type)<BR>{<BR>intresult,length;<BR>if((result=msgrcv(qid,NULL,0,type,IPC_NOWAIT))==-1)<BR>{<BR>if(errno==E2BIG)<BR>return(TRUE);<BR>}<BR>return(FALSE);<BR>}<BR>
在上面的程序中,我们忽略了缓冲区的地址和长度。这样,系统调用将会失败。尽管如此,我们可以检查返回的E2BIG值,它说明符合条件的消息确实存在。
<P>
<P>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I250 name=I250></A>
<CENTER><B><FONT size=+2>msgctl()</FONT></B></CENTER><BR>系统调用msgctl()
<P> 下面我们继续讨论如何使用一个给定的消息队列的内部数据结构。我们可以使用系统调用msgctl (
)来控制对消息队列的操作。
<P>系统调用: msgctl( ) ;<BR>调用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds
*buf );<BR>返回值: 0 ,如果成功。<BR>- 1,如果失败:errno = EACCES (没有读的权限同时cmd 是IPC_STAT
)<BR>EFAULT (buf 指向的地址无效)<BR>EIDRM (在读取中队列被删除)<BR>EINVAL (msgqid无效, 或者msgsz 小于0
)<BR>EPERM (IPC_SET或者IPC_RMID
命令被使用,但调用程序没有写的权限)<BR>下面我们看一下可以使用的几个命令:<BR>IPC_STAT<BR>读取消息队列的数据结构msqid_ds,并将其存储在b
u
f指定的地址中。<BR>IPC_SET<BR>设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。<BR>IPC_RMID<BR>从系统内核中移走消息队列。<BR>
我们在前面讨论过了消息队列的数据结构(msqid_ds)。系统内核中为系统中的每一个消息队列保存一个此数据结构的实例。通过使用IPC_STAT命令,我们可以得到一个此数据结构的副本。下面的程序就是实现此函数的过程:
<P>int get_queue_ds( int qid, struct msgqid_ds *qbuf )<BR>{<BR>if( msgctl( qid,
IPC_STAT, qbuf) == -1)<BR>{<BR>return(-1);<BR>}<BR>return(0);<BR>}
<P>
如果不能复制内部缓冲区,调用进程将返回-1。如果调用成功,则返回0。缓冲区中应该包括消息队列中的数据结构。<BR>
消息队列中的数据结构中唯一可以改动的元素就是ipc_perm。它包括队列的存取权限和关于队列创建者和拥有者的信息。你可以改变用户的id、用户的组id以及消息队列的存取权限。<BR>
下面是一个修改队列存取模式的程序:
<P>int change_queue_mode(int qid, char *mode )<BR>{<BR>struct msqid_ds
tmpbuf;<BR>/* Retrieve a current copy of the internal data structure
*/<BR>get_queue_ds( qid, &tmpbuf);<BR>/* Change the permissions using an old
trick */<BR>sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);<BR>/* Update the
internal data structure */<BR>if( msgctl( qid, IPC_SET, &tmpbuf) ==
-1)<BR>{<BR>return(-1);<BR>}<BR>return(<BR>}
<P> 我们通过调用get_queue_ds来读取队列的内部数据结构。然后,我们调用sscanf(
)修改数据结构msg_perm中的mode
成员的值。但直到调用msgctl()时,权限的改变才真正完成。在这里msgctl()使用的是IPC_SET命令。<BR>
最后,我们使用系统调用msgctl ( )中的IPC_RMID命令删除消息队列:
<P>int remove_queue(int qid )<BR>{<BR>if( msgctl( qid, IPC_RMID, 0) ==
-1)<BR>{<BR>return(-1);<BR>}<BR>return(0);<BR>}<BR>};
<P>
<P>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I251 name=I251></A>
<CENTER><B><FONT size=+2>信号量</FONT></B></CENTER><BR>
信号量是一个可以用来控制多个进程存取共享资源的计数器。它经常作为一种锁定机制来防止当一个进程正在存取共享资源时,另一个进程也存取同一资源。下面先简要地介绍一下信号量中涉及到的数据结构。
<P>1.内核中的数据结构semid_ds<BR>和消息队列一样,系统内核为内核地址空间中的每一个信号量集都保存了一个内部的数据结构。数据结构的原型是semid_ds。它是在linux/sem.h中做如下定义的:<BR>/*One
semid data structure for each set of semaphores in the
system.*/<BR>structsemid_ds{<BR>structipc_permsem_perm;/*permissions..seeipc.h*/<BR>time_tsem_otime;/*last
semop time*/<BR>time_tsem_ctime;/*last change time*/<BR>structsem*sem_base;/*ptr
to first semaphore in
array*/<BR>structwait_queue*eventn;<BR>structwait_queue*eventz;<BR>structsem_undo*undo;/*undo
requestson this array*/<BR>ushortsem_nsems;/*no. of semaphores in
array*/<BR>};<BR>sem_perm是在linux/ipc.h定义的数据结构ipc_perm的一个实例。它保存有信号量集的存取权限的信息,以及信号量集创建者的有关信息。<BR>sem_otime最后一次semop()操作的时间。<BR>sem_ctime最后一次改动此数据结构的时间。<BR>sem_base指向数组中第一个信号量的指针。<BR>sem_undo数组中没有完成的请求的个数。<BR>sem_nsems信号量集(数组)中的信号量的个数。
<P>2.内核中的数据结构sem<BR>在数据结构semid_ds中包含一个指向信号量数组的指针。此数组中的每一个元素都是一个<BR>数据结构sem。它也是在linux/sem.h中定义的:<BR>/*One
semaphore structure for each semaphore in the
system.*/<BR>structsem{<BR>shortsempid;/*pid of las
toperation*/<BR>ushortsemval;/*current value*/<BR>ushortsemncnt;/*num procs
awaiting increase in semval*/<BR>ushortsemzcnt;/*num procs awaiting
semval=0*/<BR>};<BR>sem_pid最后一个操作的PID(进程ID)。<BR>sem_semval信号量的当前值。<BR>sem_semncnt等待资源的进程数目。<BR>sem_semzcnt等待资源完全空闲的进程数目。
<P>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I252 name=I252></A>
<CENTER><B><FONT size=+2>semget()</FONT></B></CENTER><BR>
我们可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集:
<P>系统调用:semget();<BR>原型:intsemget(key_t key,int nsems,int
semflg);<BR>返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1:errno=EACCESS(没有权限)<BR>EEXIST(信号量集已经存在,无法创建)<BR>EIDRM(信号量集已经删除)<BR>ENOENT(信号量集不存在,同时没有使用IPC_CREAT)<BR>ENOMEM(没有足够的内存创建新的信号量集)<BR>ENOSPC(超出限制)
<P>
系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是在linux/sem.h中定义的:
<P>#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/<BR>下面是一个打开和创建信号量集的程序:<BR>intopen_semaphore_set(key_t
keyval,int
numsems)<BR>{<BR>intsid;<BR>if(!numsems)<BR>return(-1);<BR>if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)<BR>{<BR>return(-1);<BR>}<BR>return(sid);<BR>}<BR>};
<P>
<P>
<P><BR>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I253 name=I253></A>
<CENTER><B><FONT
size=+2>semop()</FONT></B></CENTER><BR>系统调用:semop();<BR>调用原型:int semop(int
semid,struct sembuf*sops,unsign
ednsops);<BR>返回值:0,如果成功。-1,如果失败:errno=E2BIG(nsops大于最大的ops数目)<BR>EACCESS(权限不够)<BR>EAGAIN(使用了IPC_NOWAIT,但操作不能继续进行)<BR>EFAULT(sops指向的地址无效)<BR>EIDRM(信号量集已经删除)<BR>EINTR(当睡眠时接收到其他信号)<BR>EINVAL(信号量集不存在,或者semid无效)<BR>ENOMEM(使用了SEM_UNDO,但无足够的内存创建所需的数据结构)<BR>ERANGE(信号量值超出范围)
<P>
第一个参数是关键字值。第二个参数是指向将要操作的数组的指针。第三个参数是数组中的操作的个数。参数sops指向由sembuf组成的数组。此数组是在linux/sem.h中定义的:
<P>/*semop systemcall takes an array of
these*/<BR>structsembuf{<BR>ushortsem_num;/*semaphore index in
array*/<BR>shortsem_op;/*semaphore operation*/<BR>shortsem_flg;/*operation
flags*/<BR>sem_num将要处理的信号量的个数。<BR>sem_op要执行的操作。<BR>sem_flg操作标志。
<P>
如果sem_op是负数,那么信号量将减去它的值。这和信号量控制的资源有关。如果没有使用IPC_NOWAIT,那么调用进程将进入睡眠状态,直到信号量控制的资源可以使用为止。如果sem_op是正数,则信号量加上它的值。这也就是进程释放信号量控制的资源。最后,如果sem_op是0,那么调用进程将调用sleep(),直到信号量的值为0。这在一个进程等待完全空闲的资源时使用。
<P>
<P>
<P><BR>
<CENTER><A
href="http://www.huihoo.com/joyfire.net/11.html#Content">[目录]</A></CENTER>
<HR>
<BR><A id=I254 name=I254></A>
<CENTER><B><FONT
size=+2>semctl()</FONT></B></CENTER><BR>系统调用:semctl();<BR>原型:int semctl(int
semid,int semnum,int cmd,union
semunarg);<BR>返回值:如果成功,则为一个正数。<BR>如果失败,则为-1:errno=EACCESS(权限不够)<BR>EFAULT(arg指向的地址无效)<BR>EIDRM(信号量集已经删除)<BR>EINVAL(信号量集不存在,或者semid无效)<BR>EPERM(EUID没有cmd的权利)<BR>ERANGE(信号量值超出范围)
<P>
系统调用semctl用来执行在信号量集上的控制操作。这和在消息队列中的系统调用msgctl是十分相似的。但这两个系统调用的参数略有不同。因为信号量一般是作为一个信号量集使用的,而不是一个单独的信号量。所以在信号量集的操作中,不但要知道IPC关键字值,也要知道信号量集中的具体的信号量。这两个系统调用都使用了参数cmd,它用来指出要操作的具体命令。两个系统调用中的最后一个参数也不一样。在系统调用msgctl中,最后一个参数是指向内核中使用的数据结构的指针。我们使用此数据结构来取得有关消息队列的一些信息,以及设置或者改变队列的存取权限和使用者。但在信号量中支持额外的可选的命令,这样就要求有一个更为复杂的数据结构。<BR>系统调用semctl()的第一个参数是关键字值。第二个参数是信号量数目。
<P> 参数cmd中可以使用的命令如下:<BR>
·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。<BR>
·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。<BR>
·IPC_RMID将信号量集从内存中删除。<BR>
·GETALL用于读取信号量集中的所有信号量的值。<BR>
·GETNCNT返回正在等待资源的进程数目。<BR>
·GETPID返回最后一个执行semop操作的进程的PID。<BR>
·GETVAL返回信号量集中的一个单个的信号量的值。<BR>
·GETZCNT返回这在等待完全空闲的资源的进程数目。<BR>
·SETALL设置信号量集中的所有的信号量的值。<BR> ·SETVAL设置信号量集中的一个单独的信号量的值。
<P> 参数arg代表一个semun的实例。semun是在linux/sem.h中定义的:<BR>/*arg for
semctl systemcalls.*/<BR>unionsemun{<BR>intval;/*value for
SETVAL*/<BR>structsemid_ds*buf;/*buffer for
IPC_STAT&IPC_SET*/<BR>ushort*array;/*array for
GETALL&SETALL*/<BR>structseminfo*__buf;/*buffer for
IPC_INFO*/<BR>void*__pad;
<P>
val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。<BR>
下面的程序返回信号量的值。当使用GETVAL命令时,调用中的最后一个参数被忽略:
<P>intget_sem_val(intsid,intsemnum)<BR>{<BR>return(semctl(sid,semnum,GETVAL,0));<BR>}
<P> 下面是一个实际应用的例子:
<P>#defineMAX_PRINTERS5<BR>printer_usage()<BR>{<BR>int
x;<BR>for(x=0;x<MAX_PRINTERS;x++)<BR>printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));<BR>}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -