2007-01-27 20:54:24

by Denys Vlasenko

[permalink] [raw]
Subject: O_NONBLOCK setting "leak" outside of a process??

Hi,

I am currently on Linux 2.6.18, x86_64.
I came across strange behavior while working on one
of busybox applets. I narrowed it down to these two
trivial testcases:

#include <unistd.h>
#include <fcntl.h>
int main() {
fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
return 0;
}

#include <unistd.h>
#include <fcntl.h>
int main() {
fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
return 0;
}

If I run "nonblock" in Midnight Commander in KDE's Konsole,
screen redraw starts to work ~5 times slower. For example,
Ctrl-O ("show/hide panels" in MC) takes ~0.5 sec to redraw.
This persists after the program exist (which it
does immediately as you see).
Running "block" reverts things to normal.

I mean: how can O_NONBLOCK _issued in a process which
already exited_ have any effect whatsoever on MC or Konsole?
They can't even know that it did it, right?

Either I do not know something subtle about Unix or some sort
of bug is at work.

Any advice?
--
vda


2007-01-30 03:48:44

by Philippe Troin

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

Denis Vlasenko <[email protected]> writes:

> Hi,
>
> I am currently on Linux 2.6.18, x86_64.
> I came across strange behavior while working on one
> of busybox applets. I narrowed it down to these two
> trivial testcases:
>
> #include <unistd.h>
> #include <fcntl.h>
> int main() {
> fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
> return 0;
> }
>
> #include <unistd.h>
> #include <fcntl.h>
> int main() {
> fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
> return 0;
> }
>
> If I run "nonblock" in Midnight Commander in KDE's Konsole,
> screen redraw starts to work ~5 times slower. For example,
> Ctrl-O ("show/hide panels" in MC) takes ~0.5 sec to redraw.
> This persists after the program exist (which it
> does immediately as you see).
> Running "block" reverts things to normal.
>
> I mean: how can O_NONBLOCK _issued in a process which
> already exited_ have any effect whatsoever on MC or Konsole?
> They can't even know that it did it, right?
>
> Either I do not know something subtle about Unix or some sort
> of bug is at work.

Because they all share the same stdin file descriptor, therefore they
share the same file descriptor flags?

Phil.

2007-02-01 23:03:16

by Denys Vlasenko

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

On Tuesday 30 January 2007 04:40, Philippe Troin wrote:
> > int main() {
> > fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
> > return 0;
> > }
> >
> > int main() {
> > fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
> > return 0;
> > }
> >
> > If I run "nonblock" in Midnight Commander in KDE's Konsole,
> > screen redraw starts to work ~5 times slower. For example,
> > Ctrl-O ("show/hide panels" in MC) takes ~0.5 sec to redraw.
> > This persists after the program exits (which it
> > does immediately as you see).
> > Running "block" reverts things to normal.
> >
> > I mean: how can O_NONBLOCK _issued in a process which
> > already exited_ have any effect whatsoever on MC or Konsole?
> > They can't even know that it did it, right?
> >
> > Either I do not know something subtle about Unix or some sort
> > of bug is at work.
>
> Because they all share the same stdin file descriptor, therefore they
> share the same file descriptor flags?

What share the same file descriptor? MC and programs started from it?

I thought after exec() fds atre either closed (if CLOEXEC) or
becoming independent from parent process
(i.e. it you seek, close, etc your fd, parent would not notice that).

Am I wrong?
--
vda

2007-02-01 23:15:58

by Philippe Troin

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

Denis Vlasenko <[email protected]> writes:

> What share the same file descriptor? MC and programs started from it?

All the processes started from your shell share at least fds 0, 1 and 2.

> I thought after exec() fds atre either closed (if CLOEXEC) or
> becoming independent from parent process
> (i.e. it you seek, close, etc your fd, parent would not notice that).
>
> Am I wrong?

I'm afraid so. Seek position and flags are still shared after an
exec.

Phil.

2007-02-02 12:10:51

by Roland Kuhn

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

Hi Philippe!

On 2 Feb 2007, at 00:15, Philippe Troin wrote:

> Denis Vlasenko <[email protected]> writes:
>
>> What share the same file descriptor? MC and programs started from it?
>
> All the processes started from your shell share at least fds 0, 1
> and 2.
>
>> I thought after exec() fds atre either closed (if CLOEXEC) or
>> becoming independent from parent process
>> (i.e. it you seek, close, etc your fd, parent would not notice that).
>>
>> Am I wrong?
>
> I'm afraid so. Seek position and flags are still shared after an
> exec.
>
That's a bug, right? I couldn't find anything to that effect in IEEE
Std. 1003.1, 2004 Edition...

Ciao,
Roland

--
TU Muenchen, Physik-Department E18, James-Franck-Str., 85748 Garching
Telefon 089/289-12575; Telefax 089/289-12570
--
CERN office: 892-1-D23 phone: +41 22 7676540 mobile: +41 76 487 4482
--
Any society that would give up a little liberty to gain a little
security will deserve neither and lose both. - Benjamin Franklin
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GS/CS/M/MU d-(++) s:+ a-> C+++ UL++++ P+++ L+++ E(+) W+ !N K- w--- M
+ !V Y+
PGP++ t+(++) 5 R+ tv-- b+ DI++ e+++>++++ h---- y+++
------END GEEK CODE BLOCK------



Attachments:
smime.p7s (4.22 kB)
PGP.sig (186.00 B)
This is a digitally signed message part
Download all attachments

2007-02-02 13:49:05

by Guillaume Chazarain

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

2007/2/2, Roland Kuhn <[email protected]>:

> That's a bug, right?

No, if you want something like: (echo toto; date; echo titi) > file
to work in your shell, you'll be happy to have the seek position
shared in the processes.

--
Guillaume

2007-02-02 15:04:29

by Roland Kuhn

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

Hi Guillaume!

On 2 Feb 2007, at 14:48, Guillaume Chazarain wrote:

> 2007/2/2, Roland Kuhn <[email protected]>:
>
>> That's a bug, right?
>
> No, if you want something like: (echo toto; date; echo titi) > file
> to work in your shell, you'll be happy to have the seek position
> shared in the processes.
>
As a naive user I'd probably expect that each of the above adds to
the output, which perfectly fits the O_APPEND flag (to be set by the
shell, of course).

The immediate point was about the flags, though, and having
O_NONBLOCK on or off certainly is a _design_ choice when writing a
program. If I remove O_NONBLOCK, I have a right to expect that I/O
functions do not return EAGAIN!

Ciao,
Roland

--
TU Muenchen, Physik-Department E18, James-Franck-Str., 85748 Garching
Telefon 089/289-12575; Telefax 089/289-12570
--
CERN office: 892-1-D23 phone: +41 22 7676540 mobile: +41 76 487 4482
--
Any society that would give up a little liberty to gain a little
security will deserve neither and lose both. - Benjamin Franklin
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GS/CS/M/MU d-(++) s:+ a-> C+++ UL++++ P+++ L+++ E(+) W+ !N K- w--- M
+ !V Y+
PGP++ t+(++) 5 R+ tv-- b+ DI++ e+++>++++ h---- y+++
------END GEEK CODE BLOCK------



Attachments:
smime.p7s (4.22 kB)
PGP.sig (186.00 B)
This is a digitally signed message part
Download all attachments

2007-02-02 19:01:31

by Philippe Troin

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

Roland Kuhn <[email protected]> writes:

> Hi Guillaume!
>
> On 2 Feb 2007, at 14:48, Guillaume Chazarain wrote:
>
> > 2007/2/2, Roland Kuhn <[email protected]>:
> >
> >> That's a bug, right?
> >
> > No, if you want something like: (echo toto; date; echo titi) > file
> > to work in your shell, you'll be happy to have the seek position
> > shared in the processes.

Absolutely right. This has been part of Unix since the beginning.

> As a naive user I'd probably expect that each of the above adds to
> the output, which perfectly fits the O_APPEND flag (to be set by the
> shell, of course).

No, no, O_APPEND has slightly different semantics.

> The immediate point was about the flags, though, and having
> O_NONBLOCK on or off certainly is a _design_ choice when writing a
> program. If I remove O_NONBLOCK, I have a right to expect that I/O
> functions do not return EAGAIN!

Generally you don't want to mess with shared resouces like stdin,
stdout and stderr.

Phil.

2007-02-04 00:56:09

by David Schwartz

[permalink] [raw]
Subject: RE: O_NONBLOCK setting "leak" outside of a process??


> That's a bug, right? I couldn't find anything to that effect in IEEE
> Std. 1003.1, 2004 Edition...
>
> Ciao,
> Roland

It's not a bug, there's no rational alternative. What would two indepedent
file descriptors for the same end of a TCP connection be? What happens when
you call 'dup' on a file descriptor? The behavior is both the only logical
behavior and consistent with other cases where a file descriptor is
split/duplicated.

DS


2007-02-04 01:24:51

by Denys Vlasenko

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

On Sunday 04 February 2007 01:55, David Schwartz wrote:
>
> > That's a bug, right? I couldn't find anything to that effect in IEEE
> > Std. 1003.1, 2004 Edition...
> >
> > Ciao,
> > Roland
>
> It's not a bug, there's no rational alternative. What would two indepedent
> file descriptors for the same end of a TCP connection be?

Easy. O_NONBLOCK should only affect whether read/write blocks or
returns EAGAIN. It's logical for this setting to be per-process.

Currently changing O_NONBLOCK on stdin/out/err affects other,
possibly unrelated processes - they don't expect that *their*
reads/writes will start returning EAGAIN!

Worse, it cannot be worked around by dup() because duped fds
are still sharing O_NONBLOCK. How can I work around this?
--
vda

2007-02-04 07:57:34

by David Schwartz

[permalink] [raw]
Subject: RE: O_NONBLOCK setting "leak" outside of a process??


> Easy. O_NONBLOCK should only affect whether read/write blocks or
> returns EAGAIN. It's logical for this setting to be per-process.

Sadly, that's not what POSIX says. POSIX says that 'dup' and 'fork' create
two references to the same file description and that O_NONBLOCK is a
per-file-description flag. So such an implementation would not be
POSIX-conforming.

> Currently changing O_NONBLOCK on stdin/out/err affects other,
> possibly unrelated processes - they don't expect that *their*
> reads/writes will start returning EAGAIN!

Then they're broken. Sorry, that's just the way it is. Code should always
correctly handle defined error codes. I agree that it's unexpected and
unfortunate, but you have to code defensively.

*Every* blocking fd operation should be followed by a check to see if the
operation failed, succeeded, or partially succeeded. If it partially
succeeded, it needs to be continued. If it failed, you need to check if the
error is fatal or transient. If transient, you need to back off and retry.
It has, sadly, always been this way. (Programs can get signals, debuggers
can interrupt a system call, the unexpected happens.)

> Worse, it cannot be worked around by dup() because duped fds
> are still sharing O_NONBLOCK. How can I work around this?

If this causes your code a problem, your code is broken. What does your code
currently do if it gets a non-fatal error from a blocking operation? If it
does anything other than back off and retry, it's mishandling the condition.

It is also an error to change the modes on an inherited file descriptor
unless you know for a fact that this is what the program that gave you the
file descriptor expects you to do (or that it has relinquished that
descriptor). So it takes two bugs to cause this problem.

I agree that the world might have been a better place had this been thought
about from the beginning.

DS


2007-02-04 20:11:28

by Michael Tokarev

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

David Schwartz wrote:
[]
>> Currently changing O_NONBLOCK on stdin/out/err affects other,
>> possibly unrelated processes - they don't expect that *their*
>> reads/writes will start returning EAGAIN!
>
> Then they're broken. Sorry, that's just the way it is. Code should always
> correctly handle defined error codes. I agree that it's unexpected and
> unfortunate, but you have to code defensively.
>
> *Every* blocking fd operation should be followed by a check to see if the
> operation failed, succeeded, or partially succeeded. If it partially
> succeeded, it needs to be continued. If it failed, you need to check if the
> error is fatal or transient. If transient, you need to back off and retry.
> It has, sadly, always been this way. (Programs can get signals, debuggers
> can interrupt a system call, the unexpected happens.)

Well, that's partly nonsense. The only error condition which is always being
checked in correctly written software is EINTR - if you've got an interrupt,
continue/retry the I/O.

Checking and retrying for EAGAIN is umm.. plain wrong. You'll get a nice
busywait eating 100% CPU this way, till the I/O actually happens, and will
get another the next try.

Checking I/Os for every possible weird condition is just non-productive.

It's like this:

if (fcntl(fd, F_SETFL, ~O_NONBLOCK) < 0) error_out();
if (fcntl(fd, F_GETFL, 0) & O_NOBLOCK) ??? what to do?
while(do_something())
if (fcntl(fd, F_GETFL, 0) & O_NOBLOCK)
if (fcntl(fd, F_SETFL, ~O_NONBLOCK) < 0) error_out();

(don't pay attention to ~O_NONBLOCK thing - it's wrong, but it's
used like that just to show the "idea" - which is to clear O_NONBLOCK)

Which is a complete nonsense. It's either set or cleared, and once
set or cleared it should stay that way, period. Until the app changes
it again.

>> Worse, it cannot be worked around by dup() because duped fds
>> are still sharing O_NONBLOCK. How can I work around this?
>
> If this causes your code a problem, your code is broken. What does your code

With dup() - maybe. But definitely NOT with fork().

> currently do if it gets a non-fatal error from a blocking operation? If it
> does anything other than back off and retry, it's mishandling the condition.

Retrying I/O in case of EAGAIN is *wrong*. See above.
But sure, in case of dup() an app should be prepared to set up all the flags
properly.

/mjt

> I agree that the world might have been a better place had this been thought
> about from the beginning.

2007-02-04 21:09:29

by David Schwartz

[permalink] [raw]
Subject: RE: O_NONBLOCK setting "leak" outside of a process??


> > *Every* blocking fd operation should be followed by a check to
> > see if the
> > operation failed, succeeded, or partially succeeded. If it partially
> > succeeded, it needs to be continued. If it failed, you need to
> > check if the
> > error is fatal or transient. If transient, you need to back off
> > and retry.
> > It has, sadly, always been this way. (Programs can get signals,
> > debuggers
> > can interrupt a system call, the unexpected happens.)

> Well, that's partly nonsense. The only error condition which is
> always being
> checked in correctly written software is EINTR - if you've got an
> interrupt,
> continue/retry the I/O.

> Checking and retrying for EAGAIN is umm.. plain wrong. You'll get a nice
> busywait eating 100% CPU this way, till the I/O actually happens, and will
> get another the next try.

I said back off and retry.

> Checking I/Os for every possible weird condition is just non-productive.
>
> It's like this:
>
> if (fcntl(fd, F_SETFL, ~O_NONBLOCK) < 0) error_out();
> if (fcntl(fd, F_GETFL, 0) & O_NOBLOCK) ??? what to do?
> while(do_something())
> if (fcntl(fd, F_GETFL, 0) & O_NOBLOCK)
> if (fcntl(fd, F_SETFL, ~O_NONBLOCK) < 0) error_out();
>
> (don't pay attention to ~O_NONBLOCK thing - it's wrong, but it's
> used like that just to show the "idea" - which is to clear O_NONBLOCK)

I agree that checking for a condition that there's no sane way to handle is
non-productive. But here we're talking about testing for a condition that
has been proven to happen and for which a sane way to handle it is
obvious -- back off and retry.

> Which is a complete nonsense. It's either set or cleared, and once
> set or cleared it should stay that way, period. Until the app changes
> it again.

Until anything with access to it changes it.

> >> Worse, it cannot be worked around by dup() because duped fds
> >> are still sharing O_NONBLOCK. How can I work around this?
> >
> > If this causes your code a problem, your code is broken. What
> > does your code
>
> With dup() - maybe. But definitely NOT with fork().

With 'fork', you either give the other process the file descriptor or you
share it. Any shared resource requires cooperation for sane results.

> > currently do if it gets a non-fatal error from a blocking
> > operation? If it
> > does anything other than back off and retry, it's mishandling
> > the condition.

> Retrying I/O in case of EAGAIN is *wrong*. See above.

You missed the "back off" part.

DS


2007-02-05 10:49:45

by bert hubert

[permalink] [raw]
Subject: Re: O_NONBLOCK setting "leak" outside of a process??

O_NONBLOCK supposedly hits the entire 'ofile' and not just the fd:
http://cr.yp.to/unix/nonblock.html

--
http://www.PowerDNS.com Open source, database driven DNS Software
http://netherlabs.nl Open and Closed source services