18

I am running a stripped down version of BSD Unix V6, bkunix on my Elektronika BK 0010-01. It has a very limited range of Unix commands. The contents of /bin are:

  • cal
  • cat
  • clock
  • cp
  • date
  • df
  • echo
  • ed
  • halt
  • ln
  • ls
  • mkdir
  • mount
  • mv
  • od
  • pwd
  • rm
  • rmdir
  • sh
  • stty
  • sync
  • umount
  • wc

and the contents of /etc are:

  • fsck
  • glob
  • init
  • mkfs
  • mknod

There are also some shell built-ins like cd (though errors mention chdir, as per V6).

So, I am trying to run this shell script called hw using sh:

#!/bin/sh
echo "Hello, world!"

If I type

#./hw

I get the error message "./hw: not found"

If I type

sh hw (or sh hw.sh - I tried this too), I get "try again".

demonstration of the problem

I am guessing it is because the shell script is not executable, though unfortunately I do not know how to make it executable, as chmod is not implemented. The system appears to be aware of permissions, as you can see them when you run ls -l.

So my question are: Is "try again" probably connected to missing permissions, and if so, how might I change file permissions with only the shell builtins and the utilities listed above?

11
  • 8
    I would be interested to know more about your setup. Would you consider putting something up on your youtube channel? May 24, 2023 at 9:31
  • 3
    Yes I'll definitely get round to the BK at some point, thanks for the encouragement!
    – harlandski
    May 24, 2023 at 9:36
  • 4
    Your assumption that the #! syntax existed then, or even that # introduces a comment line, is unwarranted. Try making a script consisting just of the commands which must be executed. Also, does "sh" without any arguments work? If an error message is not displayed, try "ps" to confirm.
    – Leo B.
    May 24, 2023 at 17:44
  • 1
    @Barmar the curly quotes are just the styling on this system I think, there are no double straight quotes, and the ones you see work with echo "Hello, world!" outside of a script. I'll experiment with single quotes, and with excluding them altogether (to do something else)
    – harlandski
    May 25, 2023 at 0:08
  • 2
    I've used old systems where the curly quote was what you got for 0x22, normally ". Possibly the Amstrad PC1512.
    – Chris H
    May 25, 2023 at 12:53

2 Answers 2

17

When the shell executes an external command (i.e., not something like cd or shift), it forks the process. The fork system call returns twice: once for the parent and once for the child.

  • The parent meddles with the file handles, and then waits for the child to exit.

  • The child does some error handling (checking that the file loads and is executable, etc, this would print any sensible error messages) and then executes the program.

The obtuse "try again" message is what's printed out when the shell fails to fork. This is when fork() returns -1.

As an aside, rm also forks (don't know what for yet) and prints a similar message if it fails to fork.


A few reasons why fork() would fail:

  • The system is out of memory
  • The maximum number of PIDs has been reached
  • At least Linux does not support forking if there is no MMU. I know the БК has no MMU, maybe the bkunix kernel doesn't support it
20
  • 13
    fork() works without MMU on ancient systems. You don't want to know how this was done.
    – Joshua
    May 24, 2023 at 16:45
  • 7
    @Joshua: Actually, I think it's useful to understand how context switching used to work, since such understanding will make it obvious why fork() was designed as it was. Normally, switching between process #5 and process #12 would entail writing the present contents of process RAM to slot #5 on the swap drive and reading slot #12 from the swap drive into RAM. If process #12 wants to fork a new process which would be assigned #15, that would be accomplished by dumping the current contents of RAM to slot #12 and modifying the "current slot number" to 15 while leaving whatever was in RAM...
    – supercat
    May 24, 2023 at 18:30
  • 6
    ...from process #12 wherever it happened to be. The next time there's a context switch or another fork, the current contents of RAM will get written out to slot #15. So fork() didn't have to make a copy of RAM for process #12--instead, all it had to do was refrain from loading slot #15 after performing the write that would have been needed when performing any kind of context switch. Of course, that design should have been replaced when it became possible to hold multiple programs in RAM simultaneously, but unfortunately Unix is still saddled with that design and consequent problems.
    – supercat
    May 24, 2023 at 18:33
  • 4
    @supercat From where I sit, that design looks serendipitous. Separating fork() from exec() is handy, giving the forked child shell a chance to adjust the context before exec(). It also allows either by itself, which can be handy.
    – John Doty
    May 24, 2023 at 19:03
  • 5
    @Barmar, the alternative to fork() isn't "just a single process, ever", or "no pipes or anything", but something like posix_spawn(), where the parent process specifies the desired fd actions to execute in the child in some more or less declarative format instead of running code in the child's context to execute them manually.
    – ilkkachu
    May 25, 2023 at 20:18
11

So, now having got the shell script to run, I can say the following:

bkunix does not recognize comments with # or shebang #! Under normal circumstances running:

sh {filename}

where the file starts with a commment or a shebang results in #: not found or #!/bin/sh: not found.

By removing the shebang line and having just the following line in my hw file:

echo "Hello, World!"

means that

sh hw

produces the desired output.

Why this was previously causing the 'try again' I can only guess at, based on @ГероямСлава's response. I had previously been testing out most of the commands I listed as being available in /bin, plus any shell builtins I could think of / draw from the Unix V6 documentation. I can only imagine that I had created too many forks already (I believe bkunix can only support 3 concurrent processes - presumably three additional processes to init and other essentials - though I cannot find the reference to this now). Unfortunately I am unable to thoroughly check this, as there is also no ps command, but I have been able to do the following test.

sh
sh
sh

The first two times produce a new shell with #, the third one produces "try again". This seems to confirm both the hypothesis that bkunix can run a maximum of three additional processes (the default instance of sh plus two more in this case), and that when this limit is exceeded, the error message "try again" is produced.

I am grateful to the various commenters who have led me to this solution, particularly @Leo B. I include a screenshot as evidence, though I have described everything in detail above.

result of typing sh three times

4
  • 1
    RE: "maximum of three processes at once": it's a number greater than three, since it must include init, which will have PID 1. There could be still other processes like device drivers or whatnot, though I don't know that. May 25, 2023 at 8:20
  • 1
    Maybe three additional processes? As I said, I unfortunately can't find the reference any more, but 'three additional' does seem to be supported by my experiment above.
    – harlandski
    May 25, 2023 at 13:36
  • @Героямслава: Possible the initial sh is pid 1? It looks like init doesn't call fork anywhere.
    – Joshua
    Jun 3, 2023 at 4:12
  • @Joshua It's possible. I know of at least one other Unix-like with an arrangement like that. Jun 3, 2023 at 6:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .