[net.unix-wizards] /dev/null

levy@ttrdc.UUCP (Daniel R. Levy) (11/20/85)

I have a question about a very commonly used "device," namely /dev/null.
Can someone explain what happens to data written into it?  Seriously,
now.  At least on the 3B20 and the 3B2 (the only Unix machines I have
handy at this moment to check) /dev/null is implemented as a device
with the same major number as /dev/mem and /dev/kmem, viz:

$ ls -l /dev/null /dev/mem /dev/kmem
crw-r-----   1 root     sys        2,  1 Mar 31  1981 /dev/kmem
crw-r-----   1 root     sys        2,  0 Mar 31  1981 /dev/mem
crw-rw-rw-   1 root     root       2,  2 Nov 19 22:00 /dev/null

This trivia became VERY interesting to me after once blowing away
/dev/null in a careless attempt as root to link it to a file system other
than / on the 3B2/300 in my department (specifically, I tried to link it to
/usr/lib/spell/spellhist).  I was racking my brain as to what I should
mknod, not having another 3B2 handy to refer to (this was on a weekend,
too) and I finally went by analogy to the 3B20 and succeeded (later verifying
it against another department's 3B2).  But I digress...

Seeing that /dev/mem and /dev/kmem are "windows" into the physical memory space
of the computer, I wonder if /dev/null is implemented as a "window" into a
nonexistent section of physical memory, or what?  Does data written into
/dev/null appear on the processor bus during the write (and not just for
the data fetch, but afterward, as if it were being written to a real periph-
eral device)?  (This is letting alone the question of how reads
from /dev/null return an immediate end of file.)  Some kernel wizard surely
can answer this off the top of his or her head, and surely the answer must
be more complicated than what I am surmising, since the kernel buffers
i/o from/to terminals rather than feeding the raw data directly, according
to the few things I have seen and read about "clists" and terminal buffers
with respect to the user-tunable parameter table for the 3B2 kernel.

Thanks in advance....  will NOT be redirected to /dev/null :-).

stephen@dcl-cs.UUCP (Stephen J. Muir) (11/23/85)

>Seeing that /dev/mem and /dev/kmem are "windows" into the physical memory space
>of the computer, I wonder if /dev/null is implemented as a "window" into a
>nonexistent section of physical memory, or what?

cp /etc/motd /dev/null
panic: segmentation violation

What happens is that all writes map onto the "write" system call (write(2)).

The code (off the top of my head) goes like:

write (filedes, buffer, nbytes)
	char	*buffer
	{ if (major_minor (filedes) == that_of ("/dev/null"))
		return (nbytes);
	  else
		go_do_the_REAL_write (filedes, buffer, nbytes);
	}

So the "write" system call just returns success and does nothing!
-- 
UUCP:	...!seismo!mcvax!ukc!dcl-cs!stephen
DARPA:	stephen%comp.lancs.ac.uk@ucl-cs	| Post: University of Lancaster,
JANET:	stephen@uk.ac.lancs.comp	|	Department of Computing,
Phone:	+44 524 65201 Ext. 4599		|	Bailrigg, Lancaster, UK.
Project:Alvey ECLIPSE Distribution	|	LA1 4YR

ron@BRL.ARPA (Ron Natalie) (11/23/85)

NULL just says the I/O has been completed without doing anything.
On read it returns zero characters read.

-Ron

levy@ttrdc.UUCP (Daniel R. Levy) (11/24/85)

Thanks everyone for all the messages I got on this subject.  As of
this writing, I have gotten replies from:

..!castor!rer -Rick Richardson PC Research, Inc.
...!ihnp4!cuuxb!dlm =Dennis L. Mumaugh Lisle, IL  
allegra!fisher!djl ***dan :The misplaced (That car sure is rusty!) Californian
..!ihnp4!mgnetp!mgwess!plw Pete Wilson AT&T IS CGBS Montgomery Works
ulysses!utah-cs!utah-gr!thomas@utah-gr.UTAH-CS (Spencer W. Thomas)
watmath!onfcanim!dave Dave Martindale
nbires!rcd (Dick Dunn)
watmath!watcgl!sahayman Steve Hayman guru in training university of waterloo
ncsu!ncrcae!sauron!wescott Mike Wescott
ihnp4!seismo!BRL.ARPA!ron Ron Natalie
ihnp4!well!perry (Perry S. Kivolowitz)
seismo!mcvax!vu44!ark!gijs Gijs

(I apologize if I have omitted anyone.)

I should have been smart enough to look at the source code before asking this
one.  Several people pointed out where to look.  Since the source is not
public domain, I obviously can't quote it for purposes of summary (on
Sys5R2 on a 3B20 it is in /usr/src/uts/3b/io/mem.c but your mileage may vary).
However, the gist of what happens is that attempts to read() from /dev/null
return 0 (EOF), and write() to /dev/null returns a value equal to the number
of characters you asked to write, both no questions asked.  In fact, you can
throw any pointer, valid or not, at such a request as a buffer location
and no error is produced.  (This happens even if this was from stdout or
stdin, say, and it was redirected to/from /dev/null from the shell.  There-
fore, a corollary is that it would NOT be good debugging practice to attach
ANY file descriptor to /dev/null if one is at the least worried about the
possibility of giving write() or read() there an out-of-bounds or bad pointer.
This aside from the fact that for write() you would want to save the output
anyhow for scrutiny.  Apparently read() from an empty file displays
this same characteristic--the validity of the passed pointer is ignored.
I tried it.)

