[comp.unix.wizards] Signals after exec

jd@csd4.milw.wisc.edu (James R Drinkwater) (02/22/89)

Signals that are caught and assigned a signal handler are reset after
a call to exec.  Besides the fact that if they were not reset, a security
problem would result with set-uid programs that are exec'ed, the only other
reason I can think of is that exec overlays the calling process, so all
code is lost.  Are there other reasons that I am missing?  Can you think
of examples in which you would like to keep a signal handler installed
across exec?
--

Jim Drinkwater
Internet: jd@csd4.milw.wisc.edu
UUCP: uwvax!uwmcsd1!uwmcsd4!jd

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/23/89)

In article <1187@csd4.milw.wisc.edu> jd@csd4.milw.wisc.edu (James R Drinkwater) writes:
>Can you think of examples in which you would like to keep a signal handler
>installed across exec?

Since it's not possible, what's the point?

lm@snafu.Sun.COM (Larry McVoy) (02/23/89)

In article <1187@csd4.milw.wisc.edu> jd@csd4.milw.wisc.edu (James R Drinkwater) writes:
>Can you think of examples in which you would like to keep a signal handler
>installed across exec?

This makes no sense.  A signal handler is part of your address space.  That
goes away on an exec.  

Larry McVoy, Lachman Associates.			...!sun!lm or lm@sun.com

greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) (02/24/89)

In article <1187@csd4.milw.wisc.edu> jd@csd4.milw.wisc.edu
(James R Drinkwater) writes:
>Can you think of examples in which you would like to keep a signal handler
>installed across exec?

In other articles, Larry McVoy and other folks argue that this doesn't
make much sense, since a signal handler is part of your address space,
which is lost upon an exec.

This was my initial reaction, too, but then I remembered a paper at the
Winter USENIX conference that presented something called "variable-weight
processes."  This paper very neatly unified the model of a thread and a
process by defining groups of resources, including memory map, file
descriptors, signals, and other resources, that could be shared between
a parent and child thread.  At the one extreme, sharing everything, this
gives a "traditional" thread, while at the other extreme, sharing nothing,
this gives a "traditional" (heavy-weight) process.  In between are some
interesting options, including the possibility of sharing signals, but
not sharing an address map.  If anybody knows how this kind of semantics
was worked out (my copy of the Proceedings isn't here, and my fuzzy memory
of the paper itself says that they didn't discuss it), I'd be interesting
in hearing about it.
-- 
-- Greg Noel, NCR Rancho Bernardo   Greg.Noel@SanDiego.NCR.COM  or  greg@ncr-sd

ram@lcuxlm.ATT.COM (Miani Rich) (02/28/89)

In article <941@ncr-sd.SanDiego.NCR.COM>, greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) writes:
> 
> This was my initial reaction, too, but then I remembered a paper at the
> Winter USENIX conference that presented something called "variable-weight
> processes."  This paper very neatly unified the model of a thread and a
> process by defining groups of resources, including memory map, file
> descriptors, signals, and other resources, that could be shared between

I think the paper was entitled "Lightweight Processes" if that helps anyone. I
have a copy somewhere ... I'll try and dig it up for those that want it...

greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) (02/28/89)

In article <941@ncr-sd.SanDiego.NCR.COM>, I write:
> This was my initial reaction, too, but then I remembered a paper at the
> Winter USENIX conference that presented something called "variable-weight
> processes."  This paper very neatly unified the model of a thread and a
> process by defining groups of resources, including memory map, file
> descriptors, signals, and other resources, that could be shared between

In article <2071@lcuxlm.ATT.COM> ram@lcuxlm.ATT.COM (Miani Rich) writes:
>I think the paper was entitled "Lightweight Processes" ....

The citation is "Variable-Weight Processes with Flexible Shared Resources"
by Ziya Aral, Illya Gertner, Alan Langerman, and Greg Schaffer of Encore
Computer Group and James Bloom and Thomas Doepplner of Brown University.
It's in the proceedings of the Winter 1989 USENIX Technical Conference,
pages 405-412.

