kernel: timers that fire, raw ping, and the sockets X needs
alarm() and setitimer() stored a countdown nothing counted down, so no
timer has ever fired; setitimer and getitimer were also registered with
no Linux number. alarmd now posts SIGALRM on the tick, while any process
has a timer armed. busybox's ping, /bin/ping on the Alpine root, sends
every request after the first from its SIGALRM handler.
It also opens SOCK_RAW, which was EPROTOTYPE. A raw ICMP socket is now a
ping socket whose reads start at the IP header. And a blocked socket
call gives up for a signal with a handler, not only for a kill: the
alarm woke recvfrom, and the wait put it straight back to sleep.
For Xorg: AF_UNIX abstract names, which read as the empty path and so as
EADDRINUSE ("server already running"); and getsockname and getpeername,
which did not exist, so libxcb sent no cookie and the server refused
every client.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
d0941780f5
commit
a4ed556543
9 changed files with 428 additions and 28 deletions
|
|
@ -311,6 +311,7 @@ found:
|
|||
memset(p->sig_handler, 0, sizeof(p->sig_handler));
|
||||
memset(p->sig_restorer, 0, sizeof(p->sig_restorer));
|
||||
p->pending_signals = 0;
|
||||
p->alarm_at = p->alarm_every = 0; // a child's timers start disarmed
|
||||
p->utime = p->stime = p->cutime = p->cstime = 0;
|
||||
|
||||
// A recycled slot must not inherit a dead process's place in the queue --
|
||||
|
|
@ -1543,6 +1544,97 @@ sig_kills_now(struct proc *p, int sig)
|
|||
return p->sig_handler[sig] == 0 && sig_default_kills(sig);
|
||||
}
|
||||
|
||||
// SIGALRM, on time.
|
||||
//
|
||||
// alarm() and setitimer() stored a countdown that nothing ever counted down,
|
||||
// so no timer anyone set has ever fired. Most programs never noticed, because
|
||||
// what they used it for was a timeout; busybox's ping sends every request but
|
||||
// the first from its SIGALRM handler, and on the Alpine root it is /bin/ping,
|
||||
// so ping printed one reply and then nothing.
|
||||
//
|
||||
// A kernel thread rather than the timer interrupt, which is in trap.c, and
|
||||
// which would have to walk the process table with interrupts off: this walks
|
||||
// it once a tick, and only while some process has a timer armed.
|
||||
static int alarms_armed; // processes with alarm_at set; a hint, not a lock
|
||||
|
||||
void
|
||||
alarm_arm(struct proc *p, uint after, uint every)
|
||||
{
|
||||
acquire(&ptable.lock);
|
||||
if (p->alarm_at == 0 && after)
|
||||
alarms_armed++;
|
||||
else if (p->alarm_at && after == 0)
|
||||
alarms_armed--;
|
||||
p->alarm_at = after ? ticks + after : 0;
|
||||
if (p->alarm_at == 0 && after)
|
||||
p->alarm_at = 1; // 0 means disarmed; a tick late is fine
|
||||
p->alarm_every = after ? every : 0;
|
||||
release(&ptable.lock);
|
||||
}
|
||||
|
||||
// Ticks until p's timer is due, 0 if it is not armed.
|
||||
uint
|
||||
alarm_left(struct proc *p)
|
||||
{
|
||||
uint at = p->alarm_at;
|
||||
if (at == 0)
|
||||
return 0;
|
||||
int left = (int)(at - ticks);
|
||||
return left > 0 ? (uint)left : 1;
|
||||
}
|
||||
|
||||
static void
|
||||
alarm_thread(void)
|
||||
{
|
||||
for (;;) {
|
||||
acquire(&tickslock);
|
||||
sleep(&ticks, &tickslock);
|
||||
uint now = ticks;
|
||||
release(&tickslock);
|
||||
if (alarms_armed == 0)
|
||||
continue;
|
||||
|
||||
acquire(&ptable.lock);
|
||||
for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
|
||||
if (p->alarm_at == 0 || (int)(now - p->alarm_at) < 0)
|
||||
continue;
|
||||
if (p->state == UNUSED || p->state == ZOMBIE) {
|
||||
p->alarm_at = 0;
|
||||
alarms_armed--;
|
||||
continue;
|
||||
}
|
||||
if (p->alarm_every) {
|
||||
p->alarm_at = now + p->alarm_every;
|
||||
} else {
|
||||
p->alarm_at = 0;
|
||||
alarms_armed--;
|
||||
}
|
||||
// sigsend takes ptable.lock itself, and with this many lines it is
|
||||
// clearer to repeat its three effects than to split it.
|
||||
if (!init_ignores(p, SIGALRM)) {
|
||||
p->pending_signals |= 1ULL << SIGALRM;
|
||||
if (sig_kills_now(p, SIGALRM)) {
|
||||
p->killed = 1;
|
||||
if (p->killsig == 0)
|
||||
p->killsig = SIGALRM;
|
||||
}
|
||||
if (p->state == SLEEPING) {
|
||||
p->state = RUNNABLE;
|
||||
sched_enqueue(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
release(&ptable.lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
alarm_thread_start(void)
|
||||
{
|
||||
if (kthread_create(alarm_thread, "alarmd") < 0)
|
||||
cprintf("alarm: no kernel thread; alarm() and setitimer() will not fire\n");
|
||||
}
|
||||
|
||||
int
|
||||
sigsend(int pid, int sig)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue