XSI IPC 相似特征介绍
程序员文章站
2022-05-28 12:45:40
...
有 3 种称作 XSI IPC 的 IPC:消息队列、信号量以及共享存储器,本节先介绍它们相类似的特征,后面再分别说明它们各自的特殊功能(XSI IPC 没有使用文件系统命名空间,而是构造了它们自己的命名空间,为此也受到了一些批评)。
(一)标识符和键
这三种 IPC 结构在内核中都用一个非负整数的标识符加以引用。例如,要向一个消息队列发送消息或者从其中取消息,只需要知道其队列标识符即可。不同于文件描述符,IPC 标识符不是小的整数。当一个 IPC 结构被删除时,与这种结构相关的标识符依然会连续加一,直到达到最大整型值后才回转到 0。
标识符是 IPC 对象的内部名,为使多个合作进程能够在同一 IPC 对象上汇聚,每个 IPC 对象都与一个键相关联,作为该对象的外部名。无论何时创建一个 IPC 结构都应指定一个键,它的数据类型是基本系统数据类型 key_t,通常在 <sys/types.h> 中被定义为长整型。这个键会由内核转变成标识符。
有多种方法使客户进程和服务器进程在同一 IPC 结构上汇聚。
(1)服务器进程可以指定键 IPC_PRIVATE 来创建一个新 IPC 结构,将返回的标识符存放在某处(如一个文件)以便客户进程取用。其缺点是它需要服务器进程将整型标识符写到文件中,此后再由客户进程读该文件取得此标识符。这个键也可用于父子进程中。父进程新创建的 IPC 结构所返回的标识符可供 fork 后的子进程使用,然后子进程又可将此标识符作为 exec 函数的一个参数传给一个新程序。
(2)可以在一个公用头文件中定义一个客户进程和服务器进程都认可的键,然后服务器进程指定此键来创建一个新的 IPC 结构。这种方法的问题是该键可能已与一个 IPC 结构相结合,这时调用 msgget、semget 或 shmget 将会出错返回,服务器进程必须处理这一错误,删除已存在的 IPC 结构,然后试着再次创建。
(3)客户进程和服务器进程认同一个路径名和一个位于 0~255 之间的字符值的项目 ID,接着调用 ftok 函数将这两个值变换为一个键,然后在方法(2)中使用此键。
这里 path 参数必须应用一个现有的文件,当产生键时,只使用 id 参数的低 8 位。
ftok 函数创建的键通常是用下列方式构成的:按给定的路径名取得其 stat 结构中的部分 st_dev 和 st_ino 字段,然后再将它们与项目 ID 组合起来。如果两个路径名引用的是不同的文件,ftok 通常会为这两个路径名返回不同的键。但因为 i 节点编号和键通常都存放在长整型中,所以创建键时可能会丢失信息。这意味着,对于不同的两个路径,如果使用同一项目 ID,那么可能产生相同的键。
msget、semget 和 shmget 函数都有两个类似的参数:一个 key 和一个整型 flag。在创建新的 IPC 结构(通常由服务器进程创建)时,如果 key 是 IPC_PRIVATE 或者和当前某种类型的 IPC 结构无关,则需指明 flag 的 IPC_CREAT 标志位。为了引用一个现有队列(通常由客户进程创建),key 必须等于队列创建时指明的 key,并且不能指定 IPC_CREAT(注意,决不能指定 IPC_PRIVATE 作为键来引用一个现有队列,因为这个特殊的键总是用于创建一个新队列。为了引用一个用 IPC_PRIVATE 创建的现有队列,一定要知道相关的标识符,然后在其他 IPC 调用(如 msgsnd、msgrcv)中使用该标识符)。如果希望创建一个新的 IPC 结构,而且要确保没有引用具有同一标识符的一个现有 IPC 结构,那么必须在 flag 中同时指定 IPC_CREAT 和 IPC_EXCL 位,这之后如果 IPC 结构已经存在就会返回 EEXIST。
(二)权限结构
每个 IPC 结构都关联了一个 ipc_perm 结构,它规定了权限和所有者,至少包括下列成员:
其中的所有字段会在创建 IPC 结构时赋予初值,之后可以调用 msgctl、semctl 或 shmctl 来修改 uid、gid 和 mode 字段。要修改这些字段需要调用进程是 IPC 结构的创建者或超级用户。mode 字段的值类似于文件的访问模式值,只是 IPC 结构不存在执行权限。下表显示了每种 IPC 的 6 种权限。
(三)存在的问题
XSI IPC 的一个基本问题是:IPC 结构是在系统范围内起作用的,没有引用计数。比如,如果进程创建了一个消息队列,然后运行一段时间后终止,那么该消息队列及其内容不会被删除,它们会一直留在系统中直至发生这几个动作之一为止:由某个进程调用 msgrcv 或 msgctl 读消息或删除消息队列;或某个进程执行 ipcrm(1)命令删除消息队列;或正在自举的系统删除消息队列。相比于管道,管道会在最后一个引用管道的进程终止时被完全地删除;对比于 FIFO,虽然 FIFO 的名字在最后一个引用 FIFO 的进程终止时仍保留在系统中,直到被显式地删除,但是留在 FIFO 中的数据已被删除了。
XSI IPC 的另一个问题是:这些 IPC 结构在文件系统中没有名字,所以不能像普通文件那样访问或修改它们的属性。为此内核中增加了多个如 msgget、semop、shmat 等的系统调用。不能用 ls 和 rm 命令来查看和删除 IPC 对象,也不能用 chmod 命令修改它们的访问权限,而是应该使用 ipcs(1) 和 ipcrm(1)命令。
另外,因为这些形式的 IPC 不使用文件描述符,所以不能对它们使用如 select 和 poll之类的多路转接 I/O 函数,这使得很难一次使用一个以上这样的 IPC 结构,或者在文件或设备 I/O 中使用这样的 IPC 结构。例如,如果没有某种形式的忙等循环,就不能使一个服务器进程等待将要放在两个消息队列中任意一个中的消息。
(一)标识符和键
这三种 IPC 结构在内核中都用一个非负整数的标识符加以引用。例如,要向一个消息队列发送消息或者从其中取消息,只需要知道其队列标识符即可。不同于文件描述符,IPC 标识符不是小的整数。当一个 IPC 结构被删除时,与这种结构相关的标识符依然会连续加一,直到达到最大整型值后才回转到 0。
标识符是 IPC 对象的内部名,为使多个合作进程能够在同一 IPC 对象上汇聚,每个 IPC 对象都与一个键相关联,作为该对象的外部名。无论何时创建一个 IPC 结构都应指定一个键,它的数据类型是基本系统数据类型 key_t,通常在 <sys/types.h> 中被定义为长整型。这个键会由内核转变成标识符。
有多种方法使客户进程和服务器进程在同一 IPC 结构上汇聚。
(1)服务器进程可以指定键 IPC_PRIVATE 来创建一个新 IPC 结构,将返回的标识符存放在某处(如一个文件)以便客户进程取用。其缺点是它需要服务器进程将整型标识符写到文件中,此后再由客户进程读该文件取得此标识符。这个键也可用于父子进程中。父进程新创建的 IPC 结构所返回的标识符可供 fork 后的子进程使用,然后子进程又可将此标识符作为 exec 函数的一个参数传给一个新程序。
(2)可以在一个公用头文件中定义一个客户进程和服务器进程都认可的键,然后服务器进程指定此键来创建一个新的 IPC 结构。这种方法的问题是该键可能已与一个 IPC 结构相结合,这时调用 msgget、semget 或 shmget 将会出错返回,服务器进程必须处理这一错误,删除已存在的 IPC 结构,然后试着再次创建。
(3)客户进程和服务器进程认同一个路径名和一个位于 0~255 之间的字符值的项目 ID,接着调用 ftok 函数将这两个值变换为一个键,然后在方法(2)中使用此键。
#include <sys/ipc.h> key_t ftok(const char *path, int id); /* 返回值:若成功,返回键;否则,返回 key_t - 1 */
这里 path 参数必须应用一个现有的文件,当产生键时,只使用 id 参数的低 8 位。
ftok 函数创建的键通常是用下列方式构成的:按给定的路径名取得其 stat 结构中的部分 st_dev 和 st_ino 字段,然后再将它们与项目 ID 组合起来。如果两个路径名引用的是不同的文件,ftok 通常会为这两个路径名返回不同的键。但因为 i 节点编号和键通常都存放在长整型中,所以创建键时可能会丢失信息。这意味着,对于不同的两个路径,如果使用同一项目 ID,那么可能产生相同的键。
msget、semget 和 shmget 函数都有两个类似的参数:一个 key 和一个整型 flag。在创建新的 IPC 结构(通常由服务器进程创建)时,如果 key 是 IPC_PRIVATE 或者和当前某种类型的 IPC 结构无关,则需指明 flag 的 IPC_CREAT 标志位。为了引用一个现有队列(通常由客户进程创建),key 必须等于队列创建时指明的 key,并且不能指定 IPC_CREAT(注意,决不能指定 IPC_PRIVATE 作为键来引用一个现有队列,因为这个特殊的键总是用于创建一个新队列。为了引用一个用 IPC_PRIVATE 创建的现有队列,一定要知道相关的标识符,然后在其他 IPC 调用(如 msgsnd、msgrcv)中使用该标识符)。如果希望创建一个新的 IPC 结构,而且要确保没有引用具有同一标识符的一个现有 IPC 结构,那么必须在 flag 中同时指定 IPC_CREAT 和 IPC_EXCL 位,这之后如果 IPC 结构已经存在就会返回 EEXIST。
(二)权限结构
每个 IPC 结构都关联了一个 ipc_perm 结构,它规定了权限和所有者,至少包括下列成员:
#include <sys/ipc.h> struct ipc_perm{ uid_t uid; // owner's effective user id gid_t gid; // owner's effective group id uid_t cuit; // creator's effective user id gid_t cgid; // creator's effective group id mode_t mode; // access modes /* ... */ };
其中的所有字段会在创建 IPC 结构时赋予初值,之后可以调用 msgctl、semctl 或 shmctl 来修改 uid、gid 和 mode 字段。要修改这些字段需要调用进程是 IPC 结构的创建者或超级用户。mode 字段的值类似于文件的访问模式值,只是 IPC 结构不存在执行权限。下表显示了每种 IPC 的 6 种权限。
(三)存在的问题
XSI IPC 的一个基本问题是:IPC 结构是在系统范围内起作用的,没有引用计数。比如,如果进程创建了一个消息队列,然后运行一段时间后终止,那么该消息队列及其内容不会被删除,它们会一直留在系统中直至发生这几个动作之一为止:由某个进程调用 msgrcv 或 msgctl 读消息或删除消息队列;或某个进程执行 ipcrm(1)命令删除消息队列;或正在自举的系统删除消息队列。相比于管道,管道会在最后一个引用管道的进程终止时被完全地删除;对比于 FIFO,虽然 FIFO 的名字在最后一个引用 FIFO 的进程终止时仍保留在系统中,直到被显式地删除,但是留在 FIFO 中的数据已被删除了。
XSI IPC 的另一个问题是:这些 IPC 结构在文件系统中没有名字,所以不能像普通文件那样访问或修改它们的属性。为此内核中增加了多个如 msgget、semop、shmat 等的系统调用。不能用 ls 和 rm 命令来查看和删除 IPC 对象,也不能用 chmod 命令修改它们的访问权限,而是应该使用 ipcs(1) 和 ipcrm(1)命令。
另外,因为这些形式的 IPC 不使用文件描述符,所以不能对它们使用如 select 和 poll之类的多路转接 I/O 函数,这使得很难一次使用一个以上这样的 IPC 结构,或者在文件或设备 I/O 中使用这样的 IPC 结构。例如,如果没有某种形式的忙等循环,就不能使一个服务器进程等待将要放在两个消息队列中任意一个中的消息。
上一篇: shell 判断条件总结(转载)