It's this sort of model that allows the Unix operating system to grow
and still remain compatible with earlier versions -- identifying the
common concept behind different things.  This particular model unifies
processes and threads in a way so that a program can have greater control
over its resources.  I hope those architects who are designing our follow-
on operating systems (I'm thinking of Mach, 4.4BSD, SysV.4, ...) should
look at this paper and think seriously about this model.

Encore is on the the net, and I think Brown is, as well.  Do any of the
authors (or others) wish to discuss the subject further?
-- 
-- Greg Noel, NCR Rancho Bernardo   Greg.Noel@SanDiego.NCR.COM  or  greg@ncr-sd

aral@pinocchio.Encore.COM (Ziya Aral) (03/02/89)

In article <963@ncr-sd.SanDiego.NCR.COM>, greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) writes:

[After referencing our USENIX paper on "variable-weight" processes]

> ... If anybody knows how this kind of semantics was worked out (my 
> copy of the Proceedings isn't here, and my fuzzy memory
> of the paper itself says that they didn't discuss it), I'd be interesting
> in hearing about it.

In article <963@ncr-sd.SanDiego.NCR.COM>, greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) writes:

[After some much appreciated praise of our paper again asks: ]

> Encore is on the the net, and I think Brown is, as well.  Do any of the
> authors (or others) wish to discuss the subject further?

Ok, Greg, you asked for it...

Actually, the work on variable-weight processes was motivated
by Parasight, a parallel debugging/programmming environment for
shared-memory multis under MACH and BSD. Parasight runs 
parallel applications and various development tools in the same
space. Since this introduces a large degree of heterogenity
in the threads of control sharing one address space, it also violates
the assumption that multiple light-weight threads of control will be
essentially homogeneous (i.e. that they will share their entire
environment).  That is really the underlying assumption of
"light-weight" threads.

Our experience indicates that with real parallel programs, the
assumption of an entirely shared environment with multiple identical
threads of control:

a) Is not always true.
b) Is almost never entirely true.

There are many cases of private file descriptors or private signal 
handlers being quite useful to otherwise similar threads of control.

At a higher level, the idea of "light-weight" threads also complicates
the semantics of UNIX. In place of a single fundamental unit of execution,
such as a process, we now have to deal with two, one "light-weight" and
the other "heavy-weight". A semantic "gap" seems to inevitably appear
between them. In MACH for example, a group of threads shares a common
signal handler. So which one takes the signal? The answer is that the
first available takes it. This is not always appropriate behavior
and the answer "don't use signals" is not very helpful.

We took a slightly different approach. Instead of introducing a light-
weight process or "thread", we asked what gives UNIX processes their
"weight". It turns out that very little does so inherently. 

UNIX carries its history around with it. When a process is created,
it defines its system context, or resource set, in-line. It assumes
that this resource set will be private to that process, in keeping
with its original time-sharing perspective. It then proceeds to
initialize this entire environment. This is the only thing that
makes a process "heavy": the need to intialize and then drag around
a private universe, a seperate address space being by far the most
important component.

But if we seperate a resource from its process and make it idependently
usable by many processes, then we don't have to initialize but once.
The idea of sharable resources is really identical to things like SysV
shared memory regions supported at the process level. The result is that
processes become variably "weighted", their cost depending on the number of
private resources they initialize. 

In practice, the transformation is very easy. A level of indirection
is added to the PROC and USER data structures to reference resources
externally. Resources exist independently and have a link count 
assosciated with them. When the link count goes to zero, the resource
automagically disappears. Linking to resources is through inheritance.
A single new system call, resctl(), specifies whether the children of
a process will inherit links to its existing resources (or a subset of them),
or whether it will create new ones. The default is existing process
behavior.

The actual changes to the kernel are almost trivial, especially in
comparison to the very real work assosciated with making individual
resources multi-threaded and sharable. It is more a conceptual change
than a code change.

The result is "heavy-weight"  processes which have the same startup costs
and are identical to existing processes, "light-weight" processes which
have the same startup costs as MACH threads (somewhat faster actually,
we will publish some interesting numbers soon...), and every combination
in between with the added benefit of semantic uniformity across the whole
range.

Similar benefits should accrue to context switch times and so on. For
example, if 2 processes share the same memory resource, it should
not be necessary to reload the MMU, etc. when they are switched, provided
either that the scheduler knows about it or that second level scheduling
is provided. Our nUNIX kernel does not yet do this optimization.

As to the amount of state an execution unit absolutely has to carry
around with it, that is largely fixed and will not change whether we call
it a process, a thread, or a banana. So why invent new paradigms?

