Setting the File Descriptor Owner F_SETOWN · F_GETOWN

 

Setting the File Descriptor Owner
F_SETOWN · F_GETOWN · Process Groups · glibc Bug | Chapter 63 · TLPI
🔧 F_SETOWN
🔧 F_GETOWN
🐞 glibc Limitation
👥 Process Groups

What is a File Descriptor Owner?

When signal-driven I/O is enabled on a file descriptor, the kernel needs to know which process (or process group) should receive the SIGIO signal when I/O becomes possible. This target is called the owner of the file descriptor.

You set the owner with fcntl(fd, F_SETOWN, pid) and you can read it back with fcntl(fd, F_GETOWN). Understanding the rules for positive and negative values — and a historic glibc bug — is important for robust programs.

🔧 F_SETOWN — Setting the Owner

The general form is:

fcntl(fd, F_SETOWN, pid);

The meaning of pid depends on whether it is positive or negative:

Value of pid Interpretation Who Gets SIGIO
positive (e.g., 1234) Treated as a process ID That specific process
negative (e.g., -56) Absolute value treated as process group ID All processes in that group
getpid() The most common usage — send to self The calling process itself
-getpgrp() Sends to the caller’s process group All processes in caller’s group

Common usage examples:

/* Send SIGIO to this process only */
if (fcntl(fd, F_SETOWN, getpid()) == -1)
    perror("fcntl F_SETOWN"), exit(1);

/* Send SIGIO to the entire process group of the caller */
if (fcntl(fd, F_SETOWN, -getpgrp()) == -1)
    perror("fcntl F_SETOWN"), exit(1);

Permission rules: The kernel checks if the sending process (the one that did F_SETOWN) has permission to send signals to the target process or group, following the same rules as kill().

🔧 F_GETOWN — Reading the Owner Back

You can query the current owner of a file descriptor at any time:

pid_t id;

id = fcntl(fd, F_GETOWN);
if (id == -1)
    perror("fcntl F_GETOWN");

/* Positive value = process ID */
/* Negative value = process group ID (absolute value is the group ID) */
Return Value Meaning
Positive number Process ID of the owner
Negative number Process group ID (negate it to get the actual PGID)
-1 with errno set Error occurred

Older UNIX implementations used ioctl() instead of fcntl() for this purpose. For compatibility, Linux also provides these ioctl operations:

fcntl() operation Equivalent older ioctl()
F_SETOWN FIOSETOWN or SIOCSPGRP
F_GETOWN FIOGETOWN or SIOCGPGRP

🐞 The glibc F_GETOWN Bug with Small Process Group IDs

There is a well-known limitation in how glibc handles the return value of fcntl(fd, F_GETOWN) on some Linux architectures (notably x86).

Situation Kernel Returns glibc Interprets App Sees
Owner is process ID 500 (positive) +500 Success 500 — correct ✓
Owner is PGID 500 (group) -500 Success (negative = group) -500 — correct ✓
Owner is PGID 3000 (group, < 4096) -3000 Looks like errno = 3000, returns -1 -1 with errno=3000 — WRONG ✗

Why does this happen?

The Linux kernel signals errors by returning a negative value in the range -1 to -4095 from a system call. glibc treats any value in that range as an error code: it negates the value into errno and returns -1 to the caller. The problem is that a valid process group ID of, say, 3000 is returned as -3000 by the kernel (because F_GETOWN uses negative for groups). This -3000 falls right inside the kernel’s error range, so glibc misreads it as an error.

⚠ Practical Impact: If your fd is owned by a process group whose ID is less than 4096, fcntl(fd, F_GETOWN) will incorrectly return -1 and set errno to the group ID value. This is almost impossible to detect by checking return values alone.

The Fix — F_GETOWN_EX (Linux 2.6.32+, glibc 2.11+):

Since glibc 2.11, the F_GETOWN wrapper internally uses F_GETOWN_EX to avoid this issue. F_GETOWN_EX fills a struct instead of returning a possibly-ambiguous integer:

#include <fcntl.h>
#include <stdio.h>

/* F_GETOWN_EX avoids the glibc limitation */
struct f_owner_ex owner;

if (fcntl(fd, F_GETOWN_EX, &owner) == -1) {
    perror("fcntl F_GETOWN_EX");
    exit(1);
}

