This is what perldoc -f syscall says:

There's a problem with syscall(SYS_pipe()): it returns the file number of the read end of the pipe it creates, but there is no way to retrieve the file number of the other end. You can avoid this problem by using pipe instead.

However, that doesn't check out. syscall works with SYS_pipe just like with any other system call, and I'm perfectly able to retrieve both ends:

perl -e '
    require "syscall.ph";
    my $p = pack "i2";
    syscall SYS_pipe(), $p;
    print join(",", unpack "i2", $p), "\n"

That was on linux, it's the same on openbsd and solaris provided that you take care of some differences (on solaris, the system call is actually pipe2(2), so syscall 42, $p, 0).

A comment in fs/pipe.c in the linux kernel source says:

 * sys_pipe() is the normal C calling standard for creating
 * a pipe. It's not the way Unix traditionally does this, though.

So what was that "traditional" way? And are there any modern systems where that's still the case?

  • Linux has amazing git commit messages. In you case, the commit where this comment was introduced, contains a hint: traditional UNIX implementations often return the two file descriptors in registers (commit: github.com/torvalds/linux/commit/…) – Danila Kiver Feb 5 at 11:52

This paragraph in the Perl documentation was introduced in Perl 5.004_04 in September 1997. I’m not familiar with the way specific Unix kernels handled SYS_pipe back then; but the original implementation in Unix V6 returned two file descriptors in registers, and the library’s pipe implementation then stored those values in an array of integers. The V6 pipe(2) manpage briefly documents this:

(pipe = 42.)
sys pipe
(read file descriptor in r0)
(write file descriptor in r1)

The commit message on the Linux patch which unified the various sys_pipe implementation also mentions this:

traditional UNIX implementations often return the two file descriptors in registers

Presumably the Perl note on syscall comes from the fact that traditional SYS_pipe returns the read file descriptor in whatever register is used to return the result of a function (r0 above, AX/EAX/RAX on PCs, etc.), which is accessible from Perl code, but you wouldn’t be able to read the value returned in r1.

I don’t know of any modern system where the file descriptors are still returned in this fashion. It’s not clear to me how many Unix systems behaved in this way in 1997 either; I get the impression at least Solaris (2.6) didn’t.

  • Many thanks for sharing a link to unix-history-repo, that's definitely a "must have" for my favorites... :) – Danila Kiver Feb 6 at 19:17
  • Thanks. I did some research too: at least on svr4, pipe() wasn't the only syscall that returned more than one value; another one was fork(), which was returning the pid in eax (r1), and a child/parent flag in edx (r2). getuid() was returning both the ruid and the euid. same with getgid(). When I have time, I'll submit a bug report changing that doc snippet to something using linux' clone ;-/ (as an example of a nasty system call) – Căcărău Feb 7 at 18:37
  • As to it being mentioned in the linux kernel, it probably has to do with their (pipe?) dream of supporting running commercial unix programs unmodified via personality(2); linux has never used that syscall convention. – Căcărău Feb 7 at 18:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.