Thanks again for all the mail.

 -------------------------------    Disclaimer:  The views contained herein are
|       dan levy | yvel nad      |  my own and are not at all those of my em-
|         an engihacker @        |  ployer or the administrator of any computer
| at&t computer systems division |  upon which I may hack.
|        skokie, illinois        |
 --------------------------------   Path: ..!ihnp4!ttrdc!levy
-- 
 -------------------------------    Disclaimer:  The views contained herein are
|       dan levy | yvel nad      |  my own and are not at all those of my em-
|         an engihacker @        |  ployer or the administrator of any computer
| at&t computer systems division |  upon which I may hack.
|        skokie, illinois        |
 --------------------------------   Path: ..!ihnp4!ttrdc!levy

tim@ISM780B.UUCP (11/27/85)

All IO system calls eventually end up as a call to a device driver.
Which driver is determined by the major device number, via a table
called bdevsw[] for block devices, and cdevsw[] character devices.

The items in these tables are structures that contain, among other
things, pointers to the routines to handle read and write ( for
character devices ), or a routine called the strategy routine ( for
block devices ( this is sort of a combined read and write routine ) ).

When the routine for the device driver is called, it is given the minor
device number, which it uses in whatever way it wants.  A typical device,
such as a magtape, will use some bits of the minor device number to
determine which of several tape drives to use, and the other bits to
indicate things like density, write-enable, or whatever.

It is the job of the device driver to transfer the data to/from the
device.  Let's concentrate on character devices ( like /dev/{null,mem,
kmem} ).  When the read or write routine is called, u.u_base will point
to the data to transfer, u.u_count will tell how may bytes to transfer,
and u.u_segflg will tell if u.u_base is a kernel address or a user address.
u.u_offset tells where in the device to read/write from/to.

For /dev/{mem,kmem,null}, the write routine looks something like this:

mwrite( minor )
{
	switch ( minor ) {
	case KMEM:
		/*
		 * verify that the requested address is part of kernel
		 * memory, make sure that the user address is in the
		 * users address space.  Transfer the data.
		 */
		break;
	case NULL:
		u.u_offset += u.u_count;        /* ?not sure */
		u.u_count = 0;          /* means all data xfered */
		break;
	case MEM:
		/*
		 * a lot like KMEM
		 */
}

Note that to most of the kernel, this looks just like a write to any
other character device, such as a line printer.  The sequence from
the system call to the device goes something like this:

	1. you do a write system call
	2. UNIX figures out it is a write system call
	3. Sets up u.u_base, u.u_count and u.u_segflg
	   from yur parameters to write
	4. uses the file descriptor you gave it to find out the inode
	5. notes that inode is for a character device
	6. gets the major and minor device number from the inode
	7. Does something like
		(*cdevsw[ major ].write_routine)( minor );

Block devices are very similar, except that they are given a pointer
to a buffer to transfer, and the system will not call the device if the
data is in the buffer cache.

					Tim Smith
					ima!ism780!tim
					ihnp4!cithep!tim

jsdy@hadron.UUCP (Joseph S. D. Yao) (11/28/85)

In article <808@dcl-cs.UUCP> stephen@comp.lancs.ac.uk (Stephen J. Muir) writes:
>write (filedes, buffer, nbytes)
>	char	*buffer
>	{ if (major_minor (filedes) == that_of ("/dev/null"))
>		return (nbytes);
>	  else
>		go_do_the_REAL_write (filedes, buffer, nbytes);
>	}

I don't know whether to hope that this is a bad joke, or to hope
that no-one would on purpose perpetrate this kind of misinformation.

The write system call  i s  a system call: the arguments get passed
down to the kernel.
The kernel checks various parameters, and especially what type of
file is being written to.  (At this point, we have common code for
read and write, with a flag saying what is being done.)  Since
/dev/{kmem,null,mem,Ukmem} are char devices, the kernel looks in
the character device driver table, and indexes off the major
device number to find which driver to use.
The driver write routine for this particular major device number
does the following things:
	If minor == KMEM, then
		while (u.u_count > 0)
			c = getc();
			put c at *u.u_offset++ in such a way
			that if there is a seg violation, the
			error gets returned to the user instead
			of causing a system crash.
	else if minor == MEM, then
		map *u.u_offset into space accessible via suword()
		and subyte().
		while (u.u_count > 0)
			c = getc();
			if (subyte(c, u.u_offset++) < 0)
				return error;
	else if minor == NULL, then
		just set u.u_count to 0.  that's it.  nothing else.
		that pretends that everything got writ.
	else ...

Some versions of UNIX may do this more elegantly/efficiently, e.g.
with copyout() rather than subyte(); but this is the original,
common idea.  It is   NOT   handled at user level.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}