/* owner.type is one of:
     F_OWNER_PID   — owner.pid is a process ID
     F_OWNER_PGRP  — owner.pid is a process group ID
     F_OWNER_TID   — owner.pid is a thread ID */

switch (owner.type) {
    case F_OWNER_PID:
        printf("Owner: process %d\n", owner.pid);
        break;
    case F_OWNER_PGRP:
        printf("Owner: process group %d\n", owner.pid);
        break;
    case F_OWNER_TID:
        printf("Owner: thread %d\n", owner.pid);
        break;
}

Similarly, F_SETOWN_EX allows you to set ownership to a specific thread (not just a process or group), which is useful in multithreaded programs:

struct f_owner_ex owner;

/* Set fd owner to current thread */
owner.type = F_OWNER_TID;
owner.pid  = gettid();   /* Linux-specific: get thread ID */

if (fcntl(fd, F_SETOWN_EX, &owner) == -1) {
    perror("fcntl F_SETOWN_EX");
}

📈 Owner Selection — Who Can Be the Owner?
Owner Type How to Specify Typical Use Case
Current process fcntl(fd, F_SETOWN, getpid()) Most common — single-process programs
Another process fcntl(fd, F_SETOWN, otherpid) Parent/child coordination
Process group fcntl(fd, F_SETOWN, -pgid) Notify entire job/session
Specific thread fcntl(fd, F_SETOWN_EX, &owner) Multithreaded servers (Linux 2.6.32+)

Key Terms

F_SETOWN F_GETOWN F_SETOWN_EX F_GETOWN_EX process group ID glibc bug f_owner_ex F_OWNER_PID F_OWNER_PGRP F_OWNER_TID FIOSETOWN SIOCSPGRP

🏫 Interview Questions — File Descriptor Owner
Q1. What does fcntl(fd, F_SETOWN, pid) do?
It sets the owner of the file descriptor. When I/O becomes possible on that fd and signal-driven I/O is enabled, the kernel sends SIGIO (or another configured signal) to the process or process group identified by pid. A positive pid is a process ID; a negative pid means the absolute value is a process group ID.
Q2. What is the difference between a positive and a negative pid in F_SETOWN?
A positive value is interpreted as a process ID and signals that specific process. A negative value’s absolute value is interpreted as a process group ID, and all processes in that group receive the signal.
Q3. Describe the glibc bug with F_GETOWN and small process group IDs.
The Linux kernel returns a negative value to indicate a process group ID from F_GETOWN. However, the kernel also uses negative values in the range -1 to -4095 to signal errors. If a process group ID is less than 4096, its negation falls in that error range. glibc misinterprets it as an error, copies the absolute value into errno, and returns -1. The application incorrectly thinks an error occurred. glibc 2.11+ fixes this by implementing F_GETOWN using F_GETOWN_EX internally.
Q4. What is F_GETOWN_EX and when should you use it?
F_GETOWN_EX is a Linux-specific fcntl operation (available from kernel 2.6.32) that retrieves the owner of a file descriptor into a struct f_owner_ex, avoiding the ambiguity of returning an integer that might clash with error codes. You should use it (or F_SETOWN_EX) when you need to set/get ownership reliably, especially with process groups, or when working with multithreaded programs where you want to direct SIGIO to a specific thread.
Q5. Can you send SIGIO to a process group rather than a single process? How?
Yes. Pass a negative value to F_SETOWN whose absolute value is the process group ID: fcntl(fd, F_SETOWN, -getpgrp()). All processes in that group will receive SIGIO when I/O is possible on the fd. However, using F_GETOWN to read back the owner may fail with the glibc bug if the group ID is less than 4096.
Q6. What were the old ioctl() equivalents of F_SETOWN and F_GETOWN?
Older UNIX systems used ioctl(fd, FIOSETOWN, &pid) or ioctl(fd, SIOCSPGRP, &pid) instead of F_SETOWN, and ioctl(fd, FIOGETOWN, &id) or ioctl(fd, SIOCGPGRP, &id) instead of F_GETOWN. Linux still supports these for backward compatibility, but modern code should use the fcntl() approach.

Next: When Is “I/O Possible” Signaled?

Terminals, pipes, FIFOs, sockets, inotify — learn the exact conditions for each file type.

Next Topic → ← Previous

Leave a Reply

Your email address will not be published. Required fields are marked *