waba has asked for the wisdom of the Perl Monks concerning the following question:

A co-worker came to me today with an interesting problem: he could not msgget the msqid of a message queue created by an external program. He was trying something like perl -e 'print msgget 0xdeadbeef, 0', with 0xdeadbeef being whatever ipcs -q displayed and he kept getting undef.

After trying it out myself, I finally resorted to strace and was quite puzzled by the following:

strace perl -e 'msgget( 0xdeadbeef, 0 );' ... msgget(0x80000000, 0) = 655361
As a matter of fact, on Linux x86 all keys greater than than 0x80000000 are rounded down to that value !

RTFSing, the first argument to the *get IPC functions (doio.c:1961 for perl 5.10.0) is extracted as a double and then compiler-casted into a key_t, whatever that is. As it happens to be a signed 32b integer on Linux x86 (and x86_64, as I just tested), we get this behaviour. In order to access keys which have their most significant bit set (in a unsigned integer representation), one has to reverse-engineer the system and provide a repacked, negative value.

Is this a bug or a feature? Clearly, the core issue is that the standard does not specify what a key_t is. I can quite follow the Perl implementors' logic of extracting the largest possible value and letting the compiler figure out how to convert it into a key_t. However, it is also quite confusing for users who expect to be able to use whatever ipcs -q tells them.

Replacing the SvNVx (extract a double) macro by SvIVx (extract an integer) in the Perl source code does the Right Thing on my system (both large positive and negative key values work). However, is it portable and/or a good idea enough than to warrant a post on perlbug?

# A testcase exhibiting the issue use IPC::SysV qw( IPC_CREAT IPC_RMID ); use Test::More tests => 1; use strict; use warnings; my $last_key = 0x80000000; my $i32b_key = 0xdeadbeef; # Anything > $last_key cleanup(); test(); cleanup(); sub cleanup { my $queue_id = msgget $last_key, 0; msgctl $queue_id, IPC_RMID, 0 if defined $queue_id; $queue_id = msgget $i32b_key, 0; msgctl $queue_id, IPC_RMID, 0 if defined $queue_id; return; } sub test { my $queue1_id = msgget $i32b_key, IPC_CREAT; my $queue2_id = msgget $last_key, IPC_CREAT; my $msg = sprintf "Queues 0x%x and 0x%x have the same ID 0x%x", $l +ast_key, $i32b_key, $queue1_id; isnt( $queue1_id, $queue2_id, $msg ); return; } 1;

Replies are listed 'Best First'.
Re: SysV IPC key_t handling in Perl
by zentara (Cardinal) on May 14, 2008 at 16:10 UTC
    As a matter of fact, on Linux x86 all keys greater than than 0x80000000 are rounded down to that value !

    See IPC::Msg - determine if queue is full I'm not an expert on this, but this might help.

    It may be that you are running into a kernel configuration problem, where the upper key limit must be set..... just a wild guess. SysV shared memory --pure perl may be useful to look at. Maybe you could switch to shmget instead of msgget.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

      Maybe should I have made this clearer - the issue resides only in the way Perl handles the first argument to msgget.

      If used from C, or even from Perl after patching doio.c to consider the key as an integer and not a double (see the bottom half of my previous post), this system call works just fine.

        i dealt with same problem (perl 5.8). Agree! Did you appeal to perl-developers?