To be honest, the idea that this whole thing might be useful to
uniprocessors and a conventional, co-operating processes model did not
occur to us till much later. In fact, it is just the inverse of
the parallel case; while conventional processes are largely private, they
can often benefit from a degree of sharing.

PHEW!!!

I will be happy to fill in any details if anyone is left awake.
This should teach Greg NEVER to ask an author to "elaborate" on his work :-)



Ziya Aral		aral@multimax.encore.com

My opinions are my own.

greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) (03/03/89)

In article <5025@xenna.Encore.COM> aral@pinocchio.UUCP (Ziya Aral) writes:
>I will be happy to fill in any details if anyone is left awake.
>This should teach Greg NEVER to ask an author to "elaborate" on his work :-)

Er, no, it doesn't -- but it does bring us back to the initial topic, which
was, what happens if you have shared signals between two processes, but
separate address spaces?  How do the semantics of that work out?

-- Greg
-- 
-- Greg Noel, NCR Rancho Bernardo   Greg.Noel@SanDiego.NCR.COM  or  greg@ncr-sd

aral@pinocchio.Encore.COM (Ziya Aral) (03/10/89)

In article <986@ncr-sd.SanDiego.NCR.COM> greg@ncr-sd.SanDiego.NCR.COM (Greg Noel) writes:

>Er, no, it doesn't -- but it does bring us back to the initial topic, which
>was, what happens if you have shared signals between two processes, but
>separate address spaces?  How do the semantics of that work out?

I will have to be forgiven because I don't make it to the net every week and am
not entirly familiar with the history of this discussion. I assume that the
original discussion is implied by the subject "Signals after exec".

The nUNIX kernel does not address the propogation of anything across
execs. It basically specifies that whenever a resource is copied (via fork),
it can instead be shared. As far as forks go, there is no semantic problem. A
conventional fork propogates the signal handler by copying the address space of
the parent, including the signal handler, to the child. The only change that
nUNIX adds is that the memory and/or signal resource of the parent may be
shared rather than copied. All possible combinations are legitimate because the
signal handler, whether shared or private, remains visible after the fork. The
only difference in a shared signal resource is that changes made to it by any
one process which uses it impact all processes which share it... i.e. the table
of signal handlers plus some additional state is made sharable. 

When we implemented shared signal handlers, we discussed whether the signal
event itself should be shared (as in MACH). We opted for the more conservative
approach of only sharing handlers as this is much better defined.

The nUNIX prototype also takes a very conservative view of what constitutes a
"resource". Essentially it defines independent resources as only the 
the six classes of resources currently broken out in the user.h header
(i.e. memory, signals, file descriptors, process stats, protection, and
resource control). While it is clear that other process attributes could be
broken out as independently sharable resources and that additional resources
could be easily added (the direction of our current research), this would not
really impact existing semantics.

The reason shared resources do not impact exec is that exec implies a process
transformation in which only some of the original process state is retained.
In particular, it means that a new memory resource, and thus also a new
signal resource must be created. The retention of existing semantics means that
memory sharing stops at an exec. For what is inherited across an exec, instead
of inheriting the the appropriate values of the exec'ing process, the execee
transparently inherits the link to the resource and whether that resource is
shared or not is largely irrelevent. If additional process attributes were to
be made independently sharable resources (for example, interval timers), the
same would apply. If exec semantics were to be changed to allow the inheritance
of more process state, the same answer would again be valid PROVIDED that such
a change did not fundamentally change UNIX semantics.

The propogation of signal handlers across exec() is an entirely different
issue in that it violates the last point above quite apart from the issues
of shared vs private resources. UNIX semantics define signal handlers as mere
user subroutines of arbitrary complexity and execs as chaining operations.
The contradiction that other posters must have mentioned becomes immediately
obvious. There is no way to reconcile the inheritance of signal handlers
without inheriting the image (shared or not) of the exec'ing process
and that is the exact opposite of what an exec is meant to do. Here it is not
even possible to retain part of the original image because there are no
limits on the dependencies of signal handlers. Since the original image AND
its logic disappears, the signal handlers must disappear with it. While it
could be argued that UNIX's address space and its user-level exception handling
are thus too tightly coupled, other solutions would be less general.

It may be possible to exercise a much more restricted form of control over
the exec'ed child by making the sigmask (currently process private in nUNIX)
and some additional signal state into an independently sharable resource but
I have not really thought about it.


Ziya Aral		aral@multimax.encore.com