Escolar Documentos
Profissional Documentos
Cultura Documentos
Jean-Marie Riet
University Paris VII - Denis Diderot
November 14, 2000
Contents
1 General presentation 3
1.1 The development of Chorus . . . . . . . . . . . . . . . . . . . . 3
1.1.1 History of the project . . . . . . . . . . . . . . . . . . . . 3
1.1.2 The Sun Embedded Workshop . . . . . . . . . . . . . . . 4
1.2 Congurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 The cross development environment . . . . . . . . . . . . . . . . 5
1.4 Main characteristics of ChorusOS . . . . . . . . . . . . . . . . . 5
1.5 The main abstractions of ChorusOS . . . . . . . . . . . . . . . . 6
1.6 The architecture of ChorusOS . . . . . . . . . . . . . . . . . . . 8
1.6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.6.2 The nucleus layer . . . . . . . . . . . . . . . . . . . . . . 9
1.6.2.1 The micro-kernel . . . . . . . . . . . . . . . . . 9
1.6.2.2 The drivers . . . . . . . . . . . . . . . . . . . . 10
1.6.2.3 The PD actor . . . . . . . . . . . . . . . . . . . 10
1.6.3 The OS layer . . . . . . . . . . . . . . . . . . . . . . . . 10
1.6.3.1 Overview . . . . . . . . . . . . . . . . . . . . . 10
1.6.3.2 The C INIT actor . . . . . . . . . . . . . . . . 11
1.6.3.3 The IOM actor . . . . . . . . . . . . . . . . . . 11
1.6.3.4 The AM actor . . . . . . . . . . . . . . . . . . 11
1.6.3.5 The ADMIN actor . . . . . . . . . . . . . . . . 11
1.6.3.6 The SHM REP actor . . . . . . . . . . . . . . . 12
1.6.3.7 The RDBC actor . . . . . . . . . . . . . . . . . 12
1.6.4 The application layer . . . . . . . . . . . . . . . . . . . . 12
1.6.5 The dierent features . . . . . . . . . . . . . . . . . . . . 12
1.6.5.1 Actor management features . . . . . . . . . . . 12
1.6.5.2 Scheduling option . . . . . . . . . . . . . . . . . 13
1.6.5.3 The memory management features . . . . . . . 13
1.6.5.4 The time management features . . . . . . . . . 13
1.6.5.5 The inter-thread synchronization features . . . 14
3
4 Contents
1.6.5.6 The local invocation optional features . . . . . 14
1.6.5.7 The communication optional features . . . . . . 14
1.6.5.8 The le system optional features . . . . . . . . 15
1.6.5.9 The debugging features . . . . . . . . . . . . . 15
1.6.5.10 The C INIT optional feature . . . . . . . . . . 16
1.6.5.11 The I/O management options . . . . . . . . . . 16
1.6.5.12 The network features . . . . . . . . . . . . . . . 16
1.6.5.13 The administration features . . . . . . . . . . . 16
1.6.6 The restart option . . . . . . . . . . . . . . . . . . . . . 17
2 Building and booting a ChorusOS system 19
2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Installing the delivery . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2.1 Downloading the les . . . . . . . . . . . . . . . . . . . . 19
2.2.2 Installing the packages . . . . . . . . . . . . . . . . . . . 20
2.2.3 Organization of the installed les tree . . . . . . . . . . . 20
2.2.3.1 The rst level . . . . . . . . . . . . . . . . . . . 20
2.2.3.2 The tools directory . . . . . . . . . . . . . . . 21
2.2.3.3 The kernel directory . . . . . . . . . . . . . . 22
2.2.3.4 The os directory . . . . . . . . . . . . . . . . . 23
2.2.3.5 The src directory . . . . . . . . . . . . . . . . 23
2.3 Building a standard image . . . . . . . . . . . . . . . . . . . . . 24
2.3.1 Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.3.2 The configure utility . . . . . . . . . . . . . . . . . . . 24
2.3.3 Building the system image . . . . . . . . . . . . . . . . . 25
2.3.4 Building the root le system . . . . . . . . . . . . . . . . 26
2.4 Conguring and tuning a system image . . . . . . . . . . . . . . 28
2.4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.4.2 The conguration les . . . . . . . . . . . . . . . . . . . 28
2.4.3 Conguration tools . . . . . . . . . . . . . . . . . . . . . 30
2.4.3.1 The graphical tool ews . . . . . . . . . . . . . . 30
2.4.3.2 Command-line tool: configurator . . . . . . . 30
2.5 Booting the ChorusOS system . . . . . . . . . . . . . . . . . . . 34
2.5.1 Preparing the boot monitor . . . . . . . . . . . . . . . . 35
2.5.2 Setting up booting services on the host station . . . . . . 36
2.5.3 The RARP service . . . . . . . . . . . . . . . . . . . . . 36
2.5.4 The TFTP service . . . . . . . . . . . . . . . . . . . . . 37
2.5.5 Connecting the console and booting . . . . . . . . . . . . 38
Contents 5
3 Getting started 41
3.1 The C INIT actor and its initialization . . . . . . . . . . . . . . 41
3.1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.2 The sysadm.ini initialization le . . . . . . . . . . . . . 41
3.1.3 Mounting the root le system . . . . . . . . . . . . . . . 42
3.1.4 About security: the /etc/security le . . . . . . . . . . 43
3.1.5 The startup les . . . . . . . . . . . . . . . . . . . . . . 44
3.2 List of C INIT built-in commands . . . . . . . . . . . . . . . . . 44
3.3 The echo command . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.4 The environment: env, setenv and unsetenv . . . . . . . . . . 46
3.5 Statistics: memstat and chorusStat . . . . . . . . . . . . . . . 46
3.5.1 The memstat command . . . . . . . . . . . . . . . . . . . 46
3.5.2 The chorusStat command . . . . . . . . . . . . . . . . . 46
3.6 The mount and umount commands . . . . . . . . . . . . . . . . . 47
3.7 The aps command . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.8 The arun command . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.8.1 General form of the arun command . . . . . . . . . . . . 49
3.8.2 What the command does . . . . . . . . . . . . . . . . . . 49
3.8.3 Executing standard commands: the example of cs . . . . 50
3.8.3.1 The cs command without argument . . . . . . 50
3.8.3.2 Getting information on system conguration . . 51
3.8.3.3 Getting the system's statistics . . . . . . . . . . 52
3.8.3.4 Getting unique identiers . . . . . . . . . . . . 52
3.8.3.5 Getting information on scheduling . . . . . . . 53
3.8.3.6 Getting information about one thread . . . . . 53
3.8.3.7 Getting information about IPC ports . . . . . . 53
3.8.3.8 Getting information about one actor . . . . . . 54
3.8.4 Dynamic loading of user applications . . . . . . . . . . . 54
3.9 The akill command . . . . . . . . . . . . . . . . . . . . . . . . 59
3.10 The ping command . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.11 The reboot command . . . . . . . . . . . . . . . . . . . . . . . 59
3.12 The kernel debugger: kdb . . . . . . . . . . . . . . . . . . . . . 60
3.12.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.12.1.1 Entering the kernel debugger . . . . . . . . . . 60
3.12.1.2 Exiting the kernel debugger . . . . . . . . . . . 60
3.12.1.3 The help command . . . . . . . . . . . . . . . . 60
3.12.2 List of kdb commands . . . . . . . . . . . . . . . . . . . 60
6 Contents
4 Producing ChorusOS applications 63
4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.2 Organization of the working space . . . . . . . . . . . . . . . . . 64
4.3 General conventions for writing C or C++ codes for ChorusOS . 64
4.3.1 Header les . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.2 Symbolic constants . . . . . . . . . . . . . . . . . . . . . 66
4.3.3 Predened types . . . . . . . . . . . . . . . . . . . . . . 66
4.3.4 Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.5 Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.4 General principles . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.1 The libraries . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.2 Entry point . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.3 Supervisor binaries . . . . . . . . . . . . . . . . . . . . . 68
4.4.4 User binaries . . . . . . . . . . . . . . . . . . . . . . . . 68
4.5 The production's environment . . . . . . . . . . . . . . . . . . . 68
4.5.1 The make environment . . . . . . . . . . . . . . . . . . . 68
4.5.2 The imake environment . . . . . . . . . . . . . . . . . . 69
4.5.3 Producing binaries . . . . . . . . . . . . . . . . . . . . . 71
4.5.3.1 The ChorusOSMkMf command . . . . . . . . . . 71
4.5.3.2 Producing the dependencies . . . . . . . . . . . 71
4.5.3.3 Producing the binary . . . . . . . . . . . . . . . 72
4.5.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.5.4.1 Producing examples . . . . . . . . . . . . . . . 72
4.5.4.2 Building a library and using it . . . . . . . . . 74
4.6 Dynamic actors . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.6.2 Building a dynamic library . . . . . . . . . . . . . . . . . 78
4.6.2.1 The DynamicLibraryTarget macro . . . . . . . 78
4.6.2.2 Example . . . . . . . . . . . . . . . . . . . . . . 79
4.6.3 Using a dynamic library . . . . . . . . . . . . . . . . . . 79
4.6.3.1 Dynamic link at actor startup . . . . . . . . . . 79
4.6.3.2 Dynamic programming . . . . . . . . . . . . . . 80
4.6.4 Running a dynamic program . . . . . . . . . . . . . . . . 82
5 General services 83
5.1 Naming in ChorusOS . . . . . . . . . . . . . . . . . . . . . . . . 83
5.1.1 Unique identiers . . . . . . . . . . . . . . . . . . . . . . 83
5.1.1.1 Denition and characteristics . . . . . . . . . . 83
5.1.1.2 The KnUniqueId type . . . . . . . . . . . . . . 84
Contents 7
5.1.1.3 Clearing a unique identier . . . . . . . . . . . 84
5.1.1.4 Comparing unique identiers . . . . . . . . . . 84
5.1.1.5 Sites and unique identiers . . . . . . . . . . . 85
5.1.1.6 Building a unique identier . . . . . . . . . . . 85
5.1.2 Capabilities . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.1.2.1 Denition and characteristics . . . . . . . . . . 86
5.1.2.2 The KnCap type . . . . . . . . . . . . . . . . . . 86
5.1.3 Local identiers . . . . . . . . . . . . . . . . . . . . . . . 87
5.1.4 Getting the local site number . . . . . . . . . . . . . . . 87
5.1.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.2 Getting information about ChorusOS . . . . . . . . . . . . . . . 88
5.2.1 Getting a tunable parameter . . . . . . . . . . . . . . . . 88
5.2.2 Accessing the environment . . . . . . . . . . . . . . . . . 90
5.2.2.1 Getting a value: sysGetEnv . . . . . . . . . . . 90
5.2.2.2 Setting a value: sysSetEnv . . . . . . . . . . . 91
5.2.2.3 Deleting a value: sysUnsetEnv . . . . . . . . . 91
5.2.2.4 Example . . . . . . . . . . . . . . . . . . . . . . 91
5.3 Input/output from the system console . . . . . . . . . . . . . . 93
5.3.1 Reading from the system console: sysRead . . . . . . . . 93
5.3.2 Writing to the system console: sysWrite . . . . . . . . . 93
5.3.3 Polling the system console: sysPoll . . . . . . . . . . . 94
5.3.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.4 Rebooting the local system . . . . . . . . . . . . . . . . . . . . . 95
6 Actors 97
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.2 Actors attributes . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2.1 Identifying an actor . . . . . . . . . . . . . . . . . . . . . 99
6.2.1.1 Capability of an actor . . . . . . . . . . . . . . 99
6.2.1.2 Symbolic name . . . . . . . . . . . . . . . . . . 99
6.2.1.3 The case of c actors . . . . . . . . . . . . . . . 100
6.2.2 Actors states . . . . . . . . . . . . . . . . . . . . . . . . 100
6.2.2.1 The basic states . . . . . . . . . . . . . . . . . 100
6.2.2.2 Changing the basic state of an actor . . . . . . 101
6.2.3 Actors privilege . . . . . . . . . . . . . . . . . . . . . . . 101
6.2.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.3 Getting status of actors on the site . . . . . . . . . . . . . . . . 105
6.3.1 Getting status of all actors on the site . . . . . . . . . . 105
6.3.2 Getting status of all c actors on the site . . . . . . . . . 106
8 Contents
6.4 Creating actors . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.4.2 Promoting an actor to the status of c actor . . . . . . . . 109
6.4.3 High level creation of c actors . . . . . . . . . . . . . . . 109
6.4.3.1 Specic attributes of a c actor . . . . . . . . . . 109
6.4.3.2 Spawning a c actor: the afexec primitives . . . 110
6.4.4 Low level operations on c actors . . . . . . . . . . . . . . 118
6.4.4.1 Creating an empty c actor: acreate . . . . . . 118
6.4.4.2 Loading text and data for a c actor: aload . . 120
6.4.4.3 Activating a c actor: astart . . . . . . . . . . 125
6.4.5 The low level interface: actorCreate . . . . . . . . . . . 126
6.5 Deleting actors . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
6.5.1 The low level interface: actorDelete . . . . . . . . . . . 129
6.5.2 Terminating c actors . . . . . . . . . . . . . . . . . . . . 130
6.5.2.1 Termination of the main thread . . . . . . . . . 130
6.5.2.2 Killing a c actor: akill . . . . . . . . . . . . . 131
6.6 Synchronizing c actors' threads' executions . . . . . . . . . . . . 133
7 Basic memory management 139
7.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.1.1 The memory models and the features . . . . . . . . . . . 139
7.1.2 Memory management . . . . . . . . . . . . . . . . . . . . 141
7.1.2.1 Supervisor and user address spaces . . . . . . . 141
7.1.2.2 The page size . . . . . . . . . . . . . . . . . . . 141
7.1.2.3 Parameters for memory management . . . . . . 142
7.2 Organization of an actor's address space . . . . . . . . . . . . . 144
7.2.1 The concept of region . . . . . . . . . . . . . . . . . . . . 144
7.2.2 Organization of user c actors' address space . . . . . . . 144
7.2.2.1 The MEM FLAT model . . . . . . . . . . . . . 144
7.2.2.2 The MEM PROTECTED model . . . . . . . . 146
7.2.3 Organization of supervisor c actors . . . . . . . . . . . . 147
7.2.4 Regions' descriptors . . . . . . . . . . . . . . . . . . . . . 148
7.2.5 Regions' attributes . . . . . . . . . . . . . . . . . . . . . 149
7.2.5.1 Initialization attributes . . . . . . . . . . . . . 149
7.2.5.2 Protection attributes . . . . . . . . . . . . . . . 149
7.2.5.3 Virtual memory specic attributes . . . . . . . 151
7.2.6 Virtual addresses and physical addresses . . . . . . . . . 151
7.2.7 Displaying all regions of an actor . . . . . . . . . . . . . 154
7.2.7.1 The rgnStat function . . . . . . . . . . . . . . 154
Contents 9
7.2.7.2 Examples . . . . . . . . . . . . . . . . . . . . . 155
7.3 The Posix interface . . . . . . . . . . . . . . . . . . . . . . . . . 157
7.4 Creating a new region . . . . . . . . . . . . . . . . . . . . . . . 158
7.4.1 The rgnAllocate primitive . . . . . . . . . . . . . . . . 158
7.4.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 159
7.4.2.1 Internal allocation . . . . . . . . . . . . . . . . 159
7.4.2.2 External allocation . . . . . . . . . . . . . . . . 162
7.5 Freeing regions in an actor . . . . . . . . . . . . . . . . . . . . . 166
7.5.1 Implicit deallocation at actor termination . . . . . . . . . 166
7.5.2 The rgnFree primitive . . . . . . . . . . . . . . . . . . . 167
7.5.3 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
7.6 Advanced operations on regions . . . . . . . . . . . . . . . . . . 170
7.6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 170
7.6.2 Sharing memory . . . . . . . . . . . . . . . . . . . . . . 171
7.6.2.1 The rgnMapFromActor primitive . . . . . . . . 171
7.6.2.2 Example . . . . . . . . . . . . . . . . . . . . . . 172
7.6.3 Inheriting a copy of existing regions . . . . . . . . . . . . 178
7.6.3.1 The rgnInitFromActor primitive . . . . . . . . 178
7.6.3.2 Example . . . . . . . . . . . . . . . . . . . . . . 179
7.7 Manipulating supervisor address space . . . . . . . . . . . . . . 181
7.7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 181
7.7.2 Displaying all regions in supervisor address space . . . . 182
7.7.3 The supervisor address space memory allocator . . . . . 183
7.7.4 Safely accessing supervisor address space . . . . . . . . . 187
7.7.4.1 The svMemRead primitive . . . . . . . . . . . . 187
7.7.4.2 The svMemWrite primitive . . . . . . . . . . . . 187
7.7.4.3 Example . . . . . . . . . . . . . . . . . . . . . . 188
7.8 The POSIX shared memory . . . . . . . . . . . . . . . . . . . . 191
7.8.1 The POSIX SHM feature and the SHM REP actor . . . . . . 191
7.8.2 Creating/opening a shared memory object . . . . . . . . 192
7.8.3 Unlinking a shared memory object: shm unlink . . . . . 192
7.8.4 Setting the size of a shared object: ftruncate . . . . . . 193
7.8.5 Mapping a shared memory object: mmap . . . . . . . . . 193
7.8.6 Unmapping a shared memory object: munmap . . . . . . 194
8 Threads 195
8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
8.2 Threads names and states . . . . . . . . . . . . . . . . . . . . . 198
8.2.1 Thread identication . . . . . . . . . . . . . . . . . . . . 198
10 Contents
8.2.1.1 Local identication . . . . . . . . . . . . . . . . 198
8.2.1.2 Symbolic name . . . . . . . . . . . . . . . . . . 198
8.2.2 Thread states . . . . . . . . . . . . . . . . . . . . . . . . 199
8.3 Threads scheduling . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.3.2 The scheduling classes . . . . . . . . . . . . . . . . . . . 200
8.3.2.1 The FIFO class . . . . . . . . . . . . . . . . . . 201
8.3.2.2 The RR class . . . . . . . . . . . . . . . . . . . 201
8.3.2.3 The RT class . . . . . . . . . . . . . . . . . . . 202
8.3.3 Getting or setting scheduling information . . . . . . . . . 203
8.3.3.1 The default KnThreadDefaultSched class . . . 203
8.3.3.2 The threadScheduler primitive . . . . . . . . 203
8.3.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 204
8.3.4.1 Getting scheduling attributes . . . . . . . . . . 204
8.3.4.2 Modifying scheduling attributes . . . . . . . . . 206
8.3.4.3 Fifo scheduling vs Round Robin scheduling . . 207
8.4 Delaying a thread . . . . . . . . . . . . . . . . . . . . . . . . . . 208
8.4.1 The threadDelay primitive . . . . . . . . . . . . . . . . 208
8.4.2 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
8.5 Creating and deleting a thread . . . . . . . . . . . . . . . . . . . 211
8.5.1 Creating a thread . . . . . . . . . . . . . . . . . . . . . . 211
8.5.1.1 The threadCreate primitive . . . . . . . . . . 211
8.5.1.2 Examples . . . . . . . . . . . . . . . . . . . . . 213
8.5.2 Deleting a thread . . . . . . . . . . . . . . . . . . . . . . 225
8.5.2.1 The eect of calling exit and exit . . . . . . 225
8.5.2.2 The threadDelete primitive . . . . . . . . . . 226
8.5.2.3 Examples . . . . . . . . . . . . . . . . . . . . . 226
8.6 Activating a thread . . . . . . . . . . . . . . . . . . . . . . . . . 229
8.7 Stopping and restarting a thread . . . . . . . . . . . . . . . . . 230
8.7.1 Stopping a thread . . . . . . . . . . . . . . . . . . . . . . 230
8.7.2 Restarting a stopped thread . . . . . . . . . . . . . . . . 230
8.8 Aborting a thread . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.8.2 The complete states diagram of a thread . . . . . . . . . 231
8.8.3 The threadAbort and threadAborted primitives . . . . 232
8.8.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 232
8.8.4.1 A command for aborting a given thread . . . . 232
8.8.4.2 Thread's auto-abortion . . . . . . . . . . . . . . 233
8.8.4.3 A non abortable call . . . . . . . . . . . . . . . 233
Contents 11
8.8.4.4 Testing the aborted state . . . . . . . . . . . . 234
8.9 Getting the attributes of all threads in an actor . . . . . . . . . 235
8.9.1 The threadStat primitive . . . . . . . . . . . . . . . . . 235
8.9.2 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
8.10 Execution times . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.11 Managing a thread's context . . . . . . . . . . . . . . . . . . . . 239
8.12 A complete example: a fork function . . . . . . . . . . . . . . . 240
8.12.1 Objectives and general principles . . . . . . . . . . . . . 240
8.12.2 Variations on solutions . . . . . . . . . . . . . . . . . . . 241
8.12.2.1 A solution with creation of a c actor . . . . . . 241
8.12.2.2 A solution with low level actor's creation . . . . 245
8.13 Posix threads (pthreads) . . . . . . . . . . . . . . . . . . . . . . 246
8.13.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 246
8.13.2 Posix threads' attributes . . . . . . . . . . . . . . . . . . 246
8.13.3 Creating a pthread . . . . . . . . . . . . . . . . . . . . . 247
8.13.4 Yielding the processor . . . . . . . . . . . . . . . . . . . 248
8.13.5 Terminating a pthread . . . . . . . . . . . . . . . . . . . 248
8.13.6 Killing a pthread . . . . . . . . . . . . . . . . . . . . . . 248
8.13.7 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
9 Synchronization 251
9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
9.2 Thread semaphores . . . . . . . . . . . . . . . . . . . . . . . . . 252
9.2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 252
9.2.2 Initializing a thread semaphore . . . . . . . . . . . . . . 252
9.2.3 Posting a thread semaphore . . . . . . . . . . . . . . . . 253
9.2.4 Waiting on a thread semaphore . . . . . . . . . . . . . . 253
9.2.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
9.3 General mutexes . . . . . . . . . . . . . . . . . . . . . . . . . . 256
9.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 256
9.3.2 Initializing a mutex . . . . . . . . . . . . . . . . . . . . . 256
9.3.3 Acquiring a mutex . . . . . . . . . . . . . . . . . . . . . 257
9.3.4 Freeing a mutex . . . . . . . . . . . . . . . . . . . . . . . 257
9.3.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
9.4 Real-time mutexes . . . . . . . . . . . . . . . . . . . . . . . . . 259
9.5 General semaphores . . . . . . . . . . . . . . . . . . . . . . . . . 261
9.5.1 Initializing a semaphore: semInit . . . . . . . . . . . . . 261
9.5.2 Waiting for a semaphore: semP . . . . . . . . . . . . . . 262
9.5.3 Signaling a semaphore: semV . . . . . . . . . . . . . . . . 262
12 Contents
9.5.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
9.6 Event sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
9.6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 267
9.6.2 Initializing an event set: eventInit . . . . . . . . . . . . 267
9.6.3 Posting an event: eventPost . . . . . . . . . . . . . . . 268
9.6.4 Waiting for events: eventWait . . . . . . . . . . . . . . . 268
9.6.5 Clearing events: eventClear . . . . . . . . . . . . . . . . 269
9.6.6 Example: a synchronization barrier's implementation . . 270
9.7 Posix synchronization mechanism . . . . . . . . . . . . . . . . . 276
9.7.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 276
9.7.2 Waiting for thread termination . . . . . . . . . . . . . . 277
9.7.3 Posix mutexes . . . . . . . . . . . . . . . . . . . . . . . . 277
9.7.3.1 Attributes of Posix mutexes . . . . . . . . . . . 277
9.7.3.2 Locking a Posix mutex . . . . . . . . . . . . . . 278
9.7.3.3 Unlocking a Posix mutex . . . . . . . . . . . . . 278
9.7.4 Posix condition variables . . . . . . . . . . . . . . . . . . 278
9.7.4.1 Initializing a condition variable . . . . . . . . . 278
9.7.4.2 Waiting on a condition variable . . . . . . . . . 279
9.7.4.3 Signaling a condition variable . . . . . . . . . . 279
9.7.4.4 General outline . . . . . . . . . . . . . . . . . . 280
10 Private data 281
10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
10.2 Standard services . . . . . . . . . . . . . . . . . . . . . . . . . . 282
10.2.1 The errno variable . . . . . . . . . . . . . . . . . . . . . 282
10.2.2 Threads' local identiers . . . . . . . . . . . . . . . . . . 284
10.3 The private data keys . . . . . . . . . . . . . . . . . . . . . . . . 284
10.4 Chorus per-thread private data . . . . . . . . . . . . . . . . . . 285
10.4.1 Creating and deleting a thread specic data key . . . . . 285
10.4.2 Setting a thread-specic value . . . . . . . . . . . . . . . 286
10.4.3 Getting a thread specic value . . . . . . . . . . . . . . . 287
10.4.4 Deleting all thread specic values . . . . . . . . . . . . . 287
10.4.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.5 Chorus per-actor private data . . . . . . . . . . . . . . . . . . . 291
10.5.1 Creating and deleting an actor private key . . . . . . . . 291
10.5.2 Setting an actor specic value . . . . . . . . . . . . . . . 292
10.5.3 Getting an actor specic value . . . . . . . . . . . . . . . 292
10.5.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
10.6 Posix private data . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Contents 13
10.6.1 Creating a Posix thread-specic key . . . . . . . . . . . . 296
10.6.2 Deleting a Posix thread-specic key . . . . . . . . . . . . 297
10.6.3 Associating a value to a Posix thread-specic key . . . . 297
10.6.4 Getting the value for a Posix thread-specic key . . . . . 297
11 Local access points (LAPs) 299
11.1 General overview . . . . . . . . . . . . . . . . . . . . . . . . . . 299
11.2 General tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
11.2.1 Copying data between user and supervisor address spaces 301
11.2.2 Manipulating lap descriptors . . . . . . . . . . . . . . . . 302
11.2.2.1 Initializing a lap descriptor to zero . . . . . . . 302
11.2.2.2 Testing if a lap descriptor is initialized . . . . . 302
11.2.2.3 Duplicating a lap descriptor . . . . . . . . . . . 303
11.3 The server's point of view . . . . . . . . . . . . . . . . . . . . . 303
11.3.1 Creating a local access point . . . . . . . . . . . . . . . . 303
11.3.2 Binding a symbolic name to a lap descriptor . . . . . . . 304
11.3.3 Unbinding a lap's symbolic name . . . . . . . . . . . . . 304
11.3.4 Getting handler invoker . . . . . . . . . . . . . . . . . . 305
11.3.5 Deleting a local access point: svLapDelete . . . . . . . . 305
11.3.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 305
11.3.6.1 Installing a lap in the current actor . . . . . . . 305
11.3.6.2 Copy service between user and supervisor spaces 306
11.3.6.3 Dening dierent lap handlers in the same actor 309
11.4 The client's point of view . . . . . . . . . . . . . . . . . . . . . . 312
11.4.1 Resolving a name . . . . . . . . . . . . . . . . . . . . . . 312
11.4.2 Synchronous invocation of a lap handler . . . . . . . . . 312
11.4.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 313
11.4.3.1 A client of the lapCp service . . . . . . . . . . . 313
11.4.3.2 A client of the multiLap service . . . . . . . . . 315
12 Time management 321
12.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
12.1.1 The features . . . . . . . . . . . . . . . . . . . . . . . . . 321
12.1.2 Describing time intervals . . . . . . . . . . . . . . . . . . 321
12.1.3 Time resolution and system time . . . . . . . . . . . . . 322
12.2 The DATE feature . . . . . . . . . . . . . . . . . . . . . . . . . . 323
12.2.1 Getting universal time resolution: univTimeGetRes . . . 323
12.2.2 Getting time of day: univTime . . . . . . . . . . . . . . 323
12.2.3 Setting time of day: univTimeSet, univTimeAdjust . . . 323
14 Contents
12.2.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
12.3 Chorus timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
12.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 325
12.3.2 Initializing a thread pool object . . . . . . . . . . . . . . 326
12.3.3 Creating a Chorus timer . . . . . . . . . . . . . . . . . . 326
12.3.4 Deleting a Chorus timer . . . . . . . . . . . . . . . . . . 327
12.3.5 Starting, canceling or querying a timer: timerSet . . . . 327
12.3.6 Blocking a thread on a thread pool object . . . . . . . . 328
12.3.7 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
12.4 The VTIMER feature . . . . . . . . . . . . . . . . . . . . . . . . . 333
12.4.1 Virtual timeouts . . . . . . . . . . . . . . . . . . . . . . 333
12.4.2 Setting a virtual timeout . . . . . . . . . . . . . . . . . . 334
12.4.2.1 The general interface . . . . . . . . . . . . . . . 334
12.4.2.2 Setting an actor's virtual timeout . . . . . . . . 335
12.4.2.3 Setting a thread's virtual timeout . . . . . . . . 336
12.4.3 Canceling a virtual timeout . . . . . . . . . . . . . . . . 336
12.4.3.1 The general interface . . . . . . . . . . . . . . . 336
12.4.3.2 Cancelling an actor's virtual timeout . . . . . . 337
12.4.3.3 Canceling a thread's virtual timeout . . . . . . 337
12.4.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 337
12.4.4.1 The compute function . . . . . . . . . . . . . . 337
12.4.4.2 A general service . . . . . . . . . . . . . . . . . 340
12.4.4.3 The server's point of view . . . . . . . . . . . . 340
12.4.4.4 The client's point of view . . . . . . . . . . . . 345
12.4.4.5 Execution . . . . . . . . . . . . . . . . . . . . . 346
12.5 Posix interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
12.5.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 350
12.5.2 General services . . . . . . . . . . . . . . . . . . . . . . . 350
12.5.3 Posix timers . . . . . . . . . . . . . . . . . . . . . . . . . 351
12.5.3.1 Identifying a Posix timer . . . . . . . . . . . . . 351
12.5.3.2 Creating a Posix timer . . . . . . . . . . . . . . 351
12.5.3.3 Deleting a Posix timer . . . . . . . . . . . . . . 352
12.5.3.4 Arming a Posix timer . . . . . . . . . . . . . . 352
13 Communications 355
13.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
13.2 Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
13.2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 356
13.2.2 Creating a message space . . . . . . . . . . . . . . . . . . 356
Contents 15
13.2.3 Opening a message space . . . . . . . . . . . . . . . . . . 358
13.2.4 Operations on messages . . . . . . . . . . . . . . . . . . 358
13.2.4.1 General principles . . . . . . . . . . . . . . . . 358
13.2.4.2 Allocating a message . . . . . . . . . . . . . . . 359
13.2.4.3 Posting a message to a queue . . . . . . . . . . 360
13.2.4.4 Getting a message from a queue . . . . . . . . . 361
13.2.4.5 Freeing a message . . . . . . . . . . . . . . . . 361
13.2.4.6 Removing a message from a queue . . . . . . . 362
13.2.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
13.3 The ChorusOS IPC . . . . . . . . . . . . . . . . . . . . . . . . . 368
13.3.1 The features . . . . . . . . . . . . . . . . . . . . . . . . . 368
13.3.1.1 The IPC feature . . . . . . . . . . . . . . . . . . 368
13.3.1.2 The IPC REMOTE feature . . . . . . . . . . . . . 369
13.3.2 Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
13.3.2.1 Overview . . . . . . . . . . . . . . . . . . . . . 371
13.3.2.2 Identication . . . . . . . . . . . . . . . . . . . 372
13.3.2.3 Port states . . . . . . . . . . . . . . . . . . . . 373
13.3.2.4 Creating a port . . . . . . . . . . . . . . . . . . 374
13.3.2.5 Deleting a port . . . . . . . . . . . . . . . . . . 375
13.3.3 Ports groups . . . . . . . . . . . . . . . . . . . . . . . . . 376
13.3.3.1 Introduction . . . . . . . . . . . . . . . . . . . 376
13.3.3.2 Allocating a port group capability . . . . . . . 377
13.3.3.3 Inserting a port into a port group . . . . . . . . 378
13.3.3.4 Removing a port from a port group . . . . . . . 379
13.3.4 Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
13.3.4.1 Message structure . . . . . . . . . . . . . . . . 379
13.3.4.2 Current message . . . . . . . . . . . . . . . . . 380
13.3.5 Destinations and modes . . . . . . . . . . . . . . . . . . 381
13.3.6 Asynchronous communication . . . . . . . . . . . . . . . 382
13.3.6.1 Overview . . . . . . . . . . . . . . . . . . . . . 382
13.3.6.2 Sending a message . . . . . . . . . . . . . . . . 382
13.3.6.3 Receiving a message . . . . . . . . . . . . . . . 384
13.3.6.4 Replying to a message . . . . . . . . . . . . . . 386
13.3.6.5 Example . . . . . . . . . . . . . . . . . . . . . . 386
13.3.7 Synchronous communications . . . . . . . . . . . . . . . 393
13.3.7.1 Overview . . . . . . . . . . . . . . . . . . . . . 393
13.3.7.2 Initiating an RPC . . . . . . . . . . . . . . . . 393
13.3.7.3 Sending a reply . . . . . . . . . . . . . . . . . . 395
13.3.7.4 Example . . . . . . . . . . . . . . . . . . . . . . 395
16 Contents
13.3.8 Manipulating the current message . . . . . . . . . . . . . 398
13.3.8.1 Getting information about the current message 398
13.3.8.2 Getting the current message body . . . . . . . . 399
13.3.8.3 Saving the current message . . . . . . . . . . . 399
13.3.8.4 Restoring a saved message as current message . 400
13.4 Message handlers . . . . . . . . . . . . . . . . . . . . . . . . . . 401
13.4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 401
13.4.2 Connecting and disconnecting a handler to a port . . . . 402
13.4.3 Exiting a message handler . . . . . . . . . . . . . . . . . 403
13.4.4 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
13.5 Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
13.5.1 Getting a port sequence number . . . . . . . . . . . . . . 408
13.5.2 Migrating a port . . . . . . . . . . . . . . . . . . . . . . 408
13.5.2.1 The service . . . . . . . . . . . . . . . . . . . . 408
13.5.2.2 Example . . . . . . . . . . . . . . . . . . . . . . 409
14 Abortions, exceptions and traps 413
14.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
14.2 Abort handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
14.2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 414
14.2.2 Consulting the abort handlers vector . . . . . . . . . . . 417
14.2.3 Connecting abort handlers . . . . . . . . . . . . . . . . . 417
14.2.4 Disconnecting an abort handler . . . . . . . . . . . . . . 418
14.2.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
14.3 Exception handlers . . . . . . . . . . . . . . . . . . . . . . . . . 421
14.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 421
14.3.2 Consulting the exception handlers vector . . . . . . . . . 422
14.3.3 Connecting an exception handler . . . . . . . . . . . . . 423
14.3.4 Disconnecting an exception handler . . . . . . . . . . . . 424
14.3.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 424
14.3.5.1 Example 1 . . . . . . . . . . . . . . . . . . . . . 424
14.3.5.2 Example 2: a server for automatic allocation . . 430
14.4 Traps and subsystems . . . . . . . . . . . . . . . . . . . . . . . 438
14.4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 438
14.4.2 Trap handlers . . . . . . . . . . . . . . . . . . . . . . . . 439
14.4.2.1 General presentation . . . . . . . . . . . . . . . 439
14.4.2.2 Connecting a trap handler . . . . . . . . . . . . 440
14.4.2.3 Disconnecting a trap handler . . . . . . . . . . 440
14.4.2.4 Getting a trap handler . . . . . . . . . . . . . . 441
Contents 17
14.4.3 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
14.5 Timeout handlers . . . . . . . . . . . . . . . . . . . . . . . . . . 446
14.5.1 Setting a timeout . . . . . . . . . . . . . . . . . . . . . . 446
14.5.2 Canceling a timeout . . . . . . . . . . . . . . . . . . . . 447
14.5.3 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
15 The MEM VIRTUAL memory management model 449
15.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
15.1.1 General overview . . . . . . . . . . . . . . . . . . . . . . 449
15.1.2 Segments . . . . . . . . . . . . . . . . . . . . . . . . . . 450
15.1.3 Local caches . . . . . . . . . . . . . . . . . . . . . . . . . 451
15.1.4 The generic interface . . . . . . . . . . . . . . . . . . . . 451
15.2 Basic concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
15.2.1 General aspects . . . . . . . . . . . . . . . . . . . . . . . 451
15.2.2 Parameters for memory management . . . . . . . . . . . 452
15.2.3 Regions' attributes . . . . . . . . . . . . . . . . . . . . . 453
15.2.3.1 Paging attributes . . . . . . . . . . . . . . . . . 453
15.2.3.2 Inheritance attributes . . . . . . . . . . . . . . 454
15.2.3.3 Opaque attributes . . . . . . . . . . . . . . . . 454
15.3 Organization of c actors . . . . . . . . . . . . . . . . . . . . . . 455
15.4 Duplicating an actor address space . . . . . . . . . . . . . . . . 456
15.4.1 The rgnDup primitive . . . . . . . . . . . . . . . . . . . . 456
15.4.2 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
15.4.2.1 The application . . . . . . . . . . . . . . . . . . 457
15.4.2.2 The case of user actors . . . . . . . . . . . . . . 458
15.4.2.3 The case of supervisor actors . . . . . . . . . . 461
15.5 Regions and segments . . . . . . . . . . . . . . . . . . . . . . . 461
15.5.1 The KnObjDesc type . . . . . . . . . . . . . . . . . . . . 461
15.5.2 Creating and initializing a region from a segment . . . . 462
15.5.3 Creating a region and mapping a segment on it . . . . . 463
15.6 Explicit access to a segment . . . . . . . . . . . . . . . . . . . . 464
15.6.1 Reading data from a segment . . . . . . . . . . . . . . . 464
15.6.2 Writing data to a segment . . . . . . . . . . . . . . . . . 464
15.7 Explicit access to a local cache . . . . . . . . . . . . . . . . . . . 465
15.7.1 Finding or creating a local cache . . . . . . . . . . . . . 465
15.7.2 Getting statistics of a cache . . . . . . . . . . . . . . . . 466
15.7.3 Flushing and controlling access to a local cache . . . . . 466
15.7.4 Initializing a local cache . . . . . . . . . . . . . . . . . . 468
15.7.5 Reading data through a local cache . . . . . . . . . . . . 468
18 Contents
15.7.6 Writing data through a local cache . . . . . . . . . . . . 469
15.8 The generic interface . . . . . . . . . . . . . . . . . . . . . . . . 469
15.8.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 469
15.8.2 The MpCreate transaction . . . . . . . . . . . . . . . . 470
15.8.2.1 A request . . . . . . . . . . . . . . . . . . . . . 470
15.8.2.2 A reply . . . . . . . . . . . . . . . . . . . . . . 470
15.8.3 MpRelease transaction . . . . . . . . . . . . . . . . . . 470
15.8.3.1 A request . . . . . . . . . . . . . . . . . . . . . 471
15.8.3.2 A reply . . . . . . . . . . . . . . . . . . . . . . 471
15.8.4 MpGetAccess transaction . . . . . . . . . . . . . . . . 471
15.8.4.1 A request . . . . . . . . . . . . . . . . . . . . . 471
15.8.4.2 A reply . . . . . . . . . . . . . . . . . . . . . . 472
15.8.5 MpPullIn transaction . . . . . . . . . . . . . . . . . . . 472
15.8.5.1 A request . . . . . . . . . . . . . . . . . . . . . 472
15.8.5.2 A reply . . . . . . . . . . . . . . . . . . . . . . 473
15.8.6 MpPushOut transaction . . . . . . . . . . . . . . . . . 473
15.8.6.1 A request . . . . . . . . . . . . . . . . . . . . . 473
15.8.6.2 A reply . . . . . . . . . . . . . . . . . . . . . . 473
15.9 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
15.9.1 Dealing with kernel requests . . . . . . . . . . . . . . . . 474
15.9.2 A mapper . . . . . . . . . . . . . . . . . . . . . . . . . . 478
15.9.3 A client using rgnMap . . . . . . . . . . . . . . . . . . . . 480
15.9.4 Clients using sgRead or sgWrite . . . . . . . . . . . . . 485
15.9.5 Client using sgFlush . . . . . . . . . . . . . . . . . . . . 487
16 The ChorusOS driver framework 489
16.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
16.1.1 General overview . . . . . . . . . . . . . . . . . . . . . . 489
16.1.2 Hardware organization . . . . . . . . . . . . . . . . . . . 491
16.1.3 Software organization . . . . . . . . . . . . . . . . . . . . 491
16.2 The components of the framework . . . . . . . . . . . . . . . . . 493
16.2.1 General organization . . . . . . . . . . . . . . . . . . . . 493
16.2.2 The device tree . . . . . . . . . . . . . . . . . . . . . . . 493
16.2.3 The driver registry module . . . . . . . . . . . . . . . . . 494
16.2.4 The device registry module . . . . . . . . . . . . . . . . 496
16.2.5 Files organization . . . . . . . . . . . . . . . . . . . . . . 498
16.2.6 General tools . . . . . . . . . . . . . . . . . . . . . . . . 500
16.2.6.1 Message logging . . . . . . . . . . . . . . . . . 500
16.2.6.2 An Imakefile . . . . . . . . . . . . . . . . . . 500
Contents 19
16.3 DKI common services . . . . . . . . . . . . . . . . . . . . . . . . 500
16.3.1 The synchronization service . . . . . . . . . . . . . . . . 500
16.3.2 Memory allocation . . . . . . . . . . . . . . . . . . . . . 501
16.3.2.1 Allocating/freeing space . . . . . . . . . . . . . 501
16.3.3 Allocating physical memory . . . . . . . . . . . . . . . . 501
16.3.4 The device tree . . . . . . . . . . . . . . . . . . . . . . . 501
16.3.4.1 Browsing the device tree . . . . . . . . . . . . . 501
16.3.4.2 Modifying the device tree . . . . . . . . . . . . 502
16.3.4.3 Nodes properties . . . . . . . . . . . . . . . . . 503
16.3.4.4 Examples . . . . . . . . . . . . . . . . . . . . . 506
16.3.4.5 Device tree high-level services . . . . . . . . . . 510
16.3.5 Driver registry . . . . . . . . . . . . . . . . . . . . . . . 511
16.3.5.1 Registering a driver . . . . . . . . . . . . . . . 511
16.3.5.2 Accessing the driver registry . . . . . . . . . . . 511
16.3.5.3 Example . . . . . . . . . . . . . . . . . . . . . . 512
16.3.6 Device registry . . . . . . . . . . . . . . . . . . . . . . . 513
16.3.6.1 Services dedicated to device drivers . . . . . . . 513
16.3.6.2 Services for driver clients . . . . . . . . . . . . 514
16.3.7 Other services . . . . . . . . . . . . . . . . . . . . . . . . 516
16.3.7.1 Timeouts . . . . . . . . . . . . . . . . . . . . . 516
16.3.7.2 Busy wait . . . . . . . . . . . . . . . . . . . . . 517
16.3.7.3 System event management . . . . . . . . . . . . 517
16.3.7.4 Global interrupt masking . . . . . . . . . . . . 517
16.3.7.5 Thread preemption disabling . . . . . . . . . . 517
16.4 DKI specic services . . . . . . . . . . . . . . . . . . . . . . . . 518
16.4.1 Specic input/output services . . . . . . . . . . . . . . . 518
16.4.2 Processor family specic services . . . . . . . . . . . . . 518
16.5 Device Driver Interface (DDI) . . . . . . . . . . . . . . . . . . . 518
16.5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 518
16.5.2 Common bus services . . . . . . . . . . . . . . . . . . . . 519
16.5.3 Common bus properties . . . . . . . . . . . . . . . . . . 519
16.5.4 Common bus driver events . . . . . . . . . . . . . . . . . 519
16.5.5 Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
16.5.6 Common bus driver service routines . . . . . . . . . . . . 521
A System calls error codes 525
B Index and list of predened constants 527
20 Contents
C Index and list of predened types 531
D Index and list of primitives 533
Preface
About this book
This book is a programmer's guide: it presents the main aspects of the system
you will face when starting using it. It intends to help you to develop your own
applications for ChorusOS.
The text is divided into
Part 1 (chapters 1-2) describes the overall structure of the system and how a
system can be built, congured and booted;
Part 2 (chapters 3-4) shows how to create applications that can be executed
under ChorusOS.
Part 3 (chapters 5-13) presents the abstarctions of the system and the dier-
ents APIs which are provided to develop applications intended to be executed
under ChorusOS (actors and threads, memory management, synchronization,
communications and cross actor invocation).
Part 4 (chapters 14-16) presents advanced concepts (handlers, virtual memory
and drivers).
To help you along, this book provides numerous examples written in C. I have
tried to keep these examples as short and as simple as possible so that they are
readable.
A compressed archive (tar/gzip) of the various include les and source les of
this book is located at
http://www.pps.jussieu.fr/~rifflet/CHORUS/examples.gz
You can send me messages electronically at
rifflet@pps.jussieu.fr
Finally, for more information about ChorusOS see
http://www.sun.com/software/chorusos
1
2 Preface
Chapter 1
General presentation
1.1 The development of Chorus
1.1.1 History of the project
The Chorus system started life in the 80's as a research project at INRIA (Insti-
tut National de Recherche en Informatique et Automatisme) which is a French
public computing research institute. Chorus aimed at distribution, integrating
communication mechanisms at the heart of the system. Chorus V2 brought a
rst distributed Unix V7 layer within the system.
The "Chorus systemes" company was founded at the beginning of 1987 by Hu-
bert Zimmermann, Michel Gien and a small team. This led to the Chorus/MiX
V3.2 system being released in 1989; this was a distributed, real-time system pro-
viding binary compatibility with SCO systems on Intel based machines. Various
avors of this system have been used within dierent PABX's of Alcatel. Other
avors of the system have been used to equip JVC karaoke machines in Japan.
In 1989, Chorus systemes started a partnership with Unisys in order to provide
a Single System Image Unix system on top of MPP machines. In the same
time frame, the Unix basis evolved from SVR3.2 to SVR4.0. The Unisys OPUS
machine ran this type of system, the ICL Goldrush machine also ran a variant
of that system.
A joint-development eort set up with Novell led to the development of a Chorus
based SVR4.2 ES/MP system which was abandoned when Novell ceased all Unix
development activities.
In 1994, Chorus systemes focussed its activities on the micro-kernel at the heart
of the system, targeting real-time embedded applications. Since then, a small
3
4 Chapter 1. General presentation
OS layer providing some limited POSIX API's based on FreeBSD software has
been used to deliver the ChorusOS system. This has been used in some large
environments, mainly by Telecom Equipment Manufacturers such as Lucent,
Nortel and others. However, it is still used in some low-end devices such as
webphones (with a Java environment), set-top boxes or point of sales devices.
In 1997, Chorus systems, and its 80 employees in France, USA and Japan, has
been acquired by Sun Microsystems Inc.
1.1.2 The Sun Embedded Workshop
The Sun Embedded Workshop (SEW) provides as a rst component a set of
tools and utilities for developing and managing applications intended to run
on a high-performance operating system (the Chorus Operating System) which
constitutes the second component of the workshop. The rst component is a
host-target development environment and the second one (ChorusOS) is a real-
time modular operating system. This book is based on SEW 2.1 and the 4.0
release of ChorusOS.
1.2 Congurations
A typical installation of the SEW requires a minimum hardware conguration
consisting of two machines connected by a Ethernet network or a serial line:
a target PC/AT which will run a ChorusOS operating system instance and
applications. The hardware prerequisites for this PC are as follows:
- a processor of one of the following types: Intel 386, 486 or Pentium;
- an Ethernet board (SMC EtherEZ or Novell NE2000) for downloading the
system and applications which otherwise will be loaded from a diskette;
- approximately 4 megabytes of main memory;
- possibly a
oppy disk (3 1/4 inch) to boot the system.
Of course SEW runs on hardware congurations other than this trivial but
common example. Boards or machines based on SPARC or PPC may also be
used;
a host development machine which will be used for building the ChorusOS
system bootable image, for developing applications and downloading these ap-
plications. The following host machines may be used:
- Sun/Sparc station with Solaris system (version >2.5) or SunOS (version
>4.1);
- PC/AT with Microsoft Windows NT 4.0.
1.3. The cross development environment 5
This type of basic conguration corresponds to the following diagram:
le system
User applications
NFS Applications
System and
applications rsh tftp rarp operating system
CHORUS kernel
target machine host machine
network
The conguration may be more complicated: for example the boot server used
for loading the system and the host used for development may be separate
machines. Furthermore, other machines may be used to NFS mount specic le
systems.
G1 port group G2
threads
regions
actor A1 actor A2 actor B1 an empty actor
site A site B
network
Chorus domain
where
- the Chorus domain consists of two sites named A and B where a ChorusOS is
running;
- the actor A1 belongs to the site A and encapsulates two ports, two memory
regions and two threads; the actor A2 on site A encapsulates two ports, three
regions and one thread. The actor B1 belongs to the site B and encapsulates
three ports, one region and three threads and nally, there is an empty actor
on site B (this actor just contains a port);
- two port groups are dened: the group G1 contains three ports that are all
located on the site A and the group G2 contains one port located on the site A
and another one on the site B;
- one message is sent in one-to-one communication and another one is broadcast
to all ports of the group G2.
Application
actorCreate
afexec
APPLICATION LAYER open
shm open await
ADMIN open
shm open AM
open
read
OS LAYER
NUCLEUS LAYER
D mc146818 D i8254 PD KERN
drivers
Therefore, conguring a system consists mainly of dening which actors will
be created when loading the system, and which modules in each actor will
be included: this process re
ects both the congurability and modularity of
ChorusOS.
1.6.2 The nucleus layer
This section describes the dierent components of this layer which provide the
basic services, as shown in the above picture.
1.6.2.1 The micro-kernel
The low-level basic services are provided by a core executive, micro-kernel cor-
responding to the KERN actor in the picture. It provides the services required
to support real-time applications: management of actors, threads, scheduling,
memory, synchronization, cross actor invocations (LAPs), and so on.
This executive supports multiple independent applications running either as
system applications (that is, sharing the address space of the kernel) or as user
applications. When building a given instance of the kernel, it is possible to
add separate modules providing specic services meeting the needs of the ap-
plications intended to be executed on the system. These modules correspond
to optional features which may be selected when conguring the system (see
1.6.5 and 2.4).
10 Chapter 1. General presentation
1.6.2.2 The drivers
The nucleus layer also includes drivers which represent the hardware compo-
nents. These drivers are implemented as separate actors (for example, in the
picture the actors D i8254 and D mc146818): they allow the operating sys-
tem to interact with the hardware components. More information on drivers is
given in chapter 16.
1.6.2.3 The PD actor
Finally, this layer includes the PD actor which oers a high level API (see
chapter 10) to manage per-thread or per-actor private data. One of the main
uses of these data is for the denition of destruction handlers for actors. This
service is also used to retrieve the le context of extended actors.
1.6.3 The OS layer
1.6.3.1 Overview
This layer provides higher level services: management of input/output, network,
le systems, administration. It also oers a high level management of actors. It
is composed of dierent specialized actors interacting with the nucleus layer.
Within ChorusOS 4.0, the architecture of the OS layer has evolved to satisfy
the following objectives:
achieve a true modularity to enforce congurability;
enhance I/O performance;
remove unused features.
The main aspects of the evolution of this architecture since the ChorusOS 3.2
release are the following:
all cross actor invocations are now based on LAP invocations. The trap
mechanism is no longer used: this means that no binary compatibility with
previous releases of ChorusOS is provided;
the AM and IOM actors are truly independent. Thus, a system may be
congured with one or the other or both actors: services provided by each actor
are clearly identied. The AM is a regular client of the IOM: it uses standard
calls (open, read, . . . ) to access les. The IOM is more modular: dierent
independent features allow specic services to be selected;
a new embedded actor (ADMIN has been added to perform administrative tasks.
We now detail the various actors shown in the picture, which may or may not
be present in a given system, depending on the conguration that has been
adopted (and the system actors that have been embedded in the corresponding
archive).
1.6. The architecture of ChorusOS 11
1.6.3.2 The C INIT actor
It rst plays the role of a remote-shell daemon and listens for commands trans-
mitted by users via the rsh command. It also acts as a command interpreter.
This actor uses the services provided by the AM, the IOM and the ADMIN
actors. It is presented in detail in 3.1.
1.6.3.3 The IOM actor
This actor provides management of dierent input/output operations, depend-
ing on the related features that are selected. It has been upgraded with FreeBSD
2.2 source code. It includes le systems (UFS, NFS, MSDOS le system), dif-
ferent POSIX operations on les, sockets, message queues, shared memory or
pipes.
1.6.3.4 The AM actor
The actor manager is an optional component of the OS layer (corresponding
to the ACTOR EXTENDED MNGT feature) which serves calls related to actor man-
agement like afexec, akill and acreate. Its interface provides a high level
service for creating, loading, running and deleting actors. Actors that are cre-
ated by this specic actor are commonly called c actors or . Unlike actors
created by directly invoking the kernel's service which are empty when created,
c actors look like classic POSIX processes. First, once created, a c actor has
a non empty address space: this address space gives access to a program and
to data which are loaded by the loader of the actor manager. A thread is also
created: it is called the main thread and it executes the main function of the
corresponding program. From the IOM point of view, this actor is a client like
other actors (except it is the only client of the IOM which requests le context
inheritance between actors). When an actor is created through an afexec call,
the AM opens the le to be loaded by invoking the IOM. Thus, a c actor has
specic attributes, among them a le context that allows POSIX operations on
les.
1.6.3.5 The ADMIN actor
This mono-threaded actor was introduced in release 4 of the system. The role
of this actor is to embed all administration utilities which may be required to
administer a ChorusOS system when no le system is available. Depending on
the conguration of specic features, it provides system calls such as ifconfig,
mount, rarp or route which are accessed through a lap's invocation.
12 Chapter 1. General presentation
Most of these services are also available as binary executable les which may
be accessed within the /bin directory of the exported tree.
1.6.3.6 The SHM REP actor
It provides a POSIX-compatible shared memory library (POSIX 1003.1). This
actor is created by the IOM actor when the POSIX SHM feature (which now
applies to the IOM) is active. The only function of this actor is to provide an
address space for shared memory segments provided by the feature. We will see
that this actor is an empty actor when created (see 7.8.1).
/opt/SUNWconn/SEW/4.0:
chorus-doc/
chorus-x86/
/opt/SUNWconn/SEW/4.0/chorus-doc:
html/
man/
pdf/
ps/
4.0
XRAY/
chorus-x86/
tools/
configure ews
5.0/
bin/
The PATH variable of the shell environment should be modied by adding the
directory containing the various utilities that will be used on the host system:
--> export PATH=install dir/4.0/chorus-x86/tools/host/bin
-->
It is important to note that in release 4.0 the code corresponding to the IOM
actor is provided as source code and thus does not appear in this tree.
iom/ nucleus/
Examples:
configure -b ChorusDir -d SILENT=
configure -b ChorusDir -s SourceDrv SourceBsp
--> man configure
NAME
configure - prepare a build directory for ChorusOS
.......... [more lines] ..........
-->
The chorus.bmon contains a standard system image that can be used directly
or be modied to build new ones, for example by adding or removing features or
modifying tunable parameters with conguration tools (ews or configurator)
that will be presented later. This standard image has the following character-
istics:
list of system actors: ADMIN, AM, C INIT, and IOM;
list of kernel features: DATE, EVENT, IPC, LAPBIND, LAPSAFE, LOG, MIPC, PERF,
RTC, SEM, TIMER, USER MODE, VIRTUAL ADDRESS SPACE;
list of AM features: ACTOR EXTENDED MNGT;
list of C INIT features: RSH;
list of IOM features: AF LOCAL, BPF, DEV MEM MSDOSF, NFS CLIENT, POSIX SOC-
KETS, RAM DISK;
list of ADMIN features: ADMIN IFCONFIG, ADMIN MOUNT, ADMIN RARP, ADMIN
ROUTE, ADMIN SHUTDOWN.
The four directories: build-BSP, build-DRV, build-DRV F and build-IOM con-
tain the binary les generated from the sources les (drivers, bsp and iom).
The obj directory contains the binaries that have been built. The correspond-
ing tree lists the les kern.r, ADMIN.r, AM.r, C INIT.r, and so on.
The image directory contains the object les generated during the process of
generation.
Finally, the conf directory is the root of a subtree which contain all the con-
guration les (expressed in XML); these les contain the characteristics of the
system image. These les may be modied with standard tools to prepare the
production of a new system image. We present these les in section 2.4.1 when
describing how a system image may congured.
a) the bin subdirectory contains a number of binary les which can be executed
on a target system (we will see how in the next chapter):
basic Unix like commands: arp, chmod, cp, date, dd, df, format, fsck, ls,
mkdir, mkfifo, mknod, mount, mv, newfs, pax, rm, rmdir, touch, umount;
network commands: arp, domainname, ftp, hostname, ifconfig, inetNSdns,
inetNShost, inetNSnis, netstat, nfsstat, pppstart, route, shutdown, uname,
ypbind, ypcat, ypmatch, ypwhich;
specic ChorusOS commands:
- chat: automated conversational script with a modem;
- chorusNSinet, chorusNSsite: ChorusOS name servers;
- chorusStat: information about ChorusOS resources;
- cs: report ChorusOS resource status (see 3.8.3);
- dhclient: Dynamic Host Conguration Protocol client;
- disklabel: disk pack label;
- flashdefrag: defragment a
ash memory;
- fsck dos: create an MS-DOS (FAT) le system;
- newfs dos: check and repair an MS-DOS (FAT) le system;
- profctl: ChorusOS proling control tool;
- rdbc: ChorusOS remote debugging daemon;
- slattach: attach serial lines as network interfaces;
28 Chapter 2. Building and booting a ChorusOS system
- sysctl: get or set kernel state;
- tclsh: interpreter for Tcl, an embeddable scripting language;
b) the etc subdirectory contains:
traditional administration les like exports, protocols or services;
binary executable for servers like ftpd, nfsd or portmap;
and specic ChorusOS administration les like security or sysadm.ini (see
3.1).
where
the ChorusOS.xml le is the top level conguration le. The whole congu-
ration of a system can be accessed through this le. It contains references to
the other conguration les;
the mkconfig subdirectory is a directory which contains the description of
the dierent components of the systems:
2.4. Conguring and tuning a system image 29
- the kern.xml le contains the denition of the features of the kernel and
their dependencies, the values of the tunable parameters of the kernel and
the default values for a standard conguration;
- the kern action.xml, kern action f.xml and kern f.xml les contain
specic conguration actions and production rules used for the congura-
tion;
- the nucleus.xml, os.xml, admin.xml, am.xml, cinit.xml, iom.xml les
correspond to the denition of the corresponding components of the sys-
tem. Action les like am action.xml or iom action.xml contain congu-
ration actions for the corresponding components;
the mkimage subdirectory gives access to the les containing the information
needed to build a given system image. It contains the following les:
- mkimage.xml: contains two congurable declarations:
BOOT MODE which may be ram to build an image for RAM, or rom to
build an image for ROM;
SYSTEM which may be set to chorus, kernonly or kts depending on
whether the system image built will contain a default chorus system
image, a kernel only system image or will include kernel tests;
- family.xml: contains the family dependent denitions;
- target.xml: contains the binary of all the conguration options related
to the BSP and the list of the drivers;
- model.xml: contains the binary models for executable les;
- system.xml: contains all system binaries and the conguration of the
system image;
- applications.xml: contains the description of all applications that will
be included in the system image;
the basic and extended les contain the denitions of the predened cong-
uration proles. They correspond to the following:
- the basic prole: this is a conguration providing limited services in order
to keep the footprint small. Applications are generally embedded within
the system image and launched either at boot time or later from a local
le system. It uses the MEM FLAT memory management model. Adminis-
tration is purely local: the C INIT actor is directly accessed through the
system console. It includes the following features: ACTOR EXTENDED MNGT,
ADMIN IFCONFIG, ADMIN MOUNT, ADMIN ROUTE, ADMIN SHUTDOWN, AF LOCAL,
DATE, EVENT, IPC, LAPBIND, LAPSAFE, LOCAL CONSOLE, LOG, MIPC, MSDOSFS,
PERF, POSIX SOCKETS RAM DISK, RTC, SEM, TIMER, USER MODE;
30 Chapter 2. Building and booting a ChorusOS system
- the extended prole: provides more general services. It uses the MEM PRO-
TECTED memory management model and includes support for networking
and an NFS client allowing mounting of remote les. It includes the fol-
lowing features: ACTOR EXTENDED MNGT, ADMIN IFCONFIG, ADMIN MOUNT,
ADMIN RARP, ADMIN ROUTE, ADMIN SHUTDOWN, AF LOCAL, BPF, DATE, DEV
MEM, EVENT, IPC, LAPBIND, LAPSAFE, LOG, MIPC, MON, MSDOSFS, NFS CLIENT,
PERF, POSIX SOCKETS RAM DISK, RSH, RTC, SEM, TIMER, USER MODE, VIR-
TUAL ADDRESS SPACE.
The value of a given parameter may be set to a given value or reset to its default
by calling the commands
configurator -c cong le -set name=value
configurator -c cong le -reset name
In the next sequence, we change the values of certain parameters:
--> configurator -c conf/ChorusOS.xml -set kern.exec.maxActorNumber=92
--> configurator -c conf/ChorusOS.xml -set kern.exec.maxThreadNumber=256
--> configurator -c conf/ChorusOS.xml -set am.afexecschedprio=100
-->
More precisely:
- the maximum number of actors is set to 92;
- the maximum number of threads is set to 256;
- the priority of the main thread of an actor dynamically created by the actor
manager is set to 100.
g) Listing or changing the system environment
The system environment is dened by (name, value) pairs, where both name
and value are character strings. This environment is accessed by using the
commands
configurator -c cong le -list env
configurator -c cong le -list env pattern
configurator -c cong le -setenv name=value
configurator -c cong le -resetenv name
The environment contains an internal variable OS CONF whose value should not
be modied. Other variables it may be useful to dene are ROOT HOSTNAME,
ROOT HOSTADDR and ROOT PATHNAME; they are used by the C INIT actor to mount
a root le system automatically.
--> configurator -c conf/ChorusOS.xml -list env
OS_CONF=:IOM:AM:ADMIN:C_INIT
-->
34 Chapter 2. Building and booting a ChorusOS system
The next sequence denes dierent variables in the environment of the archive:
--> configurator -c conf/ChorusOS.xml -setenv ROOT_HOSTADDR=134.157.168.9
--> configurator -c conf/ChorusOS.xml -setenv ROOT_HOSTNAME=fluorine
-->
Using unit 0
Kernel modules : CORE SCHED_FIFO SEM MIPC IPC_L MEM_VM KDB TICK MON ENV
ETIMER LOG LAPSAFE MUTEX EVENT MEM_DFPXM UI DATE PERF TIMEOUT LAPBIND DKI
MEM: memory device 'sys_bank' vaddr 0x7bc17000 size 0x1a4000
[
.......... more lines ]..........
C_INIT: started
C_INIT: /image/sys_bank mounted on /dev/bd00
C_INIT: found /image/sys_bank/sysadm.ini
C_INIT: executing start-up file /image/sys_bank/sysadm.ini
[
.......... more lines ]..........
C_INIT: rshd started
2.5. Booting the ChorusOS system 39
For the carbon system (869DA806 is used instead of 869DA806.ChorusOS.4.0),
we have:
RAM size: 0x1000000 bytes
We then start the nfs server by using the nfs.server script in the /etc/init.d
directory with the start argument. It creates the /etc/dfs/sharetab le of
exported les by executing the share commands in the /etc/dfs/dfstab le:
--> su
Password: (= provide root's password
# /etc/init.d/nfs.server
# exit
--> cat /etc/dfs/sharetab
/home/jmr/CHORUS/r4/build/root - nfs rw
-->
If the nfsd daemon is already active, we simply execute the share command. If
no root le system is mounted, C INIT displays the following warning message
C_INIT: warning - root file system not mounted
In this case, the root le system must be mounted explicitly using the mount
built-in command. In our example, the root of the le system of the target
named neon is the /home/jmr/CHORUS/r4/build/root directory of the host
system whose name is fluorine and whose IP address is 134.157.168.9:
--> rsh neon mount 134.157.168.9:/home/jmr/CHORUS/r4/build/root /
134.157.168.9:/home/jmr/CHORUS/r4/build/root on / (nfs)
-->
We can observe that, apart from the two remote le-systems we mounted, the
neon target system has dierent local or RAM le-systems.
The umount command is used by a trusted user to unmount a le-system:
--> rsh neon umount /mnt
--> rsh neon mount
root_device on / (pdevfs)
devfs on /dev (pdevfs)
devfs on /image (pdevfs)
/dev/bd00 on /image/sys_bank (msdos)
134.157.168.9:/home/jmr/CHORUS/r4/build/root on / (nfs)
-->
These results show that there are two c actors on the booted system.
3.8. The arun command 49
3.8 The arun command
3.8.1 General form of the arun command
This command, whose call has the general form
arun [-g rgid] [-Sj-U] [-k] [-T] [-d] [-q] [-D] [-Z] actor name [actor args]
is used to create new c actors.
In the command:
actor name is the pathname of a binary executable le which should be loaded
and executed by the newly created actor. If the pathname is not absolute, the
le is searched according to the value of the PATH environment variable. As
seen before, the default value of this variable is /bin : it allows the execution
of standard Unix-like commands located in that directory (cp, ls, mkdir, . . . )
or specic ChorusOS commands (chorusStat or cs);
actor args is an optional list of arguments that will be transmitted as an argv
argument when the main thread of the actor calls the main function.
3.8.2 What the command does
The eect of the arun command is to create an actor. The C INIT actor relies
on the services of the actor manager, which in turn invokes the appropriate
services of the IOM and of the kernel. As a consequence, an actor is created by
the AM actor and, is called a c actor (or extended actor). This type of actor
is a CHORUS actor which owns resources and has some additional attributes:
it has a local identier on the site (aid) which is displayed on the screen. If
none of the -S, -U or -T options are used, the privilege of the actor is automat-
ically deduced from the le which is loaded; it may be a user actor (le with
the normal extension u, produced by linking the user library) or a supervisor
actor (le with the normal extension s.r, produced by linking the supervisor
library). The use of one (and only one) of the options denes the type of the
actor (-U corresponds to a user actor, -T to a trusted user actor [system actor]
and -S to a supervisor actor). If one of the options is used, it must be compat-
ible with the type of code corresponding to the loaded le. In any case, only a
trusted user can create a system or a supervisor c actor;
it has a memory context which is created for that actor, the code is loaded
and a data region and a stack region are created;
a thread executing a call to the main function of the code is created (it will
be called the main thread of the c actor). The call to the main function is
done using the list of arguments actor arguments. This thread has a default
scheduling class and a default priority which were dened when the system was
built.
50 Chapter 3. Getting started
On the neon target, the default conguration has been selected (the main thread
belongs to the FIFO scheduling class and its priority is 140);
it has a le context: it has a standard input, a standard output, a standard
error output, a root and a current directory. This property is specic to actors
created using the arun command or created by any actor through the afexec
system calls we will present in chapter 6.
The other options of the command have the following eects:
-k: if the new c actor is a supervisor actor, its symbol table will be
accessible by the kernel debugger (see section 4.3) otherwise, the option
is ignored;
-d: the new c actor is created in the stopped state (used by some debug-
gers).
The execution is synchronous: on the host machine, the remote shell waits for
the termination of the actor. However, it is possible to spawn other actors even
though the rst one has not yet terminated.
3.8.3 Executing standard commands: the example of cs
We rst illustrate the creation and loading of a new actor and the creation and
activation of a main thread by using the arun command with the cs ChorusOS
utility. This command is located in the /bin directory: it gives internal infor-
mation (from the kernel's point of view) for all the actors currently existing in
the system. This command can only be executed by trusted users (since it gives
information like the capabilities of actors).
3.8.3.1 The cs command without argument
Firstly, the command may be called without any argument as in the following
example:
--> rsh neon arun cs
started aid = 2
ChorusOS r4.0.0 Site 0 Time 1h 38m 46
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000001 869da80a 00000001 00000001 0001 SUP STARTED 003 kern
2000001b 869da80a 00000002 00000001 0002 SUP STARTED 001 cs
20000003 869da80a 00000003 00000000 0003 SUP STARTED 000 D_pci
20000004 869da80a 00000004 00000004 0004 SUP STARTED 000 D_pcienum
20000005 869da80a 00000005 00000001 0005 SUP STARTED 000 D_pci_pic
20000006 869da80a 00000006 00000007 0006 SUP STARTED 000 D_pci_isa
20000007 869da80a 00000007 00000000 0007 SUP STARTED 000 D_i8254
20000008 869da80a 00000008 00000008 0008 SUP STARTED 000 D_mc146818
20000009 869da80a 00000009 00000001 0009 SUP STARTED 000 D_ns16550
3.8. The arun command 51
2000000a 869da80a 0000000a 0000000b 0010 SUP STARTED 000 D_epic100
2000000b 869da80a 0000000b 00000000 0011 SUP STARTED 000 D_dec21x4x
2000000c 869da80a 0000000c 0000000c 0012 SUP STARTED 000 D_el3
2000000d 869da80a 0000000d 00000001 0013 SUP STARTED 000 D_smc1660
2000000e 869da80a 0000000e 0000000f 0014 SUP STARTED 000 D_ne2000
2000000f 869da80a 0000000f 00000000 0015 SUP STARTED 000 D_sram
20000010 869da80a 00000010 00000010 0016 SUP STARTED 000 D_idebios
20000011 869da80a 00000011 00000001 0017 SUP STARTED 000 PD
20000012 869da80a 00000012 00000013 0018 SUP STARTED 002 N_iom
20000013 869da80a 00000013 00000000 0019 SUP STARTED 000 AM
20000014 869da80a 00000014 00000014 0020 SUP STARTED 000 ADMIN
20000015 869da80a 00000015 00000001 0021 SUP STARTED 001 C_INIT
-->
After listing general information about the system (version, site's identication
and time information), it gives the following information for every existing actor
on the site (all actors, and not only c actors):
ACTOR-UI: actor's unique identier expressed as two 4-byte numbers (head
and tail) in hexadecimal format;
KEY: key of the actor's capability expressed in the same form as the unique
identier. The UI and the key form the actor's capability;
LID: actor's local identier: each actor is locally identied on the site it belongs
to by a small integer;
TYPE: actor's type: SUP for a supervisor actor and USER otherwise (for a user
actor or a system actor);
STATUS: actor's status (STARTED or STOPPED);
TH#: number of threads belonging to the actor;
NAME: actor's symbolic name, if any.
In our example, we can see the dierent system actors we presented in chapter
1 and the actor created for executing the cs command itself; we can also see
that this actor is a supervisor actor.
Note that the type of the actor (user or supervisor) to be created is deduced
from the contents of the loaded les, there is no need to use the -S option to
create a supervisor actor.
3.8. The arun command 57
The request to create a new actor with arun may also be launched in back-
ground:
--> rsh neon arun trivialAct_u aaaaa bbbbb &
[1] 5713 (=
message from local shell
-->
We can observe that nothing happens; in fact the UNIX process on the host
system is stopped due to a tty input (message [1] Stopped (SIGTTIN) ...)
(even if no reads are posted by the remote command). This type of stopped
process has to be restarted in foreground mode:
--> fg %1
rsh neon arun trivialAct_u aaaaa bbbbb
started aid = 2
Hello! I am the trivial actor
I have 2 parameters
==> aaaaa
==> bbbbb
-->
A better solution is to use the -n option of the rsh command; the standard
input is then redirected to /dev/null and the command executes normally:
--> rsh -n neon arun trivialAct_u xxxxx yyyyy &
[1] 5716 (=
message from local shell
--> started aid = 2 (=
shell's prompt is displayed
Hello! I am the trivial actor
I have 2 parameters
==> xxxxx
==> yyyyy
It is important to note that if an actor is created using the rsh command with
the -n option, threads in that actor cannot read data on the tty.
We have now created, on the host system, two shell aliases (here csh or
tcsh) named neon and neon-n (as well as others deduced from the names of
other target systems like carbon) for the commands rsh neon arun and rsh
-n neon arun. These will be used to create and load actors on the target
machine with les extracted from the $CHORUS/bin86 directory (here the value
of the CHORUS variable is /home/jmr/CHORUS):
--> alias
..........
neon rsh neon arun /home/jmr/CHORUS/bin86/!*
neon-n rsh -n neon arun /home/jmr/CHORUS/bin86/!*
carbon rsh carbon arun /home/jmr/CHORUS/bin86/!*
carbon-n rsh -n carbon arun /home/jmr/CHORUS/bin86/!*
..........
58 Chapter 3. Getting started
--> neon trivialAct_u A B
started aid = 21
Hello! I am the trivial actor
I have 2 parameters
==> A
==> B
-->
We now use the noDelay1 application which, unlike the previous trivialAct
one, does not terminate; it allows us to use standard utilities we presented
before to examine the state of the system. We assume here that we are using a
non-secured system:
--> neon-n noDelay1_u &
[1] 5752 (= message from local shell
--> started aid = 2 (= local id of the new actor
The new actor appears in the results of the aps command:
--> rsh neon aps
UID AID NAME DBG GROUP
0 2 noDelay1_u 0 N/A
0 21 C_INIT 0 N/A
0 20 ADMIN 0 N/A
-->
Complete information concerning the new actor is retrieved by using the cs
standard utility:
--> rsh neon arun cs -la 2
started aid = 22 (=
local id of the actor executing cs
ChorusOS r4.0.0 Site 0 Time 3h 20m 51
ACTOR-UI KEY LID TYPE STATUS TH# NAME
2000002e 869da80a 00000002 00000000 0002 USER STARTED 001 noDelay1_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0009 140 00000000 00000000 ff61a4 0- 1- 0 main
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
2000002e 869da80a def no fac324 0 2000002e 869da80a
START SIZE OFFSET ALLOC OPTIONS
ffff4000 00006000 00000000 00006000 WR
ffffb000 00001000 00000000 00001000 WR
ffffc000 00003000 00000000 00003000 EX
-->
- we get the capability of the actor expressed as a hexadecimal value and its
local identication: here 2000002e 869da80a 00000002 00000000 and 2. The
actor is a user actor and is started;
- the actor has one thread (the main thread) whose local identier is 9;
- the actor has a default port where there is no pending message;
3.9. The akill command 59
- the actor has three memory regions (data, stack and text). It is worth noting
that the system is using the MEM PROTECTED memory management model
(the VIRTUAL ADDRESS SPACE feature is active); dierent results would be ob-
tained with the MEM VIRTUAL model (see chapters 7 and 15).
We also create a supervisor actor for the application and extract the corre-
sponding line produced by the cs command:
--> neon-n noDelay1_s.r &
[2] 5758
--> started aid = 22
--> rsh neon arun cs | grep noDelay1_s.r
started aid = 23
20000030 869da80a 00000016 00000084 0022 SUP STARTED 001 noDelay1_s.r
-->
chorus.h exec/ ipc/ am/ alloc.h iostream.h sys/ math.h stdio.h setjmp.h
The make environment denes the location of these les. Header les will be
automatically searched in the directories if the standard tools are used and the
environment of the user is correctly initialized. This allows you to use include
macros like #include <ipc/ipc.h> or #include <math.h>.
include/chorus: this directory is the root of a subtree where the kernel's
APIs and the actor manager are exported. This directory contains a num-
ber of header les (with .h extension) and subdirectories. Each subdirectory
corresponds to the interface of a particular feature. For example, the ipc subdi-
rectory contains the header les associated with the IPC feature, the am subdi-
rectory corresponds to the header le associated to the interface with the actor
manager. These les contain the denitions of constants and types, and the
prototypes of the functions of the corresponding API. A program will use the
directive #include <chorus.h> to include the most commonly needed deni-
tions;
include/stdc: this directory is the root of a subtree where the standard C,
the mathematical C and the console input/output APIs are exported. For ex-
porting the standard input/output API, the directive <stdio.h> will be used;
include/CC: this directory is the root of a subtree where the GNU C++ API
is exported;
66 Chapter 4. Producing ChorusOS applications
include/posix: this directory is the root of a subtree where various POSIX
APIs are exported (standard C, math, input/output, Posix Network, Sun RPC
and some BSD calls).
4.3.2 Symbolic constants
Appendix B lists the constants which are dened in the kernel and the ac-
tor manager interfaces and which can be used when developing applications.
Constants which are related to the kernel API have a K_ prex. For example
K MYACTOR may be used for naming the current actor and K MYSELF may be
used for naming the currently executing thread. Constants associated to the
actor manager API have AFX or AM prexes. The constants used for other
APIs are classic constants (for example NULL, O RDONLY, . . . ) used in BSD or
Posix systems.
4.3.3 Predened types
Many types are dened in the header les. The types associated with the
kernel API have the Kn prex. For example KnCap is the type of a capability,
KnLapDesc is the type of lap descriptor, and so on. Appendix C lists these
predened types.
4.3.4 Primitives
A list of the primitives provided by the dierent features, and which will be
presented in this book, is given in appendix D.
4.3.5 Errors
Services provided by the ChorusOS kernel return either the K OK value in case
of success or a negative error code; error codes have a symbolic name starting
with K_E.
The strSysError function, whose prototype is
char *strSysError(int);
returns a specic C character string when called with one of the negative errors
codes as a parameter. The dierent symbolic constants corresponding to possi-
ble errors returned when invoking a kernel service are listed in appendix A.
Kernel services related to private data return positive error codes. The corre-
sponding symbolic constants, which are prexed by PD_E and predened in the
chPd.h le, are listed in appendix A.
Regular BSD or Posix services return errors using the errno convention and
positive error codes. The perror Posix function (void perror(char *)) may
be used to display a standard message corresponding to an error code.
4.4. General principles 67
4.4 General principles
4.4.1 The libraries
The functional organization of the various libraries is illustrated by the following
tree:
nucleus/ os/
libsys.s.a libsys.u.a libm.a libebd.s.a libebd.u.a kern.a pd.s.a pd.u.a libC.a stdc.s.a stdc.u.a
If the standard tools are used and the correct rules are applied in the Imakele,
the link editor extracts the dierent functions into the corresponding libraries.
The lib/classix subdirectory contains two fundamental libraries:
libcx.u.a is a library used by user actors for accessing the kernel services. It
contains one object le for each primitive which can be called by a user actor;
libcx.s.a is a library used by supervisor actors for accessing the kernel ser-
vices.
The crt subdirectory contains:
the crth.u.o and crt0.o object les which dene the startup routine (pro-
gram entry point) of user actors;
the crth.s.o and crt0.o object les which dene the startup routine of su-
pervisor actors.
The last subdirectory we mention is the CC. It contains the libC.a library (stan-
dard C++ library). This library provides support for C++ applications with a
complete and thread-safe library package. It can be used by user and supervisor
actors.
It is important to note that some libraries cannot be used in all congurations
of the system. For example, the lib/classix/librpc.a (corresponding to the
sun RPC API) cannot be used within a basic environment.
4.4.2 Entry point
Before executing, the program entry point must be set to start , which calls
main once the libraries have been initialized.
68 Chapter 4. Producing ChorusOS applications
4.4.3 Supervisor binaries
Since all supervisor actors share the same address space (the supervisor address
space), the binary codes generated for these actors are relocatable. The choice
of the nal link is left to the congurator for embedded actors and to the actor
manager for dynamically loaded actors.
4.4.4 User binaries
The binaries produced for user actors are also relocatable, the link address of
such an actor and the maximum size of its address space are board dependent
and all user actors are linked at the same address (see chapter 7).
4.5 The production's environment
4.5.1 The make environment
This is dened by a le which contains the denitions of variables and rules
for producing applications from source codes written in C, C++ and assembly
language. As previously mentioned, when using the GNU compilation chain
this le is gcc-devsys.mk.
The following variables are dened:
CFLAGS and CXXFLAGS:dene the options for compiling C or C++ applications.
Dierent options are available:
- WARN: has two possible settings WARN ON (default value) and WARN OFF.
When the WARN ON value is set, the compiler produces warning messages;
- DEBUG: has two possible settings DEBUG ON and DEBUG OFF (default value).
When DEBUG ON is set, information for a debugging application is pro-
duced;
- PROF: has two possible settings PROF ON and PROF OFF (default value).
When PROF ON is set, extra code for producing information suitable for
the analysis of the program is generated;
- OPT: has two possible settings OPT ON (default value) and OPT OFF. When
OPT ON is set, code optimization is performed;
INCLUDES species the search path for the header les. This variable may be
overloaded at the application level;
DEPENDS species the depend value. Like INCLUDES, this variable may be
overloaded at the application level;
LD UCRT0, LD SCRT0, LD-CRT1 and LD-SCTN are used to manage the startup
code (crt) of produced object les;
4.5. The production's environment 69
LD U ACTOR and LD S ACTOR give link information for user and supervisor ac-
tors, respectively;
CLX U LIBS, CLX S LIBS, EBD U LIBS, EBD S LIBS and CXX LIBS give informa-
tion concerning the libraries for user actors, supervisor actors, embedded user
actors, embedded supervisor actors and C++ libraries.
4.5.2 The imake environment
As shown in the gure in section 4.1, various les dene the imake environment:
Imake.tmpl: contains the denitions of standard variables:
- FAMILY: its value of ix86 or powerpc denes the target family;
- COMPILER: its value (for example gcc) denes the compiler to be used;
Imake.rules: contains the denition of standard predened build rules which
can be used to build an Imakele:
- ActorTarget(prog, objs, options, crt0, libs):
creates a binary, under the name prog, by using the object les corre-
sponding to objs and produced by the C compiler with the specied
options and by transmitting the values crt0 and libs to the linker.
This rule is the lowest level one, and is called by other rules to pro-
duce specic types of actors by using the C compiler;
- UserActorTarget(prog, objs, libs):
creates, under the name prog, a binary corresponding to a user actor
by using the object les produced by the C compiler which corre-
spond to objs, and by transmitting libs to the linker;
- SupActorTarget(prog, objs, libs):
creates a binary corresponding to a supervisor actor (object les
produced by the C compiler);
- EmbeddedUserActorTarget(prog, objs, libs):
creates a binary for an embedded user actor (object les produced
by the C compiler);
- EmbeddedSupActorTarget(prog, objs, libs):
creates a binary for an embedded supervisor actor (object les pro-
duced by the C compiler);
- CXXActorTarget(prog, objs, options, crt0, libs):
is the equivalent of ActorTarget for C++ applications;
70 Chapter 4. Producing ChorusOS applications
- CXXUserActorTarget(prog, objs, libs):
creates a binary corresponding to a user actor (object les produced
by the C++ compiler);
- CXXSupActorTarget(prog, objs, libs):
creates a binary corresponding to a supervisor actor (object les
produced by the C++ compiler);
- CXXEmbeddedUserActorTarget(prog, objs, libs):
creates a binary for an embedded user actor (object les produced
by the C++ compiler);
- CXXEmbeddedSupActorTarget(prog, objs, libs):
creates a binary for an embedded supervisor actor (object les pro-
duced by the C++ compiler);
- BuiltinDriver(prog, objs, libs):
creates a ChorusOS driver;
- BspProgTarget(prog, entry, objs, libs):
creates a BSP program;
- LibraryTarget(lib, objs):
adds the object les to the lib library;
- MakeDir(dir):
creates the directory dir;
- Depend(srcs):
computes the dependencies of the les specied by srcs and adds
them to the Makele by calling the makedepend command;
Project.tmpl: contains the denition of variables and rules specic to a
project. If no such le exits in the source directory for the application, an
empty le is used;
Package.rules: contains the rules allowing a binary distribution of a com-
ponent to be built:
- DistActor(prog, dir):
creates the dir directory and copies into it the prog component;
- DistFile(le, dir):
creates the dir directory and copies into it the le le;
4.5. The production's environment 71
- DistProgram(prog, dir):
creates the dir directory and copies into it the prog program;
- DistRenFile(le, newFile, dir):
creates the dir directory and copies into it the le le with the newFile
name;
- DistLibrary(lib, dir):
creates the dir directory and copies into it the lib library.
4.5.3 Producing binaries
4.5.3.1 The ChorusOSMkMf command
The ChorusOSMkMf utility builds a Makefile from the Imakefile according to
the imakele environment; it uses the standard imake utility and the values of
variables and the rules dened in the conguration les.
The general form of the command is as follows:
ChorusOSMkMf cong-le [-s source-dir] [-b build-dir] [-d dist-dir]
where:
cong-le is the directory containing the ChorusOS binary. In all our exam-
ples, we will use the value of the BUILD DIR variable of our environment (its
value is build dir where the system image was built);
source-dir is the directory containing the source codes. The default value is
the current directory (where the command is called);
build-dir is the directory where the binary code will be produced. The default
value is the current directory (where the command is called);
dist-dir is the directory where the binary delivered is stored.
4.5.3.2 Producing the dependencies
The second step consists of generating the make dependencies; this is done by
calling the standard make command with the depend argument. Hence, building
the Makefile with all dependencies consists of the following sequence:
--> ChorusOSMkMf $BUILD_DIR
--> make depend
...............
-->
In order to use the Imakele facility consistently, modications should be ap-
plied to the Imakefile rather than to the generated Makefile. The Makefile
le may be regenerated by issuing the command make Makefile once the
Imakefile has been updated.
72 Chapter 4. Producing ChorusOS applications
4.5.3.3 Producing the binary
Once the Makefile and the dependencies have been created, the binary is built
by calling the make command.
4.5.4 Examples
4.5.4.1 Producing examples
In the examples given in 3.8.4, we used dierent binaries corresponding to very
simple applications (trivialAct and noDelay1). Here we show how they were
produced.
The two applications were developed in the sources/utilities directory of our
workspace: we rst created, for each application, a subdirectory which contains
the source le of the application (there is only one le for these applications)
and the corresponding Imakele. In these Imakeles (and all Imakeles we will
create later), we dene a number of variables:
SRCS : its value is a list of source les (with extensions .c for C source les,
.C for C++ les or .s for assembly les). The dierent names are separated
by spaces;
OBJS : its value is a list of object les (with .o extension).
--> cd $CHORUS/sources/utilities/trivialAct/C
--> cat trivialAct.c
#include <stdio.h>
main(int argc, char *argv[ ]) {
int ind;
printf("Hello! I am the trivial actor\n");
if(argc == 1)
return;
printf("I have %d parameters\n", argc - 1);
for(ind = 1; ind < argc; ind ++)
printf(" ==> %s\n", argv[ind]);
}
-->
We use the following Imakefile located in the same directory as the source le
to produce the executable binaries in the two modes:
--> cat Imakefile
SRCS=trivialAct.c
OBJS=trivialAct.o
UserActorTarget(trivialAct_u, $(OBJS), )
SupActorTarget(trivialAct_s.r, $(OBJS), )
Depend($(SRCS))
-->
We then execute, in the directory containing the Imakefile, the sequence of
4.5. The production's environment 73
commands which produce the Makele, compute the dependencies and produce
the executable binaries:
--> ChorusOSMkMf $BUILD_DIR
--> ls -l Makefile
-rw-r--r-- 1 jmr users 3625 dec 14 18:33 Makefile
--> make depend
depend trivialAct.c
--> ls -l Makefile
-rw-r--r-- 1 jmr users 5029 dec 14 18:34 Makefile
--> make
cc trivialAct.c
ld trivialAct_u
ld trivialAct_s.r
-->
The two binaries have been created in the current directory and are installed
in the bin86 directory (mounted as /mnt on the ix86 target systems):
--> ls triv*
trivialAct.c trivialAct.o trivialAct_s.r trivialAct_u
--> mv trivialAct_s.r trivialAct_u $CHORUS/bin86
-->
--> cd $CHORUS/sources/utilities/noDelay1
--> cat noDelay1.c
#include <chorus.h>
main( ) {
threadDelay(K_NOTIMEOUT); /* the calling thread sleeps for ever */
}
--> cat Imakefile
SRCS=noDelay1.c
OBJS=noDelay1.o
UserActorTarget(noDelay1_u, $(OBJS), )
SupActorTarget(noDelay1_s.r, $(OBJS), )
Depend($(SRCS))
--> ChorusOSMkMf $BUILD_DIR
--> make depend
depend noDelay1.c
--> make
cc noDelay1.c
ld noDelay1_u
ld noDelay1_s.r
--> mv noDelay1_u noDelay1_s.r $CHORUS/bin86
-->
74 Chapter 4. Producing ChorusOS applications
4.5.4.2 Building a library and using it
a) Creating a library
We now develop, in the sources/utilities/lib directory, two functions that
we will use frequently in examples in the book and build a library to contain
them (later, we will add other functions to that library). These functions per-
form input/output operations on capabilities. A capability corresponds to the
KnCap type which is dened as:
struct KnCap {
KnUniqueId ui; /* unique identifier of the capability */
KnKey key; /* key of the capability */
};
typedef struct KnCap KnCap;
and a key (key eld) which has two subelds keyHead and keyTail which are
also dened as elds of 32 bits. This type is dened as:
struct KnKey {
unsigned keyHead : 32; /* head of the key */
unsigned keyTail : 32; /* tail of the key */
};
typedef struct KnKey KnKey;
The library utilities.a that we will install in the $CHORUS/lib86 directory
will rst contain the two following functions:
void fprintCap(FILE *, char *, KnCap *) which writes to the le asso-
ciated to the FILE pointer (it may be stdout or stderr if the actor has a le
context, or it may have been obtained explicitly through a call to the fopen
standard function) the message given as second parameter and the capability
pointed to by the third one as a four hexadecimal 4-bytes number;
void readCap(char **, KnCap *) which extracts from the rst parameter
a capability: this parameter should contain the representation of a capability as
4 hexadecimal ASCII numbers. The resulting capability is stored at the address
given as a second argument.
First of all, a header le (named utilities.h) which corresponds to the library
is built, it contains the prototypes of the two functions of the library. This le
4.5. The production's environment 75
is located in the include directory of our workspace:
--> cat $CHORUS/include/utilities.h
#include <stdio.h>
#include <chorus.h>
void fprintCap(FILE *, char *, KnCap *);
void readCap(char **, KnCap *);
-->
The source code of these two functions corresponds to the two following les in
the sources/utilities/lib directory:
--> cd $CHORUS/sources/utilities/lib
--> cat fprintCap.c
#include "utilities.h"
void fprintCap(FILE *stm, char *message, KnCap *actorCap) {
fprintf(stm, "%s: ", message);
fprintf(stm, "%x %x %x %x\n", actorCap -> ui.uiHead, actorCap -> ui.uiTail,
actorCap -> key.keyHead, actorCap -> key.keyTail);
fflush(stm);
}
--> cat readCap.c
#include "utilities.h"
void readCap(char *argv[ ], KnCap *actorCap) {
int aux;
sscanf(argv[0], "%x", &aux); actorCap -> ui.uiHead = aux;
sscanf(argv[1], "%x", &aux); actorCap -> ui.uiTail = aux;
sscanf(argv[2], "%x", &aux); actorCap -> key.keyHead = aux;
sscanf(argv[3], "%x", &aux); actorCap -> key.keyTail = aux;
}
-->
The following Imakefile located in the same directory as the source les is
used to build the library:
--> cat Imakefile
INCLUDES=-I$(CHORUS)/include
SRCS=readCap.c fprintCap.c
OBJS=readCap.o fprintCap.o
LibraryTarget(libutilities.a, $(OBJS))
Depend($(SRCS))
-->
It is worth noting that the INCLUDES variable of the make environment has
been overloaded. The $(CHORUS)/include is added to the list of directories to
search for header les as it contains the utilities.h header le that we use.
We generate the corresponding Makefile and the dependencies as we did earlier,
and nally we use the make command:
76 Chapter 4. Producing ChorusOS applications
--> ChorusOSMkMf $BUILD_DIR; make depend; make
depend readCap.c fprintCap.c
cc readCap.c
cc fprintCap.c
ar libutilities.a
-->
We check that the library contains the two object les and install it in the lib86
directory:
--> ar tv libutilities.a
rw-r--r-- 1001/ 101 1064 dec 14 19:05 1999 readCap.o
rw-r--r-- 1001/ 101 1004 dec 14 19:05 1999 fprintCap.o
--> mv libutilities.a $CHORUS/lib86
-->
Remark. In the codes of the fprintCap and readCap functions given above,
we use the structure of a capability. A version of these functions which is
transparent to this structure is given below:
--> cat fprintCap2.c
#include "utilities.h"
void fprintCap(FILE *stm, char *message, KnCap *actorCap) {
int ind, *array;
fprintf(stm, "%s:", message);
array = (int *) actorCap;
for (ind = 0; ind < sizeof(KnCap) / sizeof(int); ind ++)
fprintf(stm, " %x", array[ind]);
putc('\n', stm);
fflush(stm);
}
--> cat readCap2.c
#include "utilities.h"
void readCap(char *argv[ ], KnCap *actorCap) {
int ind, *array;
array = (int *) actorCap;
for (ind = 0; ind < sizeof(KnCap) / sizeof(int); ind ++)
sscanf(argv[ind + 1], " %x", array + ind);
}
-->
b) Using a library
The noDelay2 application used here is a modied version of the noDelay1 ap-
plication which uses some basic services provided by the kernel:
the capability of the current actor is retrieved by calling the actorSelf prim-
itive and is printed by calling the fprintCap function of our library (therefore,
the corresponding header le is included in the source le);
4.5. The production's environment 77
then the local identier of the main thread of the actor is obtained by calling
threadSelf and it is printed;
nally, a message is printed before the main thread enters the sleeping state
after calling threadDelay with the K NOTIMEOUT value of the parameter.
The corresponding C program is the following:
--> cat $CHORUS/sources/utilities/noDelay2/noDelay2.c
#include "utilities.h"
KnCap actorCap;
main( ) {
actorSelf(&actorCap);
fprintCap(stdout, "Actor's capability", &actorCap);
printf("Thread's identification: %d\n", threadSelf( ));
printf("Hello. I'm gonna sleep\n");
fflush(stdout); /* useful if stdout has been redirected */
threadDelay(K_NOTIMEOUT);
printf("After threadDelay\n"); /* later, if thread is aborted */
}
We now give the corresponding Imakefile which is located in the same directory
as the application. Essentially, it gives information for retrieving the header le
through the INCLUDES variable and requests the linkage of the library in the
production rules for the user and supervisor binaries:
--> cat $CHORUS/sources/utilities/noDelay2/Imakefile
INCLUDES=-I$(CHORUS)/include
SRCS=noDelay2.c
OBJS=noDelay2.o
UserActorTarget(noDelay2_u, $(OBJS), $(CHORUS)/lib86/libutilities.a)
SupActorTarget(noDelay2_s.r, $(OBJS), $(CHORUS)/lib86/libutilities.a)
Depend($(SRCS))
-->
The binaries are now produced and installed:
--> cd $CHORUS/sources/utilities/noDelay2
--> ChorusOSMkMf $BUILD_DIR; make depend; make
[
........ more lines ]
.......
--> mv noDelay2_s.r noDelay2_u $CHORUS/bin86
--> ls $CHORUS/bin86/noDelay2*
/ens/jmr/CHORUS/bin86/noDelay2_s.r
/ens/jmr/CHORUS/bin86/noDelay2_u
-->
A user actor is dynamically created and loaded to execute the application:
--> neon-n noDelay2_u &
[1] 6316
--> started aid = 2
78 Chapter 4. Producing ChorusOS applications
Actor's capability: 20000017 869da80a 2 1
Thread's identification: 9
Hello. I'm gonna sleep
and the actor is killed:
--> rsh neon akill 2
-->
To install the dynamic library in the /lib directory in the root le system
mounted on the neon system:
--> rsh neon mount | grep root
root_device on / (pdevfs)
134.157.168.9:/home/jmr/CHORUS/r4/build/root/ on / (nfs)
--> cp $CHORUS/lib86/libutilities.so /home/jmr/CHORUS/r4/build/root/lib
This call returns an opaque handle to the calling thread which any thread of
the actor may use on subsequent calls to dlsym and dlclose. Here pathname
is the path name of the shared object to be opened. Dierent values of mode
are recognized (see the man command for more details). Here, we just mention
the RTLD NOW value which is eectively a request that all necessary relocations
are performed when the object is rst loaded.
2 get the address of a symbol (typically a function) in a shared object by
calling
#include <cx/dlfcn.h>
void *dlsym(
void *handle,
const char *name
);
4.6. Dynamic actors 81
This call returns the address of the symbol whose name is given in the object
referenced by the handle. Typically, the returned value is a pointer to the
function whose name is given.
2 close a shared objet by calling
#include <cx/dlfcn.h>
int dlclose(void *handle);
This call dissociates a shared object, previously opened by calling dlopen, from
the current actor. Upon successful completion, 0 is returned. If the object could
not be closed, or if handle does not refer to an open object, the call returns a
non-zero value (the dlerror function may be called to get a null-terminated
character string describing this error).
2 the next example is a version of the noDelay application as a dynamic
program:
--> cat $CHORUS/sources/utilities/noDelay2/dynProg/noDelay2.c
#include "utilities.h"
#include <cx/dlfcn.h>
KnCap actorCap;
void *handle;
void (*function)( );
main( ) {
handle = dlopen("libutilities.so", RTLD_NOW);
if(handle == NULL) {
printf("Can't open libutilities.so\n");
exit(2); }
function = (void (*)( )) dlsym(handle, "badFunction");
if(function == NULL)
printf("Can't find badFunction\n");
function = (void (*)( )) dlsym(handle, "fprintCap");
if(function == NULL) {
printf("Can't find fprintCap\n");
exit(2); }
actorSelf(&actorCap);
(*function) (stdout, "Actor's capability", &actorCap);
printf("Thread's identification: %d\n", threadSelf( ));
printf("Hello. I'm gonna sleep\n");
fflush(stdout);
threadDelay(K_NOTIMEOUT);
printf("After threadDelay\n");
}
The Imakele to build the binary is similar to the one used in 4.6.3.1. The
program is installed in the bin86 as noDelay2.dynProg u.
82 Chapter 4. Producing ChorusOS applications
4.6.4 Running a dynamic program
First of all, the archive that has been booted on the target must include the
DYNAMIC LIB feature.
--> rsh neon arun cs -lM
.............. DL ...............
-->
Various functions can be used to manipulate unique identiers, these are de-
tailed below.
5.1.1.3 Clearing a unique identier
Given a pointer ui to a unique identier, the function call
#include <ipc/chId.h>
int uiClear(KnUniqueId *ui);
returns 1 if the unique identier pointed to by ui is not equal to the null unique
identier and 0 otherwise.
5.1.1.4 Comparing unique identiers
The function call (dened as a macro)
#include <ipc/chId.h>
int uiEqual(
KnUniqueId *ui1,
KnUniqueId *ui2
);
returns 1 if the unique identiers pointed to by ui1 and ui2 are equal and 0
otherwise.
5.1. Naming in ChorusOS 85
5.1.1.5 Sites and unique identiers
The function call
#include <ipc/chId.h>
int uiSite(
KnUniqueId *ui,
unsigned long site
);
returns the value of the unique identier which is predened for the site whose
site number is site.
The function call
#include <ipc/chId.h>
int uiGetSite(KnUniqueId *ui);
returns the site information contained in the unique identier pointed to by ui.
The function call
#include <ipc/chId.h>
int uiIsLocal(KnUniqueId *ui);
checks whether the given unique identier is located on the current site at
present. The function returns 1 if this is case and 0 otherwise.
5.1.1.6 Building a unique identier
A unique identier corresponding to a particular type of object may be built
and returned at the ui address by calling the function
#include <ipc/chId.h>
int uiBuild(
KnUniqueId *ui,
unsigned int type,
unsigned int site,
unsigned int head,
unsigned int tail
);
where
type is the type of the unique identier with the following recognized values:
- K UIPORT: the unique identier will be recognized as a unique identier
for a port for the IPC module. This type of identier can be used when
calling portDeclare (cf. 13.3.1.4);
86 Chapter 5. General services
- K UIGROUP: the unique identier will be recognized as a unique identier
for a port group by the IPC module and will be usable when calling
grpPortInsert (cf. 13.3.2.3);
- K UISITE: the unique identier of a site;
site is a site number which, if not null, will be used as a hint by the kernel
when building the unique identier;
head and tail represent the stamp of the unique identier: tail is a 32 bits
value and head is a 13 bits value.
The function returns K OK upon success, and K EFAULT if any data is outside
the address space of the current actor. The call uiSite (ui, site) is equivalent
to uiBuild (ui, K UISITE, site, 0, 0).
5.1.2 Capabilities
5.1.2.1 Denition and characteristics
Capabilities are the most general type of identiers: a capability encapsulates
a unique identier and provides means for protecting and restricting access to
objects. Capabilities are associated to actors, groups of ports and segments.
If we consider a server managing dierent objects, a common use of capabilities
consists in identifying the dierent objects managed by the server by capabilities
built with a common unique identier (typically identifying a port where the
server should be contacted), and a key managed by the server which identies
the object and possibly denes specic protection attributes of the object.
5.1.2.2 The KnCap type
From the programmer's point of view a capability corresponds to the KnCap
type dened as
struct KnCap {
KnUniqueId ui;
KnKey key;
};
typedef struct KnCap KnCap;
KnKey key
KnUniqueId ui
the integer identifying the local site is returned at address site. The K OK value
is returned except if site is out of the address space (in that case K EFAULT is
returned).
5.1.5 Example
In the next example, the application uses dierent functions of the API we
presented in preceding sections.
--> cat $CHORUS/sources/utilities/general/general.c
#include <chorus.h>
KnUniqueId ui;
int site;
main( ) {
uiLocalSite(&site);
printf("Local site number: 0x%x [%d]\n", site, site);
uiBuild (&ui, K_UISITE, site, 0, 0); /* uiSite(&ui, site); */
printf("Predefined UI: 0x%x 0x%x\n", ui.uiHead, ui.uiTail);
88 Chapter 5. General services
portUi(&ui, K_DEFAULTPORT);
printf("Default port's UI: 0x%x 0x%x\n", ui.uiHead, ui.uiTail);
if(uiIsLocal(&ui))
printf(" It is recognized\n");
else
printf(" It is not recognized\n");
site = uiGetSite(&ui);
printf("information on site extracted from 0x%x 0x%x: 0x%x\n",
ui.uiHead, ui.uiTail, site);
}
--> neon general_u
started aid = 2
Local site number: 0x869da80a [-2036488182]
Predefined UI: 0x0 0x869da80a
Default port's UI: 0x2000001c 0x869da80a
It is recognized
information on site extracted from 0x2000001c 0x869da80a: 0x869da80a
-->
gets the value associated with the tunable parameter identied by parameter
in the module whose name is moduleName. The parameter argument may be
K GETCONF VERSION when querying for the version of the module. In the SEW
2.0 product, version numbers are all set to 1 except for the IPC feature which
is set to 1 when it is local, and to 2 when IPC REMOTE is enabled.
If the call succeeds, *ptrValue is the value of the parameter returned and K OK
is returned by the function. Otherwise, a negative error code is returned:
return value error type
K ENOTAVAILABLE non existing module in the system
K EFAULT address out of address space
Hence, in order to check whether a feature has been congured within the sys-
tem, an application may call the function and check whether it returns K OK
(the feature is congured) or K ENOTAVAILABLE (it is not).
In the next example, we extract the values of the tunable parameters corre-
sponding to the maximum numbers of actors and threads the system can man-
age and the scheduling module which is used:
--> cat $CHORUS/sources/utilities/getConf/getConf.c
#include <stdio.h>
#include <exec/chModules.h>
#include <sched/chSched.h>
#include <sched/chFifo.h>
main( ) {
int result, value;
result = sysGetConf(K_MODULE_EXEC_NAME, K_GETCONF_ACTOR_MAX, &value);
printf("Maximum number of actors: %d\n", value);
result = sysGetConf(K_MODULE_EXEC_NAME, K_GETCONF_THREAD_MAX, &value);
printf("Maximum number of threads: %d\n", value);
result = sysGetConf(K_MODULE_SCHED_NAME, K_GETCONF_VERSION, &value);
printf("Scheduler's version: %d\n", value);
}
We rst execute the application on the system neon where the default scheduling
module (based on fo policy) is active:
90 Chapter 5. General services
--> neon getConf_u
started aid = 2
Maximum number of actors: 64
Maximum number of threads: 128
Scheduler's version: 1
-->
Now we have generated a system where the ROUND ROBIN feature has been ac-
tivated, have loaded it on the carbon system where we execute the getConf
application :
--> carbon getConf_u
started aid = 2
Maximum number of actors: 64
Maximum number of threads: 128
Scheduler's version: 2 (= a dierent scheduler a dierent result
-->
is a request for the value of the Chorus conguration environment variable whose
name is envName. The ptrValue argument is the address to which the result
is to be written. On input, *ptrSize should be initialized to the size in bytes
of memory which has been allocated to ptrValue, and in output it gives the
number of bytes of the value (including the null terminating character). If the
call succeeds K OK is returned, and otherwise a negative error code is returned:
return value error type
K EFAIL undened variable
K ENOMEM not enough space allocated to ptrValue and
*ptrSize is the necessary space
K EFAULT address out of address space
5.2. Getting information about ChorusOS 91
5.2.2.2 Setting a value: sysSetEnv
The function call
#include <exec/chEnv.h>
int sysSetEnv(
const char *envName,
const char *envValue
);
replaces the current value of the envName variable of the ChorusOS environment
by envValue if this variable is dened. If the variable is not dened, the variable
and the value are inserted in the environment. The envValue argument must
point to a null terminated string. The call returns K OK if it succeeds, otherwise
a negative error code is returned:
return value error type
K EINVAL length of envName is 0
K ENOMEM not enough free space (maximum size is limited to a tunable
parameter [default is 1K])
K EFAULT address out of address space
5.2.2.4 Example
--> cat $CHORUS/sources/utilities/envir/envir.c
#include <stdio.h>
#include <chorus.h>
main( ) {
char buf[256];
int result, size = 256;
result = sysGetEnv("TARGET", buf, &size);
92 Chapter 5. General services
if (result != K_OK)
fprintf(stderr, "sysGetEnv(TARGET): %s\n", strSysError(result));
else
printf("value of TARGET: %s\n", buf);
size = 256;
result = sysGetEnv("HOST", buf, &size);
if (result != K_OK)
fprintf(stderr, "sysGetEnv(HOST): %s\n", strSysError(result));
else
printf("value of HOST: %s\n", buf);
result = sysSetEnv("VAR", "Just to see");
if (result != K_OK)
fprintf(stderr, "sysSetEnv(VAR): %s\n", strSysError(result));
size = 256;
result = sysGetEnv("VAR", buf, &size);
if (result != K_OK)
fprintf(stderr, "sysGetEnv(VAR): %s\n", strSysError(result));
else
printf("value of VAR: %s\n", buf);
result = sysUnsetEnv("VAR");
if (result != K_OK)
fprintf(stderr, "sysUnsetEnv(VAR): %s\n", strSysError(result));
result = sysGetEnv("VAR", buf, &size);
if (result != K_OK)
fprintf(stderr, "sysGetEnv(VAR): %s\n", strSysError(result));
else
printf("value of VAR: %s\n", buf);
}
The command is rst executed on a system where the only dened environment
variable is OS CONF:
--> neon envir_u
started aid = 2
sysGetEnv(TARGET): Transaction failed
sysGetEnv(HOST): Transaction failed
value of VAR: Just to see
sysGetEnv(VAR): Transaction failed
-->
We rst generate a new system where the variables TARGET, and HOST are de-
ned:
--> cd /home/jmr/CHORUS/r4/build
--> configurator -p conf/extended
--> configurator -setenv TARGET=carbon
--> configurator -setenv HOST=fluorine
--> make chorus
[ ]
.... many more lines .....
-->
5.3. Input/output from the system console 93
We then execute the envir application on the carbon machine where this con-
guration has been booted:
--> carbon envir_u
started aid = 2
value of TARGET: carbon
value of HOST: fluorine
value of VAR: Just to see
sysGetEnv(VAR): Transaction failed
-->
attempts to read one character from the system console into the buer pointed
to by buer. If there is no character to be read, the calling thread of the actor
in not blocked. The function returns the number of characters that have been
read (0 or 1) and the K EFAULT value if buer is out of the address space.
5.3.4 Example
-1-> cat $CHORUS/sources/general/console/console.c
#include <exec/chIo.h>
char bufferIn[256];
char bufferPoll[16];
char *bufferOut = "Message from the system\n";
main( ) {
int result;
result = sysWrite(bufferOut, strlen(bufferOut));
printf("sysWrite --> %d\n", result);
result = sysWrite("Enter new message: ", 19);
result = sysRead(bufferIn, 256);
printf("sysRead --> %d\n", result);
result = sysWrite(bufferIn, 10);
result = sysWrite("\n", 1);
while ((result = sysPoll(bufferPoll)) <= 0) ; /* busy loop */
printf("From first sysPoll: %d\n", result);
while ((result = sysPoll(bufferPoll)) <= 0) ; /* busy loop */
printf("From second sysPoll: %d %c\n", result, bufferPoll[0]);
}
-1-> neon console_u < the console >
started aid = 22
Message from the system
sysWrite --> 24
Enter new message: abcdefghijklmnopq
sysRead --> 17
abcdefghij
From first sysPoll: 1
-2-> rsh neon arun cs -la 22
started aid = 23
ABCD (=
from keyboard
From second sysPoll: 1 A
-1->
error: not a local actor
ChorusOS r4.0.0 Site 0 Time 39m 44
5.4. Rebooting the local system 95
5.4 Rebooting the local system
A thread executing with the supervisor privilege may request a reboot of the
local system by calling the primitive
#include <exec/chPanic.h>
int sysReboot(KnRebootReq *rebootReq);
supervisor user
actor : K SUPACTOR
c actor: AFX SUPERVISOR SPACE
returns the capability of the current actor into the structure pointed to by ac-
torCap, which must have been previously allocated.
The function returns K OK except if an address error is detected (actorCap point-
ing outside the address space of the actor), in which case the K EFAULT value is
returned.
6.2.1.2 Symbolic name
A symbolic name may be associated to an actor. This name is a string whose
length is at most K ACTORNAMEMAX (default value is 16). The old name of an
actor may be retrieved and/or a new name may be associated to an actor by
calling the function
#include <exec/chExec.h>
int actorName(
KnCap *actorCap,
VmAddr oldName,
VmAddr newName
);
returns at cactorCap the capability of the corresponding c actor on the site. Like
the cs command, this function gets the capabilities of actors. This information
should be condential, as once it is known, services like deleting or changing
attributes of the corresponding actors can be accessed. Thus, its use is restricted
to trusted c actors.
The function returns 0 if it is successful and -1 otherwise. In that case, the
external errno variable is set to a value corresponding to the error:
value of errno error type
EINVAL aid is not a valid c actor's local identier
EFAULT cactorCap points outside the address space of the c actor
ESRCH aid is not the identier of a c actor on the site
EPERM the calling c actor is not trusted
Stopped Active
actorStop
K STOPPED K ACTIVE
actorCreate
stops the actor whose capability is pointed to by actorCap. All threads of the
actor are stopped. The eect may not be immediate on the actor's threads
currently engaged in a system call, but the system guarantees that there will
not be a return from this call until the actor is activated.
The function call
#include <exec/chExec.h>
int actorStart(KnCap *actorCap);
gets the current privilege of the actor whose capability is pointed to by actorCap
at the address oldPriv if it is not NULL. Furthermore a supervisor thread or a
thread belonging to a SYSTEM actor may change the privilege of the actor to
the value pointed to by newPriv If it is not NULL. It is only possible to transform
a USER actor into a SYSTEM actor and vice-versa.
6.2.4 Example
In the following example, a c actor is created and loaded with the command
arun with argument charactActor u. This actor writes some of its attributes
to its standard output before setting itself into the stopped state. Another
actor is created for reactivating the stopped actor by executing the actorStart
program. The same task is performed successively for all three types of actor.
-1-> cat $CHORUS/sources/actors/charactActor/charactActor.c
#include "utilities.h"
#include <exec/chExec.h>
KnCap actorCap;
KnActorPrivilege privilege;
int status, localId;
char oldName[256]; /* for getting actor's old name */
char *newName = "NAME's EXAMPLE"; /* actor's new name */
main( ) {
printf("Hello. I'm the new actor\n");
(void) actorSelf(&actorCap); /* getting actor's capability */
/* writing actor's capability */
fprintCap(stdout, "My capability is", &actorCap);
if((localId = agetId( )) != -1) /* getting the local identifier */
printf("I am a c_actor and my local identifier is %d\n", localId);
else
perror("agetId");
/* getting the privilege without modifying it */
actorPrivilege(K_MYACTOR, &privilege, NULL);
6.2. Actors attributes 103
switch(privilege) {
case K_SUPACTOR : printf("I'm a supervisor actor\n"); break;
case K_SYSTEMACTOR : printf("I'm a system actor\n"); break;
case K_USERACTOR : printf("I'm a user actor\n");
}
/* getting symbolic name and changing it */
actorName(K_MYACTOR, oldName, newName);
printf("My old name was: %s\n", oldName);
printf("I'm going to stop ....\n");
actorStop(K_MYACTOR); /* stopping the actor */
printf("Back to the active world !!!\n"); /* after being activated */
}
-1-> neon charactActor_u
started aid = 23
Hello. I'm the new actor
My capability is: 20000073 869da80a 17 2d3
I am a c_actor and my local identifier is 23
I'm a user actor
My old name was: charactActor_u
I'm going to stop ....
We can check that the stopped actor is restarted, the message relating to the
printf call appears on the corresponding screen:
Back to the active world !!! (= message from the restarted actor
-1->
We now create a supervisor actor on the same system by requesting the ex-
ecution of the supervisor version (the use of the -S option is not necessary,
information in the binary le is sucient for detecting that a supervisor has to
be created).
-1-> neon charactActor_s.r
started aid = 23
Hello. I'm the new actor
My capability is: 20000076 869da80a 17 2eb
I am a c_actor and my local identifier is 23
I'm a supervisor actor
My old name was: charactActor_s.
I'm going to stop ....
which gets the status of all the actors created on the current site and writes
the information about every actor (capability and status) at the address stat in
the address space of the calling actor. This type of call is restricted to threads
running with SUPERVISOR privilege or whose actor is a SYSTEM actor.
The buSize argument gives the size (expressed as a number of bytes) of the
buer allocated at the address stat. The only value of
ags which is currently
valid is 0. If the call succeeds, the function returns the number of actors existing
on the site. If it fails, a negative value is returned:
- K EFAULT: an argument is outside the address space of the actor;
- K EPRIV: the calling thread is not allowed to get the information (this call
is restricted to trusted entities).
In the actorStat application whose source is given below, the list of actors
running on the site is retrieved by calling actorStat and for each actor, its
capability and its status are displayed (they are directly extracted from the
corresponding entry in the buer). For every actor, its privilege and its symbolic
name are retrieved by calling actorPrivilege and actorName.
-2-> cat $CHORUS/sources/actors/actorStat/actorStat.c
#include "utilities.h"
#include <exec/chExec.h>
#define N_ACTORS 50
106 Chapter 6. Actors
KnActorStat stat[N_ACTORS];
KnActorPrivilege actorPriv;
char name[K_ACTORNAMEMAX + 1];
main( ) {
int result, ind;
result = actorStat(0, stat, sizeof(stat));
if (result < 0) {
fprintf(stderr, "Error on actorStat : %s\n", strSysError(result));
exit(1);
}
printf("There are %d actors on the site\n", result);
for(ind = 0; ind < result; ind ++) {
actorName(&stat[ind].asCap, name, NULL);
actorPrivilege(&stat[ind].asCap, &actorPriv, NULL);
printf("%-20s", name);
printf("%-8s", (stat[ind].asStatus == K_ACTIVE) ?
"ACTIVE" : "STOPPED");
printf("%-5s", (actorPriv == K_SUPACTOR) ? "SUP" : "USER");
fprintCap(stdout, "capability", &stat[ind].asCap);
}
}
-2-> neon actorStat_u
started aid = 22
Error on actorStat : Privilege violation (=
calling thread is not trusted
-2-> neon actorStat_s.r
started aid = 22
There are 22 actors on the site
kern ACTIVE SUP capability: 20000001 869da80a 1 1
D_pci ACTIVE SUP capability: 20000003 869da80a 3 0
D_pcienum ACTIVE SUP capability: 20000004 869da80a 4 4
[
..... more lines ] ....
AM ACTIVE SUP capability: 20000013 869da80a 13 0
ADMIN ACTIVE SUP capability: 20000014 869da80a 14 14
C_INIT ACTIVE SUP capability: 20000015 869da80a 15 1
actorStat_s.r ACTIVE SUP capability: 2000007f 869da80a 16 0
NAME's EXAMPLE STOPPED SUP capability: 20000076 869da80a 17 2eb
-2->
The last two actors in these results are the current user actor executing the
actorStat application and the stopped supervisor actor we created in the pre-
vious section.
6.3.2 Getting status of all c actors on the site
For every c actor created on the site, the actor manager (AM) owns a descriptor
of this actor. This descriptor is the astatEntry type and has the following elds:
6.3. Getting status of actors on the site 107
int astatFlags; /* actor's status */
int astatAid; /* actor's local identier */
int astatUid; /* actor's user Id */
char astatName[ ]; /* actor's symbolic name */
The function call
#include <am/astat.h>
int astat(
astatEntry *entries,
int maxEntries
);
gets the entries for to the c actors created on the local site.
At most maxEntries entries will be lled by the function and the return value
is the number of c actors on the site.
The
ag ASTAT DEBUGGED CACTOR is used to indicate that the actor is being
debugged.
Upon success, the call returns the number of c actors that are currently active
on the site. If an error occurs, -1 is returned and errno is set to EFAULT due
to a value of entries being outside the address space.
The astat application whose code is given below displays information concern-
ing all c actors known on the local site:
-2-> cat $CHORUS/sources/actors/astat/astat.c
#include "utilities.h"
#include <am/astat.h>
#define N_ACTORS 50
astatEntry stat[N_ACTORS];
char name[K_ACTORNAMEMAX + 1];
main( ) {
int nbAct, result, ind;
KnCap capa;
nbAct = astat(stat, N_ACTORS);
if (nbAct == -1) {
perror("astat");
exit(1);
}
printf("There are %d c_actors on the site\n", nbAct);
for(ind = 0; ind < nbAct; ind ++) {
printf("Local id: %2d ", stat[ind].astatAid);
result = acap(stat[ind].astatAid, &capa);
if (result != -1) {
actorName(&capa, name, NULL);
printf("%-20s", name);
fprintCap(stdout, "capability", &capa);
}
108 Chapter 6. Actors
else
putchar('\n');
}
}
-2-> neon astat_s.r
There are 4 c_actors on the site
Local id: 22 astat_s.r capability: 20000080 869da80a 16 1
Local id: 23 NAME's EXAMPLE capability: 20000076 869da80a 17 2eb
Local id: 21 C_INIT capability: 20000015 869da80a 15 1
Local id: 20 ADMIN capability: 20000014 869da80a 14 14
-2->
We now kill the stopped supervisor c actor by using the akill command before
executing the astat application once again:
-2-> rsh neon akill 23
-2-> neon astat_s.r
started aid = 23 (= id 23 is reused for a new actor
There are 3 c_actors on the site
Local id: 23 astat_s.r capability: 20000081 869da80a 17 0
Local id: 21 C_INIT capability: 20000015 869da80a 15 1
Local id: 20 ADMIN capability: 20000014 869da80a 14 14
-2->
actorCreate actorCreate
CHORUS Kernel
A call returns 0 when successful and -1 otherwise (errno will be set to ESRCH,
EPERM or EFAULT).
The dierent parameters of the function are interpreted the same way as when
creating Unix processes or loading a new program in an existing process:
- argc is the arguments count (at least one);
- argv is an array of character pointers corresponding to the parameters;
- arge is an array of character pointers corresponding to the environment
of the actor.
Six primitives are available for requesting the actor manager (AM); they look
like exec Unix system calls. The names of these primitives have a common
afexec prex and dier as follows:
by the way they receive the arguments for the main function. It is a list ended
by a NULL pointer for afexecl, afexecle and afexeclp and it is a vector for
afexecv, afexecve and afexecvp;
by the fact that the le to load is searched (afexeclp and afexecvp) using
the PATH variable in the current environment of C INIT;
by the fact that a new environment is explicitly provided (afexecle and
afexecve) or inherited from the caller.
Their general form is
int afexecxy (char *pathName, KnCap *actorCap, AcParam *param, ...)
It is important to note that there is no search for the le when this primitive is
used; the pathname is either absolute or is relative to the current directory.
In the following example, a c actor is created to execute the trivialAct pro-
gram. The command will be run with:
a rst argument specifying the type of the new actor: u for a user actor, t for
a trusted user actor (system actor) and s for a supervisor actor;
a second argument specifying the pathname of the le to be loaded and exe-
cuted by the new actor;
6.4. Creating actors 113
other parameters used as parameters for the main function executed by the
main thread of the new actor. These parameters are used to build a vector of
parameters.
--> cat $CHORUS/sources/actors/afexecv/afexecv.c
#include "utilities.h"
#include <am/afexec.h>
AcParam newActorAttrib; /* attributes of the new c_actor */
char *newActorParam[10]; /* to build the vector of parameters */
KnCap newActorCap; /* new actor's capability */
In the rst call, the pathname used is trivialAct u; as there is no search for
the le, the call to afexecv fails since the le is not referenced in the current
directory (which is the root of the mounted le system):
--> neon afexecv_u u trivialAct_u aaaa bbbb
started aid = 23
error on afexecv: No such file or directory
-->
When trying to create a system or a supervisor actor, the call also fails if the
calling actor is a user actor; this type of actor can only be created by a system
or a supervisor actor:
--> neon afexecv_u t /mnt/trivialAct_u aaaa bbbb
started aid = 22
error on afexecv: Not owner
--> rsh neon arun -T /mnt/afexecv_u t /mnt/trivialAct_u xx yy
started aid = 22
A new actor has been created
Its local identifier is 23
Its capability is: 20000087 869da80a 17 34f
Hello! I am the trivial actor
I have 2 parameters
==> xx
==> yy
--> rsh neon arun -T /mnt/afexecv_u s /mnt/trivialAct_s.r A B
started aid = 23
A new actor has been created
Its local identifier is 22
Its capability is: 20000089 869da80a 16 360
Hello! I am the trivial actor
I have 2 parameters
==> A
==> B
-->
In the afexecvp example given below we just changed the call to afexecv
afexecv(argv[2], &newActorCap, &newActorAttrib, newActorParam)
We also add /mnt into the list of directories dened by the PATH variable of the
C INIT environment.
--> rsh neon env | grep PATH
PATH=/bin
--> rsh neon setenv PATH /bin:/mnt
--> rsh neon env | grep PATH
PATH=/bin:/mnt
--> neon afexecvp_u u trivialAct_u AAAA BB
started aid = 22
A new actor has been created
Its local identifier is 23
Its capability is: 2000008b 869da80a 17 36c
Hello! I am the trivial actor
I have 2 parameters
==> AAAA
==> BB
-->
As the /mnt directory now belongs to the list of directories dened by the vari-
able PATH, the call to afexecvp succeeds when giving trivialAct u as the
pathname of the le to load (we used the same property when running the com-
mand with arun without specifying the absolute path of the le afexecvp u).
c) The afexecve primitive
When using afexecv or afexecvp, the new actor inherits the environment of
the calling actor. By using the afexecve primitive, it is possible to dene a new
environment for the new actor. The new actor will not inherit any variables
from the environment of the calling actor. This environment is presented as an
array of strings having the form variable=value terminated by a NULL entry.
116 Chapter 6. Actors
The prototype of the function is dened as
#include <am/afexec.h>
int afexecve(
const char *pathName,
KnCap *actorCap,
const AcParam *param,
const char *const *argv,
const char *const *arge
);
#include <am/afexec.h>
int afexecl(
const char *pathName,
KnCap *actorCap,
const AcParam *param,
const char *arg0,
...,
const char *argn,
(char *) NULL
);
where:
cactorCap points to the capability of the target actor which must be a c actor.
Usually, aload is the rst event that is applied to an actor that has been created
by acreate. Nevertheless, it can be applied to any c actor as many times as
necessary. If the address space of the actor is not empty, the address range that
is to be used by the target actor must be free and the actor must be in a state
where its address space may be changed;
if it not NULL, pathName names an ordinary executable le comprising a
header, a text segment and a data segment that will be loaded;
if it is not NULL and if the operation succeeds, entryPoint will contain the ad-
dress of the entry point in the program as determined by the loader. Sucient
space to store a KnPc object must have been allocated to entryPoint;
if not NULL, ldParam points to an object corresponding to the packed rep-
resentation of the environment and the parameters which will be copied in a
region of the target actor. This object may be built from given values of argv
and arge with the function alParamBuild as described in c. If it is NULL, the
arguments and environment currently installed are not aected.
Upon success the call returns 0. Otherwise a value of -1 is returned and errno
is set to indicate the error:
value of errno error type
EPERM calling thread is not allowed to create such a c actor
ENOENT a component of the pathname does not exist
ENOTDIR a component of the pathname is not a directory
EFAULT address out of address space
EACCES search permission denied for a directory
EINVAL inconsistent attributes
ags
ENAMETOOLONG name of component of pathName or total name too long
E2BIG total length of arguments is too long
EIO I/O error when reading the le
ENXIO the le is not an ordinary le
ENOEXEC not a proper le
ESRCH cactorCap does not correspond to a valid c actor
ENOMEM address space of the target actor is not free
122 Chapter 6. Actors
b) Example 1
In the following example, a program is loaded for the actor we created with
acreate in the previous section, by using the following application:
-2-> cat $CHORUS/sources/actors/aload1/aload1.c
/* usage: aload1_u uiHead uiTail keyHead keyTail pathname */
#include "utilities.h"
#include <am/afexec.h>
int result;
KnCap cactorCap;
KnPc entryPoint;
The call stores, at the address specied by buer, the packed representation of
arguments dened by argv and environment dened by arge, buer may then be
passed as third argument to the aload function. The value of buerSize gives
the amount of space allocated to this location which must be large enough to
hold the packed representation. By convention, the AlParam object built should
not contain more than ARG MAX bytes.
The functions which follow are used by the startup of the C library to build
the arguments and the environment in the usual representation. The address
where the packed representation of the arguments and environment have been
loaded is retrieved by calling the function
#include <am/afexec.h>
int agetalparam(AlParam **argp);
The function
#include <am/afexec.h>
unsigned int alParamSize(AlParam *alParamp);
returns the size of the packed representation stored at the location alParamp.
Finally, the arguments and environment are retrieved by calling
#include <am/afexec.h>
int alParamUnpack(
AlParam *alParamp,
int *argcp,
char ***argvp
);
d) Example 2
In the following, an empty user c actor is rst created by calling the acreate
primitive. Then, its arguments (deduced from the arguments of the calling
actor) and its environment (identical to the environment of the calling actor)
are packed before calling aload:
124 Chapter 6. Actors
-2-> cat $CHORUS/sources/actors/aload2/aload2.c
/* usage: aload2_u pathname arg1 arg2 ... */
#include <stdio.h>
#include <chorus.h>
#include <am/afexec.h>
#include "utilities.h"
KnCap cactorCap; /* capability of the new actor */
AcParam param; /* attributes of the new actor */
KnPc entryPoint; /* to get the entry point from the loader */
char *pointer;
AlParam *alParamP;
int result;
main(int argc, char *argv[ ], char **arge) {
if(argc < 2) {
fprintf(stderr, "Bad usage ....\n");
exit(1); }
/* create an empty c_actor */
param.acSite = 0;
param.acFlags = AFX_USER_SPACE;
param.acStdout = NULL;
result = acreate(&cactorCap, ¶m);
if(result == -1)
perror("acreate failed");
else {
printf("A new c_actor has been created with local id: %d\n",
result);
fprintCap(stdout, "and capability", &cactorCap);
}
/* build the packed list of arguments */
pointer = (char *) malloc(0x1000);
alParamP = (AlParam *) pointer;
alParamBuild(alParamP, 0x1000, argv + 1, arge);
result = aload(&cactorCap, argv[1], alParamP, &entryPoint);
if(result == -1)
perror("aload failed");
else
printf("aload succeeded !!!\n");
/* astart(&cactorCap); */
}
We use this application to create an actor and load the trivialAct u with
three arguments:
-2-> neon aload2_u /mnt/trivialAct_u aaa bbbbb cccc
started aid = 22
A new c_actor has been created with local id: 24
and capability: 20000097 869da80a 18 3d3
aload succeeded !!!
6.4. Creating actors 125
6.4.4.3 Activating a c actor: astart
The function call
#include <am/afexec.h>
int astart(KnCap *cactorCap);
We rst apply this application to the rst actor we created and loaded:
-3-> neon astart_u 20000091 869da80a 17 0
started aid = 22
astart succeeded !!!
-3->
We can see on the screen corresponding to the actor (the rst screen) the dif-
ferent messages that it prints:
Actor's capability: 20000091 869da80a 17 0
Thread's identification: 8
Hello. I'm gonna sleep
126 Chapter 6. Actors
We now kill the actor by using the akill command:
-3-> rsh neon akill 23
-3->
We now apply the astart application with a list of arguments to the second
actor we created and loaded:
-1-> neon astart_u 20000097 869da80a 18 3d3
started aid = 23
astart succeeded !!!
-1->
The new actor inherits exception handlers and abort handlers (see 13.2 and 13.3)
from the initializing actor whose capability is pointed to by actorInitialCap. It
is worth noting that the initializing actor may be dierent from the calling actor
even if they are generally the same (argument actorInitialCap set to the value
K MYACTOR).
The privilege argument may be K USERACTOR, K SYSTEMACTOR or K SUPACTOR. A
SYSTEM actor can only be created by a thread running with the SUPERVISOR
6.4. Creating actors 127
privilege or belonging to a SYSTEM actor. Furthermore, the new actor may
be created with the SUPERVISOR privilege: memory space will be allocated
in the supervisor address space and all its threads will run with SUPERVISOR
privilege.
In the status argument the following
ags may be set:
K STOPPED: the actor is created in the stopped state. None of its threads will
be able to run when created until the actor is put into the active state by a call
to actorStart;
K ACTIVE: the new actor is active.
K OK is returned if the actor was created. Otherwise, a negative error code is
returned:
return value interpretation
K EINVAL inconsistent actor's capability
K EUNKNOWN unknown actor
K EFAULT incorrect address
K ENOMEM system out of resources
K EPRIV non authorized thread for creating a system actor
The following application can be used to create new actors by calling the
actorCreate primitive. The command will be called with two parameters:
the rst parameter denes the privilege of the new actor to be created (u for
a user actor, t for a trusted user actor and s for a supervisor actor);
the second parameter denes the state of the newly created actor: a for active
and s for stopped.
Before terminating, the c actor which created the actor will display the capa-
bility of the newly created actor:
--> cat $CHORUS/sources/actors/actorCreate/actorCreate.c
#include "utilities.h"
int result;
KnActorPrivilege privilege;
KnActorStatus state;
KnCap capa;
main(int argc, char *argv[ ]) {
if (argc < 3) {
fprintf(stderr, "error ...\n");
exit(0);
}
switch (argv[1][0]) {
case 't' : privilege = K_SYSTEMACTOR; break;
case 's' : privilege = K_SUPACTOR; break;
default : privilege = K_USERACTOR;
}
128 Chapter 6. Actors
state = (argv[2][0] == 's') ? K_STOPPED : K_ACTIVE;
result = actorCreate(K_MYACTOR, &capa, privilege, state);
if(result == K_OK)
fprintCap(stdout, "New actor's capability", &capa);
else
fprintf(stderr, "Error on actorCreate: %s\n", strSysError(result));
}
-->
We can see from the results of the cs command that the actors exist and are
empty, they only own a port. The results of the aps command show that these
new actors are not known to the actor manager. They cannot be deleted by the
akill command:
--> rsh neon arun cs
started aid = 24
ChorusOS r4.0.0 Site 0 Time 23h 42m 44
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000001 869da80a 00000001 00000001 0001 SUP STARTED 003 kern
200000a7 869da80a 00000002 00000440 0002 SUP STARTED 000 <no-name>
[
........ other actors ] .......
200000a5 869da80a 00000016 00000000 0022 USER STOPPED 000 <no-name>
200000a2 869da80a 00000017 00000424 0023 USER STARTED 000 <no-name>
200000a8 869da80a 00000018 00000001 0024 SUP STARTED 001 cs
--> rsh neon arun cs -la 2
started aid = 24
ChorusOS r4.0.0 Site 0 Time 23h 45m 13
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000a7 869da80a 00000002 00000440 0002 SUP STARTED 000 <no-name>
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
200000a7 869da80a def no ea9e40 0 200000a7 869da80a
6.5. Deleting actors 129
--> rsh neon arun cs -la 22
started aid = 24
ChorusOS r4.0.0 Site 0 Time 23h 47m 16
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000a5 869da80a 00000016 00000000 0022 USER STOPPED 000 <no-name>
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
200000a5 869da80a def no fac1e8 0 200000a5 869da80a
--> rsh neon arun cs -la 23
started aid = 24
ChorusOS r4.0.0 Site 0 Time 23h 47m 44
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000a2 869da80a 00000017 00000424 0023 USER STARTED 000 <no-name>
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
200000a2 869da80a def no fac0ac 0 200000a2 869da80a
--> rsh neon aps
UID AID NAME DBG GROUP
0 21 C_INIT 0 N/A
0 20 ADMIN 0 N/A
--> rsh neon akill 2
C_INIT: akill: 2: cannot get actor capability
-->
We then create an empty user actor with the actorCreate application we wrote
in 6.4.5:
--> neon actorCreate_u u a
started aid = 24
New actor's capability: 200000b4 869da80a 17 488
-->
As already stated, this new actor is not recognized as a c actor and cannot be
killed with the standard akill command:
--> rsh neon aps
UID AID NAME DBG GROUP
0 21 C_INIT 0 N/A
0 20 ADMIN 0 N/A
--> rsh neon arun cs | grep "no-name"
started aid = 24
200000b4 869da80a 00000017 00000488 0023 USER STARTED 000 <no-name>
--> rsh neon akill 23
C_INIT: akill: 23: cannot get actor capability (= it is not a c actor
-->
However, it can be killed with the myAkill application where its capability is
passed as argument:
--> neon myAkill_u 200000b4 869da80a 17 488
started aid = 24
actor has been deleted
--> rsh neon arun cs | grep "no-name"
started aid = 24
-->
6.6. Synchronizing c actors' threads' executions 133
6.6 Synchronizing c actors' threads' executions
By a mechanism similar to the wait primitive of Posix systems allowing a pro-
cess to wait for termination of a child process, a thread of a c actor may be
blocked until a given c actor terminates or stops due to tracing requested by
another actor through the atrace primitive. This primitive allows a c actor
to control the execution of another one and is essentially used for implement-
ing breakpoint debugging. Nevertheless, unlike Posix systems where processes
may be zombie, no such state exits for actors. Once it is terminated, an actor
completely disappears: thus, it will be impossible for a c actor to retrieve infor-
mation about termination of an actor if it calls one of the primitives we present
here after the target actor has exited. The function call
#include <am/await.h>
int awaits(
KnCap *cactorCap,
int *statusp
);
blocks the calling thread until the c actor whose capability is pointed to by
cactorCap terminates or stops because of tracing. When returning, if statusp is
not NULL, *statusp indicates why the c actor stopped or terminated (if the actor
terminated, the returned status corresponds to its exit value). If successful, the
function returns the local identier of the c actor which terminated or stopped.
Otherwise -1 is returned and errno is set to one of the following values:
value of errno interpretation
EFAULT cactorCap or statusp point to an illegal address
EINVAL cactorCap corresponds to the calling actor
ESRCH cactorCap does not correspond to a valid c actor
EINTR the calling thread has been aborted
The await primitive only has a KnCap * parameter. Thus, its prototype is
#include <am/await.h>
int await(KnCap *cactorCap);
In the following example, we rst develop a small application which may ter-
minate for dierent reasons depending on the value of its parameter.
The actor which is created
- kills itself through akill if the value is 0;
- deletes itself through actorDelete if the value is 1;
- provokes an exception by writing to a wrong address if the value is 2;
- terminates by calling exit otherwise and the corresponding value will be
passed as an argument to the call.
--> cat $CHORUS/sources/actors/await/awaited/waitedActor.c
#include "utilities.h"
#include <am/await.h>
int ind, status, arg1;
int *p = (int *) 0x47477000; /* an illegal address */
KnCap actorCap;
In the rst execution, the new c actor terminates with akill (0 is passed as an
argument):
--> neon waitingActor_u 0
started aid = 24
********************************
new c_actor has been created .......
********************************
Capability: 200000bd 869da80a 17 0
local identifier: 23
********************************
actor with local identifier 23 is terminated
==> exception: actor killed
6.6. Synchronizing c actors' threads' executions 137
In the second execution, the new c actor terminates with actorDelete (1 is
passed as an argument):
--> neon waitingActor_u 1
started aid = 24
********************************
new c_actor has been created .......
********************************
Capability: 200000bf 869da80a 17 4e4
local identifier: 23
********************************
actor with local identifier 23 is terminated
==> exception: actor deleted
The next execution provokes a termination through exit with the value 100
which is passed as argument:
--> neon waitingActor_u 100
started aid = 24
********************************
new c_actor has been created .......
********************************
Capability: 200000c3 869da80a 17 517
local identifier: 23
********************************
actor with local identifier 23 is terminated
==> exit(100)
-->
A nal execution provokes a termination by exit with the value 1000: we can
observe that the waiting actor gets this value as 1000 % 256:
138 Chapter 6. Actors
--> neon waitingActor_u 1000
started aid = 24
********************************
new c_actor has been created .......
********************************
Capability: 200000c5 869da80a 17 530
local identifier: 23
********************************
actor with local identifier 23 is terminated
==> exit(232)
-->
Chapter 7
Basic memory management
7.1 General concepts
7.1.1 The memory models and the features
Three dierent memory management models may be selected:
the MEM FLAT model: this is the model implemented if no specic feature is
enabled. Within this model, one unique unprotected supervisor address space
is dened and shared by the kernel and all applications. Addresses directly
match the physical address space, the kernel simply provides services for allo-
cating memory. Applications may not allocate more memory than is physically
available;
the MEM PROTECTED model: this provides the benet of using the
ex-
ibility and protection supported by the hardware (MMU). In particular, the
address space is divided into a supervisor space that is shared by the kernel
and all the supervisor actors, and a user address space that is used as a pri-
vate address space by every user actor. Nevertheless, no swap, no demand
paging and no mapper interface are provided. Thus, no more than physi-
cally available memory may be allocated. This model is implemented when
the VIRTUAL ADDRESS SPACE feature is enabled;
the MEM VIRTUAL model: within this model corresponding to a system
where both VIRTUAL ADDRESS SPACE and ON DEMAND PAGING features are ac-
tive, a full service for virtual memory with swapping is provided. The following
services are provided:
- like the MEM PROTECTED model, multiple protected address spaces
for user actors;
- if secondary storage is available, the possibility for applications to allo-
cate more (virtual) memory than the available physical one. Pages are
automatically swapped in and out when it is appropriate;
139
140 Chapter 7. Basic memory management
- it exports two specic abstractions: segments which are external rep-
resentations of data objects (like les or swap areas) managed by spe-
cic external actors called mappers and local caches which are their
representation in the physical memory. A protocol is dened by the
ON DEMAND PAGING feature which allows communication between the ker-
nel and mappers: this protocol is based on the IPC feature and denes
the dierent kinds of messages that can be exchanged by the kernel and
the mappers.
The choice of a model has to be made depending on the hardware and the
software requirements (paging, protections between applications, . . . ) and in
this chapter we will focus on the MEM PROTECTED model and touch on
MEM FLAT. Chapter 15 is fully dedicated to the MEM VIRTUAL memory
model.
The memVersion application whose code is now given displays a message in-
dicating the type of memory management module which is being used by the
system:
--> cat $CHORUS/sources/memory/version/memVersion.c
#include <stdio.h>
#include <chorus.h>
#include <exec/chModules.h>
#include <mem/chMemConst.h>
int result, value;
main( ) {
result = sysGetConf(K_MODULE_MEM_NAME, K_GETCONF_VERSION, &value);
if (result != K_OK) {
fprintf(stderr, "error on sysGetConf: %s\n", strSysError(result));
exit(1);
}
switch(value) {
case K_MEM_VERSION_FLM:
printf("Flat memory\n");
break;
case K_MEM_VERSION_PRM:
printf("Protected memory\n");
break;
case K_MEM_VERSION_VM:
printf("Virtual memory\n");
}
}
-->
In the examples in this chapter, we will use two systems: carbon which uses
MEM FLAT and neon which uses MEM PROTECTED:
7.1. General concepts 141
--> carbon memVersion_u
started aid = 2
Flat memory
--> neon memVersion_u
started aid = 2
Protected memory
-->
When the VIRTUAL ADDRESS SPACE feature is used, the size of a virtual page is
the same as the size of a physical page as returned by the previous function.
142 Chapter 7. Basic memory management
7.1.2.3 Parameters for memory management
The KnVmPar structure describes the memory management parameters used on
the running kernel. It has the following elds:
usrStartAddr and usrEndAddr whose type is VmAddr: they dene the bounds
of the user address space;
svStartAddr and svEndAddr whose type is VmAddr: they dene the bounds
of the supervisor address space;
various other elds are meaningful within the MEM VIRTUAL model and
will be presented in chapter 15 along with the vmSetPar primitive which is used
to modify their values.
The KnVmStat structure is used to describe the state of the memory. It has the
following elds:
par whose type is KnVmPar: it denes the parameters of the memory manage-
ment as described above;
hostMem, freeMem and lockMem whose type is VmSize: they describe the size
of the site's physical memory, the amount of currently non-allocated physical
memory, and the amount of currently allocated and non-swappable physical
memory.
This information may be retrieved by calling
#include <mem/chMem.h>
int vmStat(KnVmStat *stat);
The memoryStat application whose code is given below gets and displays the
parameters managed by the memory management module.
--> cat $CHORUS/sources/memory/memoryStat/memoryStat.c
#include <chorus.h>
KnVmStat stat;
main( ) {
int result;
printf("Page size: 0x%x [%d]\n", vmPageSize( ), vmPageSize( ));
result = vmStat(&stat);
printf("Total memory size: 0x%x [%d]\n", stat.hostMem, stat.hostMem);
printf("Amount of free memory: 0x%x [%d]\n", stat.freeMem, stat.freeMem);
printf("Amount of locked memory: 0x%x [%d]\n", stat.lockMem, stat.lockMem);
printf("Beginning of user address space: %p\n", stat.par.usrStartAddr);
printf("End of user address space: %p\n", stat.par.usrEndAddr);
printf("Beginning of superv. address space: %p\n", stat.par.svStartAddr);
printf("End of supervisor address space: %p\n", stat.par.svEndAddr);
}
-->
7.1. General concepts 143
The application is rst executed on the carbon ix86 based machine where the
MEM FLAT memory management model is used:
--> carbon memoryStat_u
started aid = 2
Page size: 0x1000 [4096]
Total memory size: 0xfa0000 [16384000]
Amount of free memory: 0xd0f000 [13692928]
Amount of locked memory: 0x291000 [2691072]
Beginning of user address space: 0x0
End of user address space: 0xffffefff
Beginning of superv. address space: 0x0
End of supervisor address space: 0xffffefff
We can observe that there is a single address space: user address space and
supervisor address space are identical.
We now execute the same application on the neon ix86 based machine within
the MEM PROTECTED memory management model:
--> neon memoryStat_u
started aid = 24
Page size: 0x1000 [4096]
Total memory size: 0xfa0000 [16384000]
Amount of free memory: 0xcae000 [13295616]
Amount of locked memory: 0x2f2000 [3088384]
Beginning of user address space: 0x8000000
End of user address space: 0xffffefff
Beginning of superv. address space: 0x0
End of supervisor address space: 0x7fffffff
The supervisor address space and the user address space correspond to dierent
segments of addresses. The division of the global address space between these
two address spaces is illustrated in the following diagram:
0xfffff000 one page out of address space
0xffffefff
user address space:
- each actor has its own space
- addresses are interpreted in
the context of the actor
0x80000000
0x7fffffff
supervisor address space:
it is shared by all
supervisor actors
and the system actors
0x0
144 Chapter 7. Basic memory management
7.2 Organization of an actor's address space
7.2.1 The concept of region
As we have seen when we displayed characteristics of actors with the cs com-
mand, the address space of an actor is divided in non-overlapping regions.
A region consists of a contiguous range of logical addresses having common at-
tributes. The number and type of these attributes depend on the memory man-
agement model being used, but they may be related to protection, inheritance
and paging. Due to the organization by pages a region consists of contiguous
logical pages. The starting address of a region is page-aligned and its size is
always a multiple of the page size.
As we will see in later sections, regions may be created and freed dynamically
by threads and may be shared by dierent actors.
We will use the following variant of the noDelay application in various examples
in this chapter (the denition of a static array has just been added):
--> cat $CHORUS/sources/utilities/noDelay3/noDelay3.c
#include <chorus.h>
char t[0x8000]; /* an array of 8 pages in static data region */
main( ) {
threadDelay(K_NOTIMEOUT);
}
We can observe that the user c actors executing the same application have the
same number of regions, and corresponding regions in both actors have the
same attributes (size and options), but that the starting addresses are dierent
as addresses are physical addresses.
Each c actor has four regions when created:
- the rst region (3 pages) contains the program;
- the second region (9 pages) is the static data region;
- the third region (4 pages) is the stack;
- the fourth region (2 pages) contains the arguments of the main thread.
Each region has attributes: it may be writable (WR), executable (EX), initialized
to zero (FZ) or implemented anywhere (AW)). These dierent attributes will be
presented in 7.2.5.
146 Chapter 7. Basic memory management
7.2.2.2 The MEM PROTECTED model
We now execute two samples of the user version of the noDelay3 application on
the neon system using the MEM PROTECTED memory management model.
We then get the memory organization of the corresponding c actors by calling
the cs utility:
--> neon-n noDelay3_u & (= create a rst user actor
[1] 8879
--> started aid = 2
--> rsh neon arun cs -la 2 (= get its characteristics
started aid = 22
ChorusOS r4.0.0 Site 0 Time 1h 17m 33
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000022 869da80a 00000002 0000007b 0002 USER STARTED 001 noDelay3_u
............................
START SIZE OFFSET ALLOC OPTIONS
fffec000 00006000 00000000 00006000 WR
ffff3000 00009000 00000000 00009000 WR
ffffc000 00003000 00000000 00003000 EX
--> neon-n noDelay3_u & (= create a second user actor
[2] 8883
--> started aid = 22
--> rsh neon arun cs -la 22 (= get its characteristics
started aid = 23
ChorusOS r4.0.0 Site 0 Time 1h 20m 48
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000024 869da80a 00000016 00000000 0022 USER STARTED 001 noDelay3_u
............................
START SIZE OFFSET ALLOC OPTIONS
fffec000 00006000 00000000 00006000 WR
ffff3000 00009000 00000000 00009000 WR
ffffc000 00003000 00000000 00003000 EX
-->
We can observe from these results that the address space of user actors which
execute the same command contains exactly the same logical regions: same
starting address (the memory management unit will translate these logical ad-
dresses to the corresponding physical addresses), same size and same attributes.
When it is created by the actor manager, a user c actor has three regions: their
characteristics (start address, size and attributes) appear in the results. In
contrast to the results within the MEM FLAT model, two regions have been
merged into a single one. In both actors:
- the region starting at 0xffffc000 is an executable region of 3 pages which
contains the program;
7.2. Organization of an actor's address space 147
- the region starting at 0xfffec000 is a 6 page writable region. It rst con-
tains the stack of the main thread of the actor: its size is the value of the
kern.exec.dflUsrStackSize tunable parameter whose default value is 0x4000
(4 pages). The two other pages of this region contain a copy of the parameters
of the main function;
- the region starting at 0xffff3000 is a 9 page writable region which corre-
sponds to the static data region.
A system stack is also automatically allocated in the supervisor space (and thus,
does not appear in the address space of the user actor) when the main thread
is created. The size of this stack is dened by the value of the conguration's
parameter kern.exec.dflSysStackSize whose default value is 0x3000.
The eects of such a call apply to the entire range of addresses specied by the
startAddr and size elds of the object pointed to by rgnDesc within the actor
named by targetActorCap. The starting address startAddr must be page-aligned
and if the ending address (startAddr + size - 1) is within a page it is rounded
to the end of that page.
The eects of the call are the following:
the K READABLE, K EXECUTABLE, K WRITABLE or K SUPERVISOR attributes in
the kernel's descriptor are replaced by their correspondent in the options eld
of the region's descriptor pointed to by rgnDesc;
protections are modied at the low level (page table entries).
The function returns K OK upon success and a negative error code otherwise.
return value error type
K EINVAL inconsistent actor capability
K EUNKNOWN unknown actor
K EFAULT address out of address space
K EROUND starting address not page-aligned
The modAttr application whose code is given below can be used to modify the
protection attributes of the regions of an actor in a given range of addresses by
calling rgnSetProtect. The arguments of the command are the capability of
the target actor, the starting address, the size of the range of addresses to be
considered, and the new value of the protection attribute (w, x or r):
7.2. Organization of an actor's address space 151
--> cat $CHORUS/sources/memory/modAttr/modAttr.c
#include "utilities.h"
main(int argc, char*argv[ ]) {
KnRgnDesc rgnDesc;
int result;
KnCap targetActorCap;
readCap(argv + 1, &targetActorCap);
sscanf(argv[5], "%x", &rgnDesc.startAddr);
sscanf(argv[6], "%x", &rgnDesc.size);
switch (argv[7][0]) {
case 'w' : rgnDesc.options = K_WRITABLE; break;
case 'x' : rgnDesc.options = K_EXECUTABLE; break;
default : rgnDesc.options = K_READABLE;
}
result = rgnSetProtect(&targetActorCap, &rgnDesc);
if (result != K_OK)
fprintf(stderr, "error on rgnSetProtect: %s\n", strSysError(result));
}
-->
user ffffc000
actor e9d000
22 ffff3000
e82000
400e5000
246555
physical
physical memory addresses
supervisor address
space
The last four elds are only meaningful within the MEM VIRTUAL memory
model.
7.2.7.2 Examples
The doRegionStat function (whose code is given below) loads within the statAr-
ray buer the characteristics of the rst numEntries regions of the complete
address space of an actor whose capability is pointed to by actorCap. Address
space is scanned between 0 and the K MAXVMSIZE symbolic value which denes
the greatest legal virtual address (in our examples it is 0xffffefff).
--> cat $CHORUS/sources/memory/regions/regions.c
#include <stdio.h>
#include <chorus.h>
The printRegion function displays the elds of a KnRgnStat entry: it also takes
into account the dierent attributes specic to the MEM VIRTUAL module
and prints the values of the opaque elds which are meaningful only with that
model:
#include <stdio.h>
#include <chorus.h>
#include <exec/chModules.h>
#include <mem/chMemConst.h>
int doRegionStat(KnCap *actorCap, KnRgnStat *statArray, int numEntries);
156 Chapter 7. Basic memory management
void printRegion(KnRgnStat *rgnStat) {
int result, value;
result = sysGetConf(K_MODULE_MEM_NAME, K_GETCONF_VERSION, &value);
printf("start = %-10p size = 0x%-6x [%p]",
rgnStat -> startAddr, rgnStat -> size, rgnStat -> options);
if(rgnStat -> options & K_ANYWHERE) printf("AW ");
if(rgnStat -> options & K_WRITABLE) printf("WR ");
if(rgnStat -> options & K_EXECUTABLE) printf("EX ");
if(rgnStat -> options & K_SUPERVISOR) printf("SU ");
if(rgnStat -> options & K_INHERITSHARE) printf("IS ");
if(rgnStat -> options & K_INHERITCOPY) printf("IC ");
if(rgnStat -> options & K_NODEMAND) printf("ND ");
if(rgnStat -> options & K_NOWAITFORMEMORY) printf("NW ");
if(rgnStat -> options & K_FILLZERO) printf("FZ ");
if(rgnStat -> options & K_NOSWAPOUT) printf("NS ");
if (value == K_MEM_VERSION_VM)
printf(" [%x] [%x]\n", rgnStat -> opaque1, rgnStat -> opaque2);
else putchar('\n');
}
Finally, the listRegions application uses the previous functions to display the
characteristics of the regions of an actor whose capability is transmitted as
argument to the command:
--> cat $CHORUS/sources/memory/regions/listRegions.c
#include "utilities.h"
#define REGIONSMAXNUMBER 30
KnRgnStat statArray[REGIONSMAXNUMBER];
main(int argc, char *argv[ ]) {
int ind, result;
KnCap actorCap;
readCap(argv + 1, &actorCap);
result = doRegionStat(&actorCap, statArray, REGIONSMAXNUMBER);
if(result >= 0) {
printf("The actor has %d regions\n", result);
if (result > REGIONSMAXNUMBER) result = REGIONSMAXNUMBER;
for(ind = 0; ind < result; ind ++) printRegion(statArray + ind);
}
}
We use this application to display the dierent regions of two c actors previously
created and loaded on the neon system (user actor 2 and supervisor actor 24):
--> neon listRegions_u 20000022 869da80a 2 7b (= user actor 2
started aid = 25
The actor has 3 regions
start = 0xfffec000 size = 0x6000 [0x2]WR
start = 0xffff3000 size = 0x9000 [0x2]WR
start = 0xffffc000 size = 0x3000 [0x1]EX
7.3. The Posix interface 157
--> neon listRegions_u 20000029 869da80a 18 0 (= supervisor actor 24
started aid = 25
The actor has 3 regions
start = 0x7bbd3000 size = 0x2000 [0x6]WR SU
start = 0x7bbda000 size = 0x9000 [0x6]WR SU
start = 0x7bbe3000 size = 0x3000 [0x5]EX SU
-->
main( ) {
int stackIntVar,
result,
ind;
printf("[program] address of stackIntVar: %p\n", &stackIntVar);
printf("[program] address of externIntVar: %p\n", &externIntVar);
result = doRegionStat(K_MYACTOR, statArray, 10);
printf("Actor has %d regions\n", result);
for(ind = 0; ind < result; ind ++) {
printf("[%d] ==> ", ind);
printRegion(statArray + ind);
}
p_malloc = (char *) malloc(0x9000);
printf("\nAfter malloc .....\n");
printf("[program] malloc returns %p\n", p_malloc);
result = doRegionStat(K_MYACTOR, statArray, 10);
printf("Actor has now %d regions\n", result);
for(ind = 0; ind < result; ind ++) {
printf("[%d] ==> ", ind);
printRegion(statArray + ind);
}
}
158 Chapter 7. Basic memory management
--> neon regionsMalloc_u
started aid = 25
[program] address of stackIntVar: 0xffff3fcc
[program] address of externIntVar: 0xffff58a0
Actor has 3 regions
[0] ==> start = 0xfffee000 size = 0x6000 [0x2]WR
[1] ==> start = 0xffff5000 size = 0x1000 [0x2]WR
[2] ==> start = 0xffff6000 size = 0x9000 [0x1]EX
After malloc .....
[program] malloc returns 0xfffe4008
Actor has now 3 regions
[0] ==> start = 0xfffe4000 size = 0x10000 [0x2]WR
[1] ==> start = 0xffff5000 size = 0x1000 [0x2]WR
[2] ==> start = 0xffff6000 size = 0x9000 [0x1]EX
-->
In this type of a call, actorCap points to the capability of the target actor (the
K MYACTOR value corresponds to the current actor). Unless the region is later
7.4. Creating a new region 159
shared with other actors or explicitly freed, it will be destroyed when the actor
is deleted.
The K SVACTOR value may be used: in that case the region will be allocated in
the supervisor address space and will not be attached to any actor. It will have
to be explicitly deallocated.
The rgnDesc parameter points to a descriptor containing the characteristics of
the new region:
unless K ANYWHERE or K RESTRICTIVE is set in the options eld of the object
pointed to by rgnDesc, the startAddr eld of this object is the starting address
of the new region and which must be page-aligned and whose size eld denes
the size of the new region;
if K ANYWHERE is set, the kernel selects the starting address and, upon return
from the function, the value of the startAddr eld of the object pointed to by
rgnDesc is the new region's starting address;
if K RESTRICTIVE is set, the region is allocated anywhere in the range dened
by the startAddr and endAddr elds of the object pointed to by rgnDesc;
other
ags may be used for selecting specic attributes of the new region
(K FILLZERO, K NODEMAND, . . . ).
Certain particular properties should be noted:
the new region may not overlap with an existing one;
if the K SUPERVISOR
ag is set, only supervisor threads will be allowed to
access the new region.
A successful call returns K OK, otherwise a negative value to indicate that an
error occurred:
return value error type
K EUNKNOWN unknown actor
K EINVAL inconsistent actor capability or
ags
K EFAULT address out of address space
K EROUND starting address not page-aligned
K ESPACE rgnDesc.size is 0 or invalid value of rgnDesc.startAddr
K EOVERLAP allocation of the region would lead to overlapping
K ENOMEM system out of resources
7.4.2 Examples
7.4.2.1 Internal allocation
We rst examine dierent situations when trying to allocate regions inside the
calling actor through the internalAlloc u and internalAlloc s.r binaries
corresponding to the following application:
160 Chapter 7. Basic memory management
--> cat $CHORUS/sources/memory/internalAlloc/internalAlloc.c
#include <stdio.h>
#include <chorus.h>
int *addr, result;
KnRgnDesc rgnDesc;
main( ) {
KnCap actorCap;
/* trying to allocate a region in the supervisor address space */
rgnDesc.startAddr = 0x66666300;
rgnDesc.size = 0x1000;
rgnDesc.options = K_WRITABLE;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result < 0)
fprintf(stderr, "error on first rgnAllocate: %s\n\n",
strSysError(result));
else
fprintf(stderr, "first rgnAllocate succeeds\n\n");
/* trying to allocate a region in the supervisor address space */
/* with K_SUPERVISOR attribute */
rgnDesc.startAddr = 0x66666000;
rgnDesc.options |= K_SUPERVISOR;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result < 0)
fprintf(stderr, "error on second rgnAllocate: %s\n\n",
strSysError(result));
else
fprintf(stderr, "second rgnAllocate succeeds\n\n");
/* allocating a region anywhere in the user address space */
/* the address specified in startAddr is not used */
rgnDesc.options = K_WRITABLE | K_ANYWHERE;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result < 0)
fprintf(stderr, "error on third rgnAllocate: %s\n",
strSysError(result));
else {
fprintf(stderr, "address of new region: %p\n", rgnDesc.startAddr);
addr = (int *) rgnDesc.startAddr;
*addr = 3333;
fprintf(stderr, "at address %p: %d\n\n", addr, *addr);
}
/* allocating a region with K_SUPERVISOR at address */
/* 0x66666000 in the supervisor address space */
rgnDesc.startAddr = 0x66666000;
rgnDesc.options = K_WRITABLE | K_SUPERVISOR;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
7.4. Creating a new region 161
if (result < 0)
fprintf(stderr, "error on fourth rgnAllocate: %s\n\n",
strSysError(result));
else {
addr = (int *) 0x66666000;
*addr = 1200;
fprintf(stderr, "in 0x66666000: %d\n", *addr); }
/* allocating a region in the user address space */
rgnDesc.startAddr = 0x90000000;
rgnDesc.options = K_WRITABLE;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result < 0)
fprintf(stderr, "error on fifth rgnAllocate: %s\n\n",
strSysError(result));
else {
addr = (int *) 0x90000000;
*addr = 1200;
fprintf(stderr, "in 0x90000000: %d\n\n", *addr); }
/* allocating a region with K_SUPERVISOR at address */
/* 0x90004000 in the user address space */
rgnDesc.startAddr = 0x90004000;
rgnDesc.options = K_WRITABLE | K_SUPERVISOR;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result < 0)
fprintf(stderr, "error on sixth rgnAllocate: %s\n\n",
strSysError(result));
else {
addr = (int *) 0x90004000;
*addr = 1200;
fprintf(stderr, "in 0x90004000: %d\n", *addr); }
}
We rst execute the user version:
--> neon internalAlloc_u
started aid = 25
error on first rgnAllocate: Alignment error
in 0x90000000: 1200
The attributes of the new region are incorrect (the FZ
ag does not appear). We
encountered various other errors concerning the management of the attributes
during our tests, as we will demonstrate later.
We now request the creation of a region to which access is reserved to threads
executing within the supervisor mode by using the K SUPERVISOR attribute (we
also use the K ANYWHERE
ag):
--> neon allocRgn_u 20000022 869da80a 2 7b 0 0x3000 0 3
started aid = 25
--> neon listRegions_u 20000022 869da80a 2 7b
started aid = 25
The actor has 6 regions
start = 0xd0000000 size = 0x5000 [0x2]WR
start = 0xfffe3000 size = 0x3000 [0x4]SU (=the new region
start = 0xfffe6000 size = 0x6000 [0x0]
start = 0xfffec000 size = 0x6000 [0x2]WR
start = 0xffff3000 size = 0x9000 [0x2]WR
start = 0xffffc000 size = 0x3000 [0x1]EX
7.4. Creating a new region 165
Finally, we request the creation of a region overlapping an existing one without
using the K ANYWHERE
ag. The call fails:
--> neon allocRgn_u 20000022 869da80a 2 7b 0 0xd0002000 0x1000
started aid = 25
error on rgnAllocate: Region overlapping
-->
We now use the modAttr application (see 7.2.5.2) to change protection attributes
of regions. We will note erroneous results provided by the rgnStat primitive.
We turn o the writable attribute of one page in the ve page region starting
at address 0xd0000000:
--> neon modAttr_u 20000022 869da80a 2 7b 0xd0002000 0x1000 r
started aid = 25
-->
In the next call, performed using the K ANYWHERE
ag and the same wrong
address as in the previous one, the kernel selects an address in the supervisor
space:
--> neon allocRgn_u 20000027 869da80a 17 0 0xd0003000 0x5000 0 1
started aid = 25
--> neon listRegions_u 20000027 869da80a 17 0
started aid = 25
The actor has 4 regions
start = 0x7bbce000 size = 0x5000 [0x2]WR (=
the new region
start = 0x7bbe6000 size = 0x2000 [0x6]WR SU
start = 0x7bbe9000 size = 0x9000 [0x6]WR SU
start = 0x7bbf2000 size = 0x3000 [0x5]EX SU
-->
This call requests freeing the address space within the range of addresses start-
ing at rgnDesc->startAddr (which must be page-aligned) and whose size is
rgnDesc->size in the actor whose capability is pointed to by actorCap. The
value of size may be rounded up in order to be page-aligned. Contrary to
the memory deallocation by free where the deallocated area must t an area
allocated by a call to malloc exactly, the rgnFree primitive may be used to
deallocate any set of contiguous addresses independently from the way they
were allocated. Furthermore, the corresponding range of addresses may contain
invalid addresses.
As stated earlier, deallocating memory in an actor does not necessarily imply
that the corresponding physical memory is deallocated (that will only be the
case when all the corresponding logical addresses have been deallocated).
The only values of the options eld which are recognized are 0 and the special
K FREEALL value which is a request for deallocating all the regions of the actor.
The actorCap parameter may have the special K SVACTOR value: this value al-
lows regions in the supervisor address space to be deallocated without specifying
a specic actor capability. This value should only be used for deallocating re-
gions which are not attached to a specic actor. When actorCap is K SVACTOR,
the K FREEALL value of the options eld cannot be used.
It is important to realize that this type of operation could lead:
either to the deletion of complete regions and shrinkage of regions as the
following diagram illustrates:
168 Chapter 7. Basic memory management
region 1 startAddr
eect of rgnFree:
region 2 - region 2 is deallocated
- regions 1 and 3 shrink
startAddr + size
region 3
startAddr
deallocated address space
startAddr + size inside a region
7.5.3 Example
We give below the source of a command to externally deallocate regions in a
given actor:
--> cat $CHORUS/sources/memory/rgnFree/rgnFree.c
#include "utilities.h"
KnRgnDesc rgnDesc;
KnCap targetActorCap;
int result;
main(int argc, char *argv[ ]) {
readCap(argv + 1, &targetActorCap);
sscanf(argv[5], "%x", &rgnDesc.startAddr);
sscanf(argv[6], "%x", &rgnDesc.size);
7.5. Freeing regions in an actor 169
result = rgnFree(&targetActorCap, &rgnDesc)
if(result != K_OK)
fprintf(stderr, "error on rgnFree: %s\n", strSysError(result));
else
fprintf(stderr, "rgnFree succeeded\n");
}
-->
We killed the existing actors and restarted a new user c actor for the noDelay3
application and allocated a new region of 6 pages at the address 0xbbbb0000 in
its address space:
--> neon-n noDelay3_u&
[1] 2708
--> started aid = 24
These results show that the region starting at address 0xbbbb0000 has shrunk
to a rst region of 2 pages, a hole of 2 pages and another region of 2 pages.
170 Chapter 7. Basic memory management
7.6 Advanced operations on regions
7.6.1 Introduction
The service provided by rgnAllocate allocates memory in the address space
of an actor, or possibly in the supervisor address space. At the low level, in
the regions kernel list, this operation may correspond either to the creation of
a new region, to the extension of an existing region or even to the merge of
existing regions into a larger single one.
Another type of service allows the creation of a region in an actor which will be
related to an existing region of another actor. This relation may be:
strong: a new region is created which will correspond to the same physical
data as an existing one. The two regions allow the two actors to share data. The
two regions may or not correspond to the same virtual address in the address
space of the actors. This is quite similar to the following mechanisms of the
Unix system:
- the sharing of the code segment by two Unix processes after a fork op-
eration. The new process executes the same program, the code region
corresponds to the same virtual address in the new process and is associ-
ated to the same physical area;
- the ability to share data through shared memory segments. In this case,
the two regions will not necessarily correspond to the same virtual address
in the address space of the actors;
weak: a new region is created which will correspond to a dierent physical
area but will be initialized, when created, with the contents of the existing re-
gion. This mechanism is similar to the duplication of the data region of a Unix
process when it forks.
Depending on the active memory management model, the following services are
provided:
within the three memory management models, sharing memory: the corre-
sponding service is accessed through the rgnMapFromActor primitive;
within the MEM PROTECTED and MEM VIRTUAL memory models, allo-
cation of memory with initialization of the allocated space from existing regions
through the rgnInitFromActor primitive;
with the MEM VIRTUAL memory management model, duplicating a com-
plete address space without a per-region option for copying or sharing through
the rgnDup primitive.
These dierent services are presented in chapter 15.
7.6. Advanced operations on regions 171
7.6.2 Sharing memory
7.6.2.1 The rgnMapFromActor primitive
The basic service allowing actors to share memory through regions of their
address space is accessed by calling the primitive
#include <mem/chMem.h>
int rgnMapFromActor(
KnCap *targetActorCap,
KnRgnDesc *targetRgnDesc,
KnCap *sourceActorCap,
KnRgnDesc *sourceRgnDesc
);
READABLE REGION
While the main thread of the actor executing the sourceMap application is
sleeping, we create a new actor whose main thread executes the targetMap
application. The capability of the source actor and the 0xb0000000 address
corresponding to the page to be mapped in the new actor are passed as argu-
ments.
176 Chapter 7. Basic memory management
-2-> neon targetMap_u 20000042 869da80a 17 1df 0xb0000000
started aid = 22
the region has been mapped in the actor's address space
logical address: 0xc0000000
physical address: 0xebd000
contents of mapped region: XXXXXXXXXXXXXXXX
The actor has 4 regions
start = 0xc0000000 size = 0x2000 [0x2]WR
start = 0xfffea000 size = 0x6000 [0x2]WR
start = 0xffff1000 size = 0x2000 [0x2]WR
start = 0xffff3000 size = 0xc000 [0x1]EX
-2->
When the main thread of the actor corresponding to the sourceMap application
is awoken, it displays the contents of the two pages of the region which has been
partially mapped in the target actor and terminates:
First page: YYYYYYYYYYYYYYYY (= page has been modied
Second page: XXXXXXXXXXXXXXXX
-1->
These results show that the rst page of the region of the rst actor has been
mapped to the second one: the corresponding physical addresses 1 and 2
are equal (0xebd000). Therefore, when the main thread of the source actor is
awoken, it can see the modication to the contents of this page. On the other
hand, it does not see the modications to the second page: this page has not
been mapped in the target actor. We can also note that the two contiguous
regions which were created in the target actor's address space have been merged
though they do not have the same attributes, this is due to the fact that one is
mapped and the other one is not. The following diagram illustrates this memory
sharing through the regions in the actors:
physical address: 0xebd000
0xb0000000
0xb0001000
0xb0002000
source actor
0xc0000000
0xc0001000
0xc0002000
These results show that the same region may be simultaneously associated to
regions in the supervisor address space and in the virtual address space of
user actors (that is in the user address space). Finally, we observe failures of
rgnAllocate calls due K EOVERLAP errors.
into its equivalent that calls rgnInitFromActor and we change the message
displayed when the new region has been created. Using the corresponding
targetInit u command:
/* Request for initializing a new region */
result = rgnInitFromActor(K_MYACTOR, &targetRgnDesc,
&sourceActorCap, &sourceRgnDesc);
if(result < 0) {
fprintf(stderr, "error on rgnInitFromActor: %s\n", strSysError(result));
exit(1);
}
printf("initialized region created in the actor's address space\n");
We get a K ESIZE error on the rgnInitFromActor call because the size eld
of the source region's descriptor is zero. After having initialized its value to
0x1000 (which corresponds to the page size) before calling rgnInitFromActor
sourceRgnDesc.size = 0x1000;
0xb0000000
0xb0001000
0xb0002000
source actor
0xc0000000
0xc0001000
0xc0002000
These results show that the address associated to the four allocated pages in
the supervisor address space does not belong to the address range of any re-
gion of the actor. Corresponding addresses are part of the supervisor address
space and will not be reallocated until they have been explicitly freed by calling
svPagesFree.
The next application displays the contents of the pages previously allocated in
the supervisor address space (an address is passed as fth argument) and frees
the allocated pages (the capability of the creating actor is passed as four rst
arguments):
186 Chapter 7. Basic memory management
--> cat $CHORUS/sources/memory/superFree/superFree.c
#include "utilities.h"
char *addr; int result, ind;
KnCap actorCap;
main(int argc, char *argv[ ]) {
readCap(argv + 1, &actorCap);
sscanf(argv[5], "%p", &addr);
for(ind = 0; ind < 4; ind ++)
printf("%p ==> %c\n", addr + ind * 0x1000, *(addr +ind * 0x1000));
result = svPagesFree((VmAddr)addr, 0x4000, &actorCap);
if(result < 0)
fprintf(stderr, "error on svPagesFree: %s\n", strSysError(result));
}
-->
We rst use the command for the rst actor we executed, we can check that the
space is still allocated although the actor is terminated:
--> neon superFree_s.r 20000056 869da80a 17 0 0x7fdd6000
started aid = 23
0x7fdd6000 ==> a
0x7fdd7000 ==> b
0x7fdd8000 ==> c
0x7fdd9000 ==> d
-->
A second call to free the same space in the supervisor address space provokes
an exception:
--> neon superFree_s.r 20000056 869da80a 17 0 0x7fdd6000
started aid = 23
Segmentation fault thread 7 PC 7bbe8067 faultAddr 7fdd6000
We now free the space allocated by the second actor, but using the capability
of the rst one: the pages are freed (the capability is not needed to free the
space):
--> neon superFree_s.r 20000056 869da80a 17 0 0x7fdd2000
started aid = 23
0x7fdd2000 ==> a
0x7fdd3000 ==> b
0x7fdd4000 ==> c
0x7fdd5000 ==> d
-->
copies count bytes from the address srcAddr in the supervisor address space to
the address dstAddr in the supervisor address space. The function returns K OK
if the call succeeds.
It returns the K EFAULT error value if a memory fault occurs during access to
one of the two areas which have srcAddr and dstAddr as the rst address and
count as size.
7.7.4.2 The svMemWrite primitive
A call to the function
188 Chapter 7. Basic memory management
#include <mem/chMem.h>
int svMemWrite(
void *srcAddr,
void *dstAddr,
int count
);
copies count bytes from the address srcAddr in the supervisor address space to
the address dstAddr in the supervisor address space. The function returns K OK
if the call succeeds.
If a memory fault occurs during the operation, the function returns:
K EFAULT if a memory fault occurred when accessing the destination memory
area;
K EFAIL if a memory fault occurred when accessing the source memory area.
7.7.4.3 Example
In the next example, which will be executed by supervisor c actors:
we rst create, two "one page" regions in the supervisor address space. These
are followed in that address space by a non-allocated page: this is ensured by
rst allocating of a 4 page region using a rgnAllocate call and the creation of
holes by two successive calls to rgnFree;
the source memory area is located at the end of the rst allocated page and
contains sourceLength bytes, this value is retrieved using the atoi(argv[1])
call;
the destination area is located at the end of the second allocated page and
contains 16 bytes;
the second argument of the command denes the count number of bytes that
will be copied from the source area into the target area.
This is illustrated in the following gure:
count
startAddr sourceAddr (argv[2])
sourceLength 16
(argv[1])
startAddr+0x2000 targetAddr
sourceLength = atoi(argv[1]);
count = atoi(argv[2]);
rgnDesc.options = K_ANYWHERE | K_WRITABLE | K_FILLZERO;
rgnDesc.startAddr = 0x0;
rgnDesc.size = 0x4000;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
fprintf(stderr, "startAddr: %p\n", rgnDesc.startAddr);
sourceAddr = (char *) rgnDesc.startAddr + 0x1000 - sourceLength;
targetAddr = (char *) rgnDesc.startAddr + 0x3000 - 16;
fprintf(stderr, "sourceAddr: %p\n", sourceAddr);
fprintf(stderr, "targetAddr: %p\n", targetAddr);
/* creating two holes after the pages */
rgnDesc.startAddr += 0x1000;
rgnDesc.size = 0x1000;
rgnDesc.options = 0;
result = rgnFree(K_MYACTOR, &rgnDesc);
rgnDesc.startAddr += 0x2000;
result = rgnFree(K_MYACTOR, &rgnDesc);
/* initializing source area */
for(ind = 0; ind < sourceLength; ind ++)
sourceAddr[ind] = 'a' + ind;
/* copying */
result = svMemRead(sourceAddr, targetAddr, count);
/* result = svMemWrite(sourceAddr, targetAddr, count); */
/* bcopy(sourceAddr, targetAddr, count); */
if(result < 0)
fprintf(stderr, "error on svMem: %s\n", strSysError(result));
fprintf(stderr, "after svMemRead addr's contents: %s\n",
targetAddr);
}
the second one (supSpaceCopy2 s.r) uses svMemWrite, and we give the results
of two erroneous executions:
--> neon supSpaceCopy2_s.r 12 13
started aid = 23
startAddr: 0x7bbe0000
sourceAddr: 0x7bbe0ff4
targetAddr: 0x7bbe2ff0
error on svMem: Bad address
after svMemRead addr's contents: abcdefghijkl
--> neon supSpaceCopy2_s.r 18 17
started aid = 14
startAddr: 0xfdcd3000
sourceAddr: 0xfdcd3fee
targetAddr: 0xfdcd5ff0
error on svMem: Transaction failed
Segmentation fault thread 20 PC fdcddd61 faultAddr fdcd6000
nally, the third one (supSpaceCopy3 s.r) uses bcopy and we give the results
of two erroneous executions:
--> neon supSpaceCopy3_s.r 12 13
started aid = 23
startAddr: 0x7bbe0000
sourceAddr: 0x7bbe0ff4
targetAddr: 0x7bbe2ff0
Segmentation fault thread 7 PC 7bbf19a3 faultAddr 7bbe1000
7.8. The POSIX shared memory 191
--> neon supSpaceCopy3_s.r 18 17
started aid = 23
startAddr: 0x7bbe0000
sourceAddr: 0x7bbe0fee
targetAddr: 0x7bbe2ff0
Segmentation fault thread 7 PC 7bbf19a3 faultAddr 7bbe3000
-->
R2 US1
US2 R6
R3
R4 PC1 R7 PC3
threadStop STOPPED
ACTIVE threadStart
FIFO RR
0
RT
95
155
195
255
default scheduler
with ROUND ROBIN feature
We now do the same using the round robin scheduling; the three actors are
created and we can observe that an interleaving of the messages from the three
threads which re
ects the round robin policy
--> neon-n fifo_rr_u 1 & neon-n fifo_rr_u 1& neon-n fifo_rr_u 1&
[1] 13415
[2] 13416
[3] 13418
--> started aid = 23
started aid = 2
started aid = 22
actor 23 : 0
actor 2 : 0
actor 23 : 1
actor 2 : 1
actor 22 : 0
actor 22 : 1
actor 2 : 2
actor 23 : 2
actor 2 : 2
actor 22 : 2
8.4.2 Example
This example illustrates the use of the K NOBLOCK argument in a threadDelay
call to yield the processor to other threads with the same priority. The main
thread executes two successive loops; the number of steps of these loops are
dened by the value of the rst parameter of the command. Furthermore, if the
command is executed with a second argument, it calls threadDelay(K NOBLOCK)
before entering the second loop:
--> cat $CHORUS/sources/threads/procYield/procYield.c
#include <stdio.h>
#include <chorus.h>
main(int argc, char *argv[ ]) {
int ind, loop;
loop = atoi(argv[1]); /* decode argv[1] as an integer */
fprintf(stderr, "c_actor %d before the first loop\n", agetId( ));
for(ind = 0; ind < loop; ind ++)
; /* first empty loop */
fprintf(stderr, "c_actor %d after the first loop\n", agetId( ));
if (argc > 2)
threadDelay(K_NOBLOCK)
for(ind = 0; ind < loop; ind ++);
; /* second empty loop */
fprintf(stderr, "c_actor %d after the second loop\n", agetId( ));
}
We rst execute three samples of the command for a small value of loop without
calling threadDelay between the loops. We observe that once an actor has been
210 Chapter 8. Threads
created, its main thread may execute completely before C INIT receives the
request to create a new actor. The three actors have the same local identier:
--> neon-n procYield_u 100000 & neon-n procYield_u 100000 &
neon-n procYield_u 100000 &
[1] 13564
[2] 13565
[3] 13567
--> started aid = 22
c_actor 22 before the first loop
c_actor 22 after the first loop
c_actor 22 after the second loop
started aid = 22
c_actor 22 before the first loop
c_actor 22 after the first loop
c_actor 22 after the second loop
started aid = 22
c_actor 22 before the first loop
c_actor 22 after the first loop
c_actor 22 after the second loop
The situation is dierent if loop has a large value: we observe the preemption
of the thread of the rst created actor by the thread of the C INIT actor when
it receives the requests to create new actors. Once they are all created, the
three threads of the new actors execute sequentially:
--> neon-n procYield_u 10000000 & neon-n procYield_u 10000000 &
neon-n procYield_u 10000000 &
[1] 13570
[2] 13571
[3] 13573
--> started aid = 22 (= rst actor is created
c_actor 22 before the first loop (= thread starts execution
started aid = 2 (= second actor is created
c_actor 22 after the first loop (= rst thread resumes
started aid = 23 (= third actor is created
c_actor 22 after the second loop (= rst thread terminates
c_actor 2 before the first loop (= ^ main thread
c_actor 2 after the first loop (= j of the second
c_actor 2 after the second loop (= _ actor
c_actor 23 before the first loop (= ^ main thread
c_actor 23 after the first loop (= j of the third
c_actor 23 after the second loop (= _ actor
If the commands are launched with two arguments, the thread calls threadDelay.
Thus, the executing thread releases the processor and is placed at the end of
the corresponding running queue.
8.5. Creating and deleting a thread 211
--> neon-n procYield_u 50000000 1 & neon-n procYield_u 50000000 1 &
neon-n procYield_u 50000000 &
[1] 13600
[2] 13601
[3] 13603
--> started aid = 23
c_actor 23 before the first loop
started aid = 2
started aid = 22
c_actor 23 after the first loop (=rst thread yields processor
c_actor 2 before the first loop (=second thread executes
c_actor 2 after the first loop (=second thread yields processor
c_actor 22 before the first loop (=third thread executes
c_actor 22 after the first loop (=third thread yields processor
c_actor 23 after the second loop (=rst thread resumes execution
c_actor 2 after the second loop (=second thread resumes execution
c_actor 22 after the second loop (=third thread resumes execution
where:
the actorCap argument designates the actor where the thread is to be created.
The K MYACTOR value indicates the current actor;
the status argument may have the value K ACTIVE or K INACTIVE;
the structure pointed to by the schedParam argument denes the attributes
for scheduling the new thread. If its value is NULL, the new thread inherits the
scheduling characteristics (scheduling class and priority) of the calling thread;
212 Chapter 8. Threads
the structure pointed to by the startInfo argument denes the initial state of
the new thread. It is machine dependent and corresponds, on most conventional
processors, to the KnDefaultStartInfo f type containing the following elds:
- dsType whose type is KnStartInfoType: K DEFAULT START INFO is a stan-
dard predened value which will generally be used;
- dsSystemStackSize which is an unsigned int: it gives the size of the
system stack expected for the thread; the K DEFAULT STACK SIZE prede-
ned value is generally used;
- dsEntry whose type is KnPc: it gives the entry point of the thread. It
is generally dened as the address of a function which will be executed
when the thread starts;
- dsUserStackPointer which points to a void: for a user thread it gives
the initial user stack pointer. The user stack may be allocated by calling
the malloc function or by creating a new region in the actor. The top
of the stack is generally accessed through a dedicated register. On most
processors, (ix86 or Sparc for instance), allocation on this stack is per-
formed towards lower addresses. This is because the value of the stack
register decreases when a PUSH instruction is performed and increases for
a POP instruction. Thus, before calling the threadCreate primitive, the
value of the dsUserStackPointer eld of the structure has to be set to
the end of the space which has been allocated for the thread user stack. A
last remark concerns the denition of the location pointed to by the stack
pointer. It is generally the last word allocated on the stack: this means
that the PUSH operation rst decrements the value of the stack pointer
and allocates the word corresponding to the resulting value. Thus, if the
stack has been allocated using p=malloc(n), the initial value of the eld
may be p + n (that is the case on ix86 processors) or any value p + n - i
word-aligned. In our examples we will use p + n - sizeof(long) as the
initial value.
If the thread is created with supervisor privilege, it will use its system
stack which is automatically allocated by the system during thread cre-
ation and this eld is not used;
- dsPrivilege whose type is KnThreadPrivilege: it has as a value of
either K USERTHREAD if the thread is a standard thread or K SUPTHREAD if
the new thread is a supervisor thread.
If the call succeeds (and a new thread is created), K OK is returned and the
local identier of the new thread is returned at the location pointed to by the
threadLi argument.
8.5. Creating and deleting a thread 213
If an error occurs, a negative error code is returned:
value of errno error type
K EINVAL actorCap is an inconsistent capability
K EUNKNOWN actorCap does not correspond to a reachable actor
K EFAULT some data points outside the address space of the actor
K EPRIV the calling thread is not allowed to create a supervisor thread
K ENOTIMP the scheduling parameter requires a non-supported policy
8.5.1.2 Examples
a) We rst develop two functions which allow us to create threads dynamically.
These functions will be added to the utilities library. We add their proto-
types and some specic chorus header les in the "utilities.h" header le
associated to that library:
--> cat $CHORUS/include/utilities.h
#include <stdio.h>
#include <chorus.h>
#include <sched/chSched.h>
#include <sched/chFifo.h>
#include <sched/chRr.h>
void fprintCap(FILE *, char *, KnCap *);
void readCap(char **, KnCap *);
KnThreadLid newThread(KnPc function, int status, char *symbName);
KnThreadLid newThreadAttr(
KnCap *actorCap, KnPc function, int status,
int stackSize, int class, int prio);
The newThread function creates a new thread in the current actor:
function is the entry point of the new thread;
status has the value (K ACTIVE or K INACTIVE) dening the initial state of the
new thread;
symbName is the symbolic name of the new thread. A NULL value may be
used if the new thread has no name.
The other attributes of the new thread are chosen as follows:
the privilege of the new thread is deduced from the privilege of the actor
where it is created. It is a supervisor thread if the actor is a supervisor actor
and a user thread otherwise;
its scheduling attributes are the same as the attributes of the calling thread;
if the thread has the user privilege a user stack is dynamically allocated in
the actor by calling the malloc function. The size of the stack is set by the
USER STACK SIZE constant, predened as four pages in the source.
214 Chapter 8. Threads
--> cat $CHORUS/sources/utilities/lib/newThread.c
#include "utilities.h"
#define USER_STACK_SIZE 0x4000
KnThreadLid newThread(KnPc entryPoint, int status, char *symbName) {
KnActorPrivilege actorPriv;
KnDefaultStartInfo_f startInfo;
char *userStack;
KnThreadLid childLident = -1;
int result;
/* set default startInfo fields */
startInfo.dsType = K_DEFAULT_START_INFO;
startInfo.dsSystemStackSize = K_DEFAULT_STACK_SIZE;
/* get actor's privilege and set thread privilege */
result = actorPrivilege(K_MYACTOR, &actorPriv, NULL);
if(result != K_OK) {
fprintf(stderr, "error on actorPrivilege: %s\n", strSysError(result));
return -1;
}
startInfo.dsPrivilege =
(actorPriv == K_SUPACTOR) ? K_SUPTHREAD : K_USERTHREAD;
/* allocation of the thread's user stack for a user thread */
if (startInfo.dsPrivilege == K_USERTHREAD) {
if((userStack = (char *) malloc(USER_STACK_SIZE)) == NULL) {
fprintf(stderr, "error in stack allocation\n");
return -1;
}
startInfo.dsUserStackPointer =
(char *) userStack + USER_STACK_SIZE - sizeof(long);
}
else
startInfo.dsUserStackPointer = NULL;
/* set entry point for the new thread */
startInfo.dsEntry = entryPoint;
/* create new thread in status state */
result = threadCreate(K_MYACTOR, &childLident, status, NULL, &startInfo);
if (result == K_OK) {
if(symbName != NULL)
threadName(K_MYACTOR, childLident, NULL, symbName);
return childLident;
}
else {
fprintf(stderr, "error on threadCreate: %d\n", strSysError(result));
if (startInfo.dsPrivilege == K_USERTHREAD)
free(userStack);
return -1;
}
}
8.5. Creating and deleting a thread 215
The newThreadAttr function is a modied version of the previous one. It cre-
ates a new thread:
whose home actor's capability is passed as rst argument when calling the
function;
that has explicit attributes (user stack's size, scheduling class and priority).
Since the thread may be created in an actor dierent from the current actor, the
stack cannot be allocated using a malloc call. A new region is created in the
target actor by calling rgnAllocate, except if it is a supervisor thread or if the
size is 0. In this case, it may be useful to create an inactive user thread whose
user stack is not dened immediately, but is created later by copying the stack
of another thread (it is the case in the example given in 8.12 of a simulation of
the Unix fork primitive).
Furthermore, if the thread is created in an actor dierent from the current one,
we will assume that the entry point of the new thread is passed as a virtual
address matching a function in the target actor's virtual address space (and not
in the address space of the current one).
--> cat $CHORUS/sources/utilities/lib/newThreadAttr.c
#include "utilities.h"
KnThreadLid newThreadAttr(
KnCap *actorCap, /* target actor's capability */
KnPc entryPoint, /* new thread's entry point */
int status, /* status: K_ACTIVE or K_INACTIVE */
int stackSize, /* user stack's size in bytes */
int prioClass, /* scheduling class: K_SCHED_FIFO or K_SCHED_RR */
int prio /* thread's priority */
) {
int infiniteSleep( ) {
int ind;
printf(" I am the new thread %d\n", threadSelf( ));
/* long empty loop */
for(ind = 0; ind < 50000000; ind ++)
; /* long empty loop */
printf(" %d ==> I'm going to sleep\n", threadSelf( ));
/* entering an infinite delay */
threadDelay(K_NOTIMEOUT);
}
main( ) {
actorSelf(&actorCap);
printf("Hello from the main thread [%d] ", threadSelf( ));
fprintCap(stdout, "in target actor", &actorCap);
printf("Address of annexThread function: %p\n", annexThread);
threadDelay(K_NOTIMEOUT);
}
8.5. Creating and deleting a thread 221
-2-> neon targetActor_u
started aid = 23
Hello from the main thread [9] in target actor: 200000a6 869da80a 17 1
Address of annexThread function: 0xffff6020
We use the following command which calls the newThreadAttr function to
create a thread in the previous actor. This thread will execute the annexThread
function dened in the target actor. As shown by the results, this function has
virtual address 0xffff6020:
-1-> cat $CHORUS/sources/threads/creatingActor/creatingActor.c
#include "utilities.h"
KnCap targetActorCap;
KnPc entryPoint;
KnDefaultStartInfo_f startInfo;
KnThreadLid newThreadLid;
main(int argc, char *argv[ ]) {
/* getting the actor capability and the entry point */
readCap(argv + 1, &targetActorCap);
sscanf(argv[5], "%x", &entryPoint);
newThreadLid = newThreadAttr(&targetActorCap, (KnPc) entryPoint,
K_ACTIVE, 0x2000, K_SCHED_FIFO, 150);
if(newThreadLid < 0) {
fprintf(stderr, "error on newThreadAttr: %s\n",
strSysError(newThreadLid));
exit(2);
}
printf("New thread %d created", newThreadLid);
fprintCap(stdout, " in actor", &targetActorCap);
}
-1-> neon creatingActor_u 200000a6 869da80a 17 1 0xffff6020
started aid = 2
New thread 7 created in actor: 200000a6 869da80a 17 1
-1->
The new thread executes and prints its results:
+++ Entering annexThread function in target actor: 200000a6 869da80a 17 1
+++ Executing thread 7 in actor : 200000a6 869da80a 17 1
We can also check that the target actor contains two threads before killing it:
-1-> rsh neon arun cs -la 23
started aid = 2
ChorusOS r4.0.0 Site 0 Time 1d 14h 28m 1
....... LID .... TH# NAME
....... 0023 .... 002 targetActor_u
222 Chapter 8. Threads
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0007 150 00000000 00000000 ff6380 0- 1- 0
0009 140 00000000 00000000 ff6018 0- 1- 0 main
...........................
-1-> rsh neon akill 23
-1->
d) Creating a thread executing a function with parameters
In the previous examples, we created threads executing functions which were
not expecting parameters; the threadCreate call does not oer this service by
itself but lets the programmer do so if required.
Before giving the code of a newThreadArg function, which creates a new thread
executing a function with a variable number of arguments, we brie
y describe
the organization of the user stack of a thread on the systems that we use (ix86
platforms). This stack is used to keep track of function calls during the execution
of the thread. When a function is invoked, an activation record or frame is
pushed onto that stack and the frame related to a function call is popped o
when returning from the call. This frame contains the following information for
void functions calls:
the value of actual parameters as dened by the calling function. Parameters
are pushed rst onto the stack in reverse order;
the return address where execution will resume when returning from the call;
a link to the frame corresponding to the caller;
local variables (corresponding to automatic variables in C).
We can now give the source of a newThreadArg function that we add to the
utilities library. The prototype of this function
KnThreadLid newThreadArg(
KnPc function, /* entry point (a function) */
int status, /* thread's status: K_ACTIVE or K_INACTIVE */
char *symbName, /* thread's symbolic name */
int nbArg, /* number or parameters */
...
);
is added in the utilities.h header le.
This function creates a new thread executing a function with an arbitrary num-
ber of arguments, it is limited to user threads. In this code we use the set
of macros dened in the stdarg.h le which allow us to write functions with
a variable number of parameters. The nbArg parameter of the newThreadArg
function denes to the number of parameters of the function that the new thread
will execute, these parameters correspond to the ... ellipse notation. We use
long as a generic type of these arguments:
8.5. Creating and deleting a thread 223
--> cat $CHORUS/sources/utilities/lib/newThreadArg.c
#include "utilities.h"
#include <stdarg.h>
#define USER_STACK_SIZE 0x4000
The next command uses this function to create a new thread executing a func-
tion with 3 long parameters. The function prints the addresses and values of
its parameters and of its local variables before entering an innite delay:
--> cat $CHORUS/sources/threads/paramTh/paramTh.c
#include "utilities.h"
void annexThread(int a, int b, int c) {
int t[2] = {16, 256};
int x = 1024;
fprintf(stderr, "address of x: %p\n", &x);
fprintf(stderr, "addresses of t[0] and t[1]: %p, %p\n", t, t + 1);
fprintf(stderr, "a : address = %p value = %d\n", &a, a);
fprintf(stderr, "b : address = %p value = %d\n", &b, b);
fprintf(stderr, "c : address = %p value = %d\n", &c, c);
threadDelay(K_NOTIMEOUT);
}
main( ) {
KnThreadLid annexThreadLid;
annexThreadLid = newThreadArg((KnPc) annexThread, K_ACTIVE,
"annex", 3, 250, 35, -13);
if(annexThreadLid == -1) {
fprintf(stderr, "Error on newThreadParam\n");
exit(2);
}
fprintf(stderr, "Thread %d has been created\n", annexThreadLid);
threadDelay(K_NOTIMEOUT);
--> neon-n paramTh_u &
[1] 15548
--> started aid = 23
Thread 7 has been created
address of x: 0xfffedfe0
addresses of t[0] and t[1]: 0xfffedfe4, 0xfffedfe8
8.5. Creating and deleting a thread 225
a : address = 0xfffedff4 value = 250
b : address = 0xfffedff8 value = 35
c : address = 0xfffedffc value = -13
--> rsh neon arun cs -la 23; rsh neon akill 23
started aid = 2
ChorusOS r4.0.0 Site 0 Time 1d 16h 39m 32
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000ba 869da80a 00000017 0000067b 0023 USER STARTED 002 paramTh_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0007 140 00000000 00000000 ff6380 0- 1- 0 annex
0009 140 00000000 00000000 ff6018 0- 1- 0 main
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
200000ba 869da80a def no fac0ac 0 200000ba 869da80a
START SIZE OFFSET ALLOC OPTIONS
fffea000 0000c000 00000000 0000c000 WR
ffff6000 00009000 00000000 00009000 EX
-->
where actorCap identies the actor the thread belongs to and where the thread
is identied by threadLi. If the value of actorCap is K MYACTOR and the value of
threadLi is K MYSELF, then the current thread is deleted.
8.5.2.3 Examples
a) One of the most important use of the threadDelete primitive concerns the
termination of annex threads created in an actor.
Let us consider the following example where an annex thread is created and
simply returns after having printed a message:
8.5. Creating and deleting a thread 227
--> cat $CHORUS/sources/threads/threadRet/threadRet.c
#include "utilities.h"
annexThread( ) {
printf(" I am the new thread %d\n", threadSelf( ));
}
main( ) {
KnThreadLid threadLi;
threadLi = newThread((KnPc) annexThread, K_ACTIVE, "annex");
printf("A new thread has been created. Its local identifier is %d\n",
threadLi);
threadDelay(K_NOTIMEOUT);
}
Execution of this code leads to the following:
--> neon threadRet_u
started aid = 23
A new thread has been created. Its local identifier is 9
Segmentation fault thread 9 PC 0 faultAddr 0
I am the new thread 9
-->
The stack's frame relating to the initial function's call performed when activat-
ing a thread has a null return point eld. Thus, when trying to return from that
level, execution leads to an exception (segmentation fault) which terminates the
actor. The function called when starting the annex thread should terminate the
thread instead of returning. It is therefore imperative to call
threadDelete(K_MYACTOR, K_MYSELF);
instead of simply returning from the function. This modication leads to a
correct execution of the corresponding threadRet2 application:
--> neon-n threadRet2_u &
[1] 15828
--> started aid = 23
A new thread has been created. Its local identifier is 9
I am the new thread 9
The results of the cs utility show that the actor still exists and only contains
its main thread:
--> rsh neon arun cs -la 23
started aid = 2
ChorusOS r4.0.0 Site 0 Time 1d 18h 19m 14
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000c3 869da80a 00000017 00000001 0023 USER STARTED 001 threadRet2_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0007 140 00000000 00000000 ff6380 0- 1- 0 main
.........................
228 Chapter 8. Threads
b) The next example shows that the main thread of an actor may be
deleted by a call to threadDelete without aecting the other threads
belonging to that actor:
--> cat $CHORUS/sources/threads/threadDel/threadDel.c
#include "utilities.h"
annexThread( ) {
printf(" I am the new thread %d\n", threadSelf( ));
threadDelay(K_NOTIMEOUT);
}
main( ) {
KnThreadLid threadLi;
threadLi = newThread((KnPc) annexThread, K_ACTIVE, "annex");
printf("A new thread has been created. Its local identifier is %d\n",
threadLi);
threadDelete(K_MYACTOR, K_MYSELF);
}
--> neon-n threadDel_u &
[1] 15869
--> started aid = 2
A new thread has been created. Its local identifier is 8
I am the new thread 8
We can observe that the c actor still exists and just contains the annex thread:
--> rsh neon arun cs -la 2
started aid = 22
ChorusOS r4.0.0 Site 0 Time 1d 18h 26m 8
ACTOR-UI KEY LID TYPE STATUS TH# NAME
200000c5 869da80a 00000002 000006c3 0002 USER STARTED 001 threadDel_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0008 140 00000000 00000000 ff61cc 0- 1- 0 annex
..............................
It is worth noting that the eect of the call may not be instantaneous if the
target thread is currently executing a system call. What is guaranteed in such
a case is that the thread will be stopped before returning from the call. The
call returns either K OK when it succeeds, or a negative error code (K EINVAL,
K EUNKNOWN or K EFAULT).
threadCreate threadActivate
K ACTIVE
ACTIVE ACTIVE
ACTIVE EXTERNAL EXTERNAL
INTERNAL ABORT ABORTED
HANDLER
8.9.2 Example
In this example, the main thread of the c actor dynamically creates 4 new
threads by calling the newThread function that we developed. The second
thread which is created (value 1 of ind) is created in the inactive state. All
other threads are active and execute the annex function. Once these dierent
threads have been created, the main thread calls the threadStat primitive
236 Chapter 8. Threads
and displays the number of threads in the actor, and for each thread, its local
identier and its state:
--> cat $CHORUS/sources/threads/threadStat/threadStat.c
#include "utilities.h"
KnThreadStat stat[10];
void annex( ) {
printf(" *** I am the thread %d\n", threadSelf( ));
threadDelay(K_NOTIMEOUT);
}
main( ) {
int ind, result, state;
printf("I am the main thread %d\n", threadSelf( ));
/* create 4 new threads */
for(ind = 0; ind < 4; ind ++) {
/* if ind is 1, the thread is inactive */
state = (ind == 1) ? K_INACTIVE : K_ACTIVE;
result = newThread((KnPc) annex, state, NULL);
printf("New thread %d has been created\n", result);}
/* yield the processor: all active threads will execute */
threadDelay(K_NOBLOCK);
result = threadStat(K_MYACTOR, 0, stat, sizeof(stat));
printf("There are %d threads in the actor\n", result);
/* for each thread in the actor, display its local id and its state */
for(ind = 0; ind < result; ind ++) {
printf("Thread %d", stat[ind].tsLid);
printf(" ==> state is %d: %s\n", stat[ind].tsStatus,
(stat[ind].tsStatus == K_ACTIVE) ? "K_ACTIVE" : "K_INACTIVE");
}
threadDelay(K_NOTIMEOUT);
}
--> neon threadStat_u
started aid = 23
I am the main thread 7
New thread 8 has been created
New thread 9 has been created
New thread 10 has been created
New thread 11 has been created
*** I am the thread 8
*** I am the thread 10
*** I am the thread 11
There are 5 threads in the actor
Thread 11 ==> state is 0: K_ACTIVE
Thread 10 ==> state is 0: K_ACTIVE
Thread 9 ==> state is 2: K_INACTIVE
Thread 8 ==> state is 0: K_ACTIVE
Thread 7 ==> state is 0: K_ACTIVE
8.10. Execution times 237
Execution of the code shows that the inactive thread (whose local identier
is 9) which was created in the K INACTIVE state is not scheduled (only three
messages *** I am the thread ... are displayed).
8.10 Execution times
When the VTIMER feature is active, it is possible to get, for each thread, the
times it consumed in internal mode (executing inside the actor where it was
created) and in external mode (executing in a cross-actor invocation).
The information can be accessed by calling the function
#include <vtimer/chVtimer.h>
int threadTimes(
KnCap *actorCap,
KnThreadLid threadLi,
KnTimeVal *internal,
KnTimeVal *external
);
If the parameter threadLi has the value K ALLACTORTHREADS, the function re-
turns the sum of execution times in each mode of all the threads belonging to
the actor identied by actorCap.
Upon success the function returns K OK. A negative error code is returned when
an error occurs (K EINVAL, K EUNKNOWN or K EFAULT).
In the following example, the main thread creates a new thread before entering
a busy loop. It then extracts information on execution time before yielding the
processor. At that time, the second thread executes: it enters a busy loop and
extracts information on execution times. When it terminates, the main thread
is scheduled and displays execution time information before terminating:
--> cat $CHORUS/sources/threads/threadTimes/threadTimes.c
#include "utilities.h"
#include <vtimer/chVtimer.h>
void annex( ) {
int ind;
KnTimeVal internal, external;
KnThreadLid localId;
localId = threadSelf( );
for(ind = 0; ind < 500000000; ind ++) ; /* a busy long loop */
threadTimes(K_MYACTOR, K_MYSELF, &internal, &external);
printf("th %d ==> internal=%ds.%d ns, external=%ds.%d ns\n",
localId, internal.tmSec, internal.tmNSec,
external.tmSec, external.tmNSec);
238 Chapter 8. Threads
threadTimes(K_MYACTOR, K_ALLACTORTHREADS, &internal, &external);
printf(
"th %d all threads ==> internal=%ds.%d ns, external=%ds.%d ns\n",
localId, internal.tmSec, internal.tmNSec,
external.tmSec, external.tmNSec);
threadDelete(K_MYACTOR, K_MYSELF);
}
main( ) {
int ind;
KnTimeVal internal, external;
KnThreadLid localId, annexId;
localId = threadSelf( );
printf("main thread's local id: %d\n", localId);
/* create a new active thread with no name */
annexId = newThread((KnPc)annex, K_ACTIVE, NULL);
for(ind = 0; ind < 500000000; ind ++) ; /* a busy loop */
/* get and print execution times */
threadTimes(K_MYACTOR, K_MYSELF, &internal, &external);
printf("th %d ==> internal=%ds.%d ns, external=%ds.%d ns\n",
localId, internal.tmSec, internal.tmNSec,
external.tmSec, external.tmNSec);
threadTimes(K_MYACTOR, K_ALLACTORTHREADS, &internal, &external);
printf(
"th %d all threads ==> internal=%ds.%d ns, external=%ds.%d ns\n",
localId, internal.tmSec, internal.tmNSec,
external.tmSec, external.tmNSec);
threadDelay(K_NOBLOCK); /* yield the processor */
threadTimes(K_MYACTOR, K_ALLACTORTHREADS, &internal, &external);
printf(
"th %d all threads ==> internal=%ds.%d ns, external=%ds.%d ns\n",
localId, internal.tmSec, internal.tmNSec, external.tmSec,
external.tmNSec);
}
--> rsh neon arun cs lM
............ VTIMER ....... (= the feature is active
--> neon threadTimes_u
started aid = 23
main thread's local id: 7
th 7 ==> internal=4s.300000000 ns, external=0s.0 ns
th 7 all threads ==> internal=4s.300000000 ns, external=0s.0 ns
th 8 ==> internal=4s.300000000 ns, external=0s.0 ns
th 8 all threads ==> internal=8s.600000000 ns, external=0s.0 ns
th 7 all threads ==> internal=8s.600000000 ns, external=0s.0 ns
-->
We will see in 12.4 that the VTIMER feature exports various other functions for
timing threads.
8.11. Managing a thread's context 239
8.11 Managing a thread's context
All threads have an execution context which has two components:
a hardware context: it is machine dependent and is dened as the set
of general purpose register values (stack pointer, program counter, . . . ). It is
pushed onto the stack when a trap, an exception or a preemption occurs;
a software context dened as two software registers (user privilege register
and supervisor privilege register). This context is used by the system itself to
provide the per-thread data service. Thus it is highly recommended not to use
it in applications.
The context of a given thread may be got or set by calling the primitive
#include <exec/chExec.h>
int threadContext(
KnCap *actorCap,
KnThreadLid threadLi,
unsigned int type,
void *oldContext,
void *newContext
);
int fork( ) {
KnThreadLid stubThreadLid;
printf("entering fork by thread %d\n",
callingThreadLid = threadSelf( ));
/* a new thread is created for executing the fork */
stubThreadLid = newThread((KnPc)forkStub, K_ACTIVE, "stub");
printf("stub thread : %d\n", stubThreadLid);
callingActorLid = agetId( );
/* the calling thread stops */
threadStop(K_MYACTOR, K_MYSELF);
actorSelf(&actorCap);
if (uiEqual(&actorCap.ui, &childActorCap.ui) == 1) {
threadDelay(K_NOBLOCK);
return 0; /* it is the child c_actor */ }
else
return childActorCap.key.keyHead; /* it is the parent c_actor */
}
8.12. A complete example: a fork function 245
The callingFork application given below uses the fork service. Due to im-
plementation optimization leaning on private data, the threads must yield the
processor before terminating (by calling threadDelay):
--> cat $CHORUS/sources/threads/fork/callingFork.c
#include "utilities.h"
main( ) {
KnCap actorCap; int result;
actorSelf(&actorCap);
fprintCap(stdout, "Initial actor", &actorCap);
if ((result = fork( )) == 0) { /* it is the child */
actorSelf(&actorCap);
fprintf(stdout, "fork( ) is %d in thread %d", result, threadSelf( ));
fprintCap(stdout, " in actor", &actorCap);
threadDelay(K_NOBLOCK);
}
else { /* it is the parent */
actorSelf(&actorCap);
fprintf(stdout, "fork( ) is %d in thread %d", result, threadSelf( ));
fprintCap(stdout, " in actor", &actorCap);
threadDelay(K_NOBLOCK);
}
}
--> neon callingFork_u
started aid = 23
Initial actor: 20000121 869da80a 17 8dc
entering fork by thread 8
stub thread : 7
Entering stubThread
A new c_actor is created: 22
New c_actor's capability: 20000122 869da80a 16 0
A thread 9 is created in the new c_actor
capa: 20000122 869da80a 16 0
capa: 20000121 869da80a 17 8dc
fork( ) is 22 in thread 8 in actor: 20000121 869da80a 17 8dc
fork( ) is 0 in thread 9 in actor: 20000122 869da80a 16 0
-->
Remark: some kind of miracle occurred: the actor was promoted to a c actor
and inherited the standard input/output les of the initial actor. This is due
to the use of private data which tends to mix up the two actors.
8.13 Posix threads (pthreads)
8.13.1 Introduction
The POSIX-THREADS library oers a compatible implementation of the Posix
interface for thread management and synchronization.
A Posix thread-identier (type pthread t) is equivalent to the Chorus local
identier of the underlying Chorus thread.
We brie
y present some of the functions of the corresponding API (a complete
presentation of pthreads programming can be found in the book of N. Bradfor
et al).
8.13.2 Posix threads' attributes
They are packed in a pthread attr t opaque structure. Examples of these
attributes and the constants corresponding to their default values are:
the stack's size (PTHREAD STACK MIN);
8.13. Posix threads (pthreads) 247
the stack's address (dynamically allocated);
the detach state (PTHREAD CREATE JOINABLE);
scheduling policy and priority.
This type of object can be globally initialized by a call to the function
#include <pthread.h>
int pthread attr init(pthread attr t *attr);
The eect of the call is to give default values for every individual attribute.
Every attribute may be modied further by specic calls:
pthread attr setstacksize for modifying the stack's size eld;
pthread attr setstackaddr for modifying the stack's address eld;
pthread attr setdetachstate for modifying the detach status eld (le-
gal values for this status are PTHREAD CREATE JOINABLE and PTHREAD CRE-
ATE DETACHED).
A thread also has scheduling attributes:
a scheduling policy: it is SCHED RR, SCHED FIFO or SCHED OTHER;
a priority: it corresponds to the sole sched priority eld of a sched pa-
ram object.
These attributes may be set by calling the functions
pthread attr setschedpolicy;
pthread attr setschedparam.
8.13.3 Creating a pthread
A call to the function
#include <pthread.h>
int pthread create(
pthread t *thread,
const pthread attr t *attr,
void *(*start routine(void *)),
void *arg
);
creates a new thread with the attributes specied by the attr parameter in the
current actor. If the attr parameter has a NULL value, the default attributes
are used. If the call succeeds, the identier of the new thread is stored at
address thread. The new thread will start execution by calling the function
start routine with arg as the only argument. Returning from that function will
be the equivalent for that thread of calling pthread exit with an argument
equal to the return value of the function.
248 Chapter 8. Threads
8.13.4 Yielding the processor
A thread may yield the processor by calling the function
#include <pthread.h>
void pthread yield(void);
The thread is queued at the end of the list of threads waiting to run relative to
its priority and the rst thread in that list is awoken.
8.13.5 Terminating a pthread
A call to the function
#include <pthread.h>
void pthread exit(void *status);
terminates the calling thread. An implicit call to that function is made when
a thread other than the main thread returns from the start routine. The value
passed as an argument to the function is used as the thread's exit status. It is
important to note that the thread (if dierent from the main thread) still exists
after calling pthread exit.
8.13.6 Killing a pthread
By a call to the function
#include <pthread.h>
int pthread kill(
pthread t thread,
int sig
);
the current thread may send a given signal to the thread with the identication
thread.
The value of sig may be:
either SIGTHREADKILL: the target thread is deleted (the signal may not
be caught, ignored or blocked);
or 0: no signal is sent. The call tests whether the target thread exists.
8.13. Posix threads (pthreads) 249
8.13.7 Example
--> cat $CHORUS/sources/threads/pthreads/pthread.c
#include <stdio.h>
#include <stdlib.h>
#include <chorus.h>
#include <pthread.h>
pthread_t pthread;
KnCap actorCap;
main( ) {
int result;
actorSelf(&actorCap);
fprintCap(stdout, "actor", &actorCap);
printf("Hello I am %d and I'm trying to create a pthread\n", threadSelf( ));
result = pthread_create(&pthread, NULL, pthread_function,
"argument for the pthread");
if(result != 0) {
fprintf(stderr, "error on pthread_create: %d\n", result);
exit(1);
}
printf("Success !!! Pthread number %d has been created\n", pthread);
printf("Main thread yields the processor ...\n");
pthread_yield( ); /* yield the processor */
printf("Main thread enters threadDelay ...\n");
threadDelay(K_NOTIMEOUT);
printf("Main thread is terminating ...\n");
}
-2-> neon pthread_u
started aid = 25
actor: 20000136 869da80a 19 948
Hello I am 9 and I'm trying to create a pthread
Success !!! Pthread number 8 has been created
Main thread yields the processor ...
A pthread has been created with chorus local identifier: 8
Function called with argument: argument for the pthread
pthread is exiting
Main thread enters threadDelay ...
250 Chapter 8. Threads
-1-> rsh neon arun cs -la 25
started aid = 24
ChorusOS r4.0.0 Site 0 Time 2d 12h 37m 13
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000136 869da80a 00000019 00000948 0025 USER STARTED 002 pthread_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0008 140 00000000 00000000 ff61cc ff- 1- 0
0009 140 00000000 00000000 ff6018 0- 1- 0 main
.........................
-1-> neon threadAbort_u 20000136 869da80a 19 948 9
started aid = 24
-1->
If the state of the thread semaphore is POSTED, the thread is not blocked (the
K_OK value is returned by the function and the thread semaphore's new state
is UNPOSTED). If the semaphore is in the UNPOSTED state, the thread is
blocked. The waitLimit parameter denes a timeout.
If a thread other than the owner performs this type of call, unspecied be-
havior results. This choice has been made to minimize the overhead of thread
semaphores.
If the function returns due to the POSTED value of the semaphore, K OK is
returned. Otherwise, a negative error code is returned:
value of errno error type
K EINVAL semaphore not correctly initialized or invalid waitLimit
K EFAULT semaphore outside the address space
K ETIMEOUT timeout occurred
K EABORT the call has been aborted
254 Chapter 9. Synchronization
9.2.5 Example
In the following example the main thread and a dynamically created thread of
the actor execute a compute function concurrently. The main thread can only
terminate and delete the actor when the other thread has nished its compu-
tation. Thus, the main thread uses a thread semaphore and when it nishes
its computation, calls threadSemWait. When it nishes its own computation,
the other thread in turn posts the semaphore to signal its termination; thus
the main thread may then exit. When the outcome command is called with
parameters, the main thread enters the round robin scheduling class.
In the code given below we use a function whose prototype is
int compute(int);
This will be presented in 12.3.4. It uses the services oered by the VTIMER
feature, allowing the denition of a virtual timeout which interrupts an innite
loop and requires a dedicated supervisor server to execute the serverCompute
application. Here we assume that the compute function has been integrated
within the libutilities.a library. We shall simply state calling the function
simulates a computation whose duration is a random number of milliseconds in
the range [0..10000]. If the parameter is not 0, a message is printed when
entering and exiting the function.
--> cat $CHORUS/sources/synchronization/threadSem/threadSem.c
#include "utilities.h"
KnThSem thSem;
KnCap actorCap;
void annex( ) {
int result;
compute(1); /* <-- computation .... */
fprintf(stderr, "thread %d posts the semaphore\n", threadSelf( ));
result = threadSemPost(&thSem);
fprintf(stderr, "in thread %d threadSemPost returns %d\n",
threadSelf( ), result);
fprintf(stderr, "thread %d terminates\n", threadSelf( ));
threadDelete(K_MYACTOR, K_MYSELF);
}
main(int argc) {
int result;
KnThreadLid threadLi;
KnThreadDefaultSched schedAttr;
actorSelf(&actorCap);
if (argc != 1) {
threadScheduler(K_MYACTOR, K_MYSELF, &schedAttr, NULL);
schedAttr.tdClass = K_SCHED_RR;
threadScheduler(K_MYACTOR, K_MYSELF, NULL, &schedAttr);
}
9.2. Thread semaphores 255
/* initialize the thread semaphore */
result = threadSemInit(&thSem);
fprintf(stderr, "main thread (%d): threadSemInit = %d\n",
threadSelf( ), result);
/* create a new thread with the newThread function */
threadLi = newThread((KnPc)annex, K_ACTIVE, "annex");
if(threadLi < 0)
exit(1);
fprintf(stderr, "main thread: new thread %d is created\n", threadLi);
compute(1); /* <-- computation .... */
fprintf(stderr, "main thread: threadSemWait is called\n");
result = threadSemWait(&thSem, K_NOTIMEOUT);
fprintf(stderr, "main thread: threadSemWait returns %d\n", result);
}
The next set of results correspond to two dierent executions within the round
robin scheduling policy. We can observe, in the rst set, round robin in the
computation step and that the main thread waits for the semaphore:
--> neon threadSem_u RR
started aid = 24
main thread (13): threadSemInit = 0
main thread: new thread 14 is created
From compute: thread 13 in actor 24 starts computation
From compute: thread 14 in actor 24 starts computation
256 Chapter 9. Synchronization
From compute: thread 13 in actor 24 ends computation
main thread: threadSemWait is called (=
main thread blocks
From compute: thread 14 in actor 24 ends computation
thread 14 posts the semaphore
in thread 14 threadSemPost returns 0
thread 14 terminates
main thread: threadSemWait returns 0
-->
In the second execution, the new thread posts the semaphore before the main
thread blocks when calling threadSemWait. Thus, when it calls this function,
the semaphore is already posted, the thread is not blocked (it returns immedi-
ately from the call) and terminates:
--> neon threadSem_u RR
started aid = 24
main thread (13): threadSemInit = 0
main thread: new thread 14 is created
From compute: thread 13 in actor 24 starts computation
From compute: thread 14 in actor 24 starts computation
From compute: thread 14 in actor 24 ends computation
thread 14 posts the semaphore
in thread 14 threadSemPost returns 0
thread 14 terminates
From compute: thread 13 in actor 24 ends computation
main thread: threadSemWait is called
main thread: threadSemWait returns 0
-->
void annex1( ) {
int ind;
KnTimeVal delay;
fprintf(stderr, "Entering first thread [%d]\n", threadSelf( ));
/* waiting for 1 second */
K_MILLI_TO_TIMEVAL(&delay, 1000);
threadDelay(&delay);
compute(1); /* computation step */
threadDelete(K_MYACTOR, K_MYSELF);
}
void annex2( ) {
int ind;
fprintf(stderr, "Entering second thread [%d]\n", threadSelf( ));
mutexGet(&mutex);
compute(1); /* computation step */
mutexRel(&mutex);
fprintf(stderr, "Second thread [%d] after mutexRel\n", threadSelf( ));
threadDelete(K_MYACTOR, K_MYSELF);
}
9.4. Real-time mutexes 259
main( ) {
int ind;
KnThreadLid lid1, lid2;
KnTimeVal delay;
KnThreadDefaultSched schedParam;
/* initialize the mutex */
mutexInit(&mutex);
/* get scheduling attributes */
threadScheduler(K_MYACTOR, K_MYSELF, &schedParam, NULL);
/* create a new thread with priority = myPriority + 10 */
lid1 = newThreadAttr(K_MYACTOR, (KnPc) annex1, K_ACTIVE, 0x4000,
schedParam.tdClass, schedParam.tdPriority + 10);
/* create a new thread with priority = myPriority + 20 */
lid2 = newThreadAttr(K_MYACTOR, (KnPc) annex2, K_ACTIVE, 0x4000,
schedParam.tdClass, schedParam.tdPriority + 20);
K_MILLI_TO_TIMEVAL(&delay, 1100);
threadDelay(&delay); /* wait for a little more than one second */
fprintf(stderr, "Main thread [%d] before mutexGet\n", threadSelf( ));
mutexGet(&mutex);
fprintf(stderr, "Main thread [%d] after mutexGet\n", threadSelf( ));
K_MILLI_TO_TIMEVAL(&delay, 3000);
threadDelay(&delay); /* wait for 3 seconds */
}
--> rsh neon aps | grep serverCompute
0 25 serverCompute_s 0 N/A (=
server is active
--> neon mutex_u
started aid = 24
Entering first thread [9]
Entering second thread [14]
From compute: thread 14 in actor 24 starts computation
From compute: thread 9 in actor 24 starts computation
Main thread [13] before mutexGet
From compute: thread 9 in actor 24 ends computation
From compute: thread 14 in actor 24 ends computation
Main thread [13] after mutexGet
Second thread [14] after mutexRel
-->
decrements the value of the semaphore by one. If the new value is strictly
negative, the calling thread is blocked and queued in the queue corresponding to
the semaphore. The thread will remain blocked until a V operation is performed
on the semaphore, or until expiration of the period dened by the value of
waitLimit. In particular, the K NOTIMEOUT value may be used for an abortable
innite delay and the K NOTIMEOUT NOABORT value for a non abortable call.
9.5.3 Signaling a semaphore: semV
A call to the primitive
#include <sync/chSem.h>
int semV(KnSem *semaphore);
increments the value of the semaphore by one. If there are threads blocked
on the semaphore, one is awoken. The function returns K OK except if the
semaphore structure has been spoiled, in which case K EINVAL is returned.
9.5.4 Example
In the following example, which is a classic implementation of the producer-
consumer relationship, we demonstrate the possibility of synchronizing threads
belonging to dierent actors by mapping a region containing semaphores and
data in the address spaces of the actors.
The prodCons.h le contains the denition of the structure related to the orga-
nization of the region shared by the producers and the consumers: the regions
contains two semaphores, a mutex and a circular buer:
9.5. General semaphores 263
--> cat $CHORUS/include/prodCons.h
#include "utilities.h"
typedef struct {
KnSem full, /* # of used cells */
empty; /* # of unused cells */
KnMutex mutexRear, /* for filling a cell */
mutexFront; /* for consuming a cell */
int front, /* first used cell */
rear, /* first unused cell */
sizeArray; /* total # of cells */
int array[1];
} data;
The region is created and initialized by a repository actor whose main thread
executes the following command and terminates:
--> cat $CHORUS/sources/synchronization/semaphores/repository/repository.c
#include "prodCons.h"
data *sharedData;
main( ) {
KnCap actorCap;
KnRgnDesc region;
int result;
region.size = vmPageSize( );
region.options = K_ANYWHERE | K_WRITABLE | K_FILLZERO | K_NODEMAND;
result = rgnAllocate(K_MYACTOR, ®ion);
if(result != K_OK) {
fprintf(stderr, "error on regionAllocate: %s\n", strSysError(result));
exit(2);
}
/* sharedData -> sizeArray should be (vmPageSize( ) - n) / sizeof(int) */
/* where n = 2 * (sizeof(KnSem) + sizeof(KnMutex) + sizeof(int) */
sharedData = (data *) region.startAddr;
sharedData -> sizeArray = 4;
semInit(&sharedData -> empty, sharedData -> sizeArray);
semInit(&sharedData -> full, 0);
mutexInit(&sharedData -> mutexRear);
mutexInit(&sharedData -> mutexFront);
sharedData -> front = sharedData -> rear = 0;
actorSelf(&actorCap);
fprintCap(stdout, "Depositary actor", &actorCap);
printf("Data regions starts at %p\n", sharedData);
threadDelete(K_MYACTOR, K_MYSELF);
}
The producers execute the following application in the round robin scheduling
class: the capability of the repository and the address of the region in that actor
to be mapped to the producer's address space are passed as arguments:
264 Chapter 9. Synchronization
--> cat $CHORUS/sources/synchronization/semaphores/producer/producer.c
#include "prodCons.h"
#include <sched/chSched.h>
#include <sched/chRr.h>
data *sharedData;
produce( ) {
int ind;
compute(1); /* a computing step */
semP(&sharedData -> empty, K_NOTIMEOUT);
mutexGet(&sharedData -> mutexRear);
sharedData -> array[sharedData -> rear] = 1 + rand( ) % 1000;
fprintf(stderr, "%d has been produced by c_actor %d\n",
sharedData -> array[sharedData -> rear], agetId( ));
sharedData -> rear = (sharedData -> rear + 1) % sharedData -> sizeArray;
mutexRel(&sharedData -> mutexRear);
semV(&sharedData -> full);
}
consume( ) {
int ind, value;
semP(&sharedData -> full, K_NOTIMEOUT);
mutexGet(&sharedData -> mutexFront);
value = sharedData -> array[sharedData -> front];
fprintf(stderr, "%d has been read by c_actor %d\n", value, agetId( ));
sharedData -> front = (sharedData -> front + 1) % sharedData -> sizeArray;
mutexRel(&sharedData -> mutexFront);
semV(&sharedData -> empty);
compute(1); /* computation step */
}
is a request for posting, in the event set pointed to by eventSet, all the events
corresponding to a bit set to 1 in the mask.
For instance, if mask has the decimal value 194 which corresponds to hexadec-
imal 0xc2 or to the binary value 11000010, the events 1, 6 et 7 are posted.
This value corresponds to (1<<1)+(1<<6)+(1<<7). The call has no eect for
the events of the set which are already in the POSTED state.
The function returns K OK upon success and a negative value otherwise (K EFAULT
if eventSet points outside the address space and K EINVAL if it has not been
correctly initialized).
After this call, the events of the set pointed to by eventSet corresponding to
bits of the mask whose value is 1 are in the UNPOSTED state (the call has no
eect on events which were already in the UNPOSTED state).
270 Chapter 9. Synchronization
9.6.6 Example: a synchronization barrier's implementa-
tion
In the following example, we use the service provided by event sets to imple-
ment a barrier synchronization of n threads cooperating in an application [see
Andrews]. All threads belong to the same actor and each thread involved in
the application is identied by an index: the main thread that creates the other
threads is identied by 0, the other threads are identied by 1, . . . , n-1 if
there are n threads. A thread arriving at the barrier will post, in the event
set arrived the event corresponding to its identication in the application. So
when all threads have reached the barrier, n events will have been posted. In
a rst approach, the "rendez-vous" of the n threads at the barrier is achieved
symmetrically by calling eventWait in each thread with the {0,1, ...,n-1}
value.
In the following example, several threads belonging to the same actor execute
the same annex function whose argument is the index of the thread. Each
thread executes the following operations:
- it rst enters a computation phase preceding the barrier corresponding
to the compute function. The CPU time consumed when executing this
function is computed randomly and may vary from one thread to another
and even from one call to another for a given thread;
- after this computation, it signals its arrival at the barrier by posting the
event corresponding to its number in the arrived event set that should
have been initialized previously;
- it waits for all other threads to reach the barrier: fullMask contains the
set of all events between 0 and n-1;
- it terminates.
We now give the code for the onceBarrier application, where the main thread
uses the newThreadArg function (see 8.5.1.2.d) to create a new thread executing
a function with an integer argument. When the command is called, the total
number of threads participating in the rendez-vous (including the main thread)
is passed as rst argument and if more arguments are given, the round robin
scheduling policy is selected:
--> cat $CHORUS/sources/synchronization/events/onceBarrier/onceBarrier.c
#include "utilities.h"
#include <sync/chEvent.h>
KnCap actorCap;
int numberOfThreads; /* # of participating threads */
KnEventSet arrived; /* for signaling threads' arrival */
unsigned int fullMask;
9.6. Event sets 271
/* function executed by the thread: ident is the thread's index */
void annex(int ident) {
unsigned int ind, myMask;
fprintf(stderr, "entering thread %d\n", ident);
/* build the mask corresponding to the thread's index */
myMask = 1 << ident;
compute(1); /* compute step before the barrier */
/* thread arrives to the barrier */
fprintf(stderr, "Thread %d arrived at barrier\n", ident);
/* the corresponding event is posted to signal its arrival at barrier */
eventPost(&arrived, myMask);
/* the thread waits for all threads to signal their arrival at barrier */
eventWait(&arrived, fullMask, K_EVENT_AND, NULL, K_NOTIMEOUT);
fprintf(stderr, "Thread %d after the barrier\n", ident);
threadDelete(K_MYACTOR, K_MYSELF);
}
main(int argc, char *argv[ ]) {
int result, ind;
unsigned int myMask;
KnThreadLid threadLi;
KnThreadDefaultSched schedAttr;
if (argc == 1) {
fprintf(stderr, "bad usage\n");
exit(1); }
if (argc > 1) {
schedAttr.tdClass = K_SCHED_RR;
schedAttr.tdPriority = 140;
threadScheduler(K_MYACTOR, K_MYSELF, NULL, &schedAttr); }
/* the total number of threads is passed as first argument */
numberOfThreads = atoi(argv[1]);
eventInit(&arrived); /* event set is initialized */
/* the mask corresponding to all threads is built */
fullMask = 0;
for(ind = 0; ind < numberOfThreads; ind ++)
fullMask += (1 << ind);
actorSelf(&actorCap);
srand(actorCap.key.keyHead + actorCap.key.keyTail); /* initialize random */
/* loop to create the new threads */
for(ind = 1; ind < numberOfThreads; ind ++) {
/* new thread created with newThreadArg */
threadLi = newThreadArg((KnPc) annex, K_ACTIVE, NULL, 1, ind);
if (threadLi == -1) {
fprintf(stderr, "error on newThreadArg\n");
exit(2); }
fprintf(stderr, "thread %d [application's id %d] is created\n",
threadLi, ind); }
/* the main thread is participating to the rendez-vous */
annex(0);
}
272 Chapter 9. Synchronization
The command is rst executed to achieve a rendez-vous of three threads (value
3 of the argument) within the fo scheduling policy. When all threads have ter-
minated, the actor has to be killed (the actor still exists but no longer contains
threads):
--> rsh neon arun cs -lM
started aid = 22
ChorusOS r4.0.0 Site 0 Time 2h 20m 0
CORE SCHED_CLASS [ FIFO RR RT ] VTIMER ..... EVENT ......
--> rsh neon aps | grep serverCompute
0 2 serverCompute_s 0 N/A
--> neon-n onceBarrier_u 3& (=3 is the number of threads
[2] 4580
--> started aid = 22
thread 7 [application's id 1] is created
thread 8 [application's id 2] is created
entering thread 0
From compute: thread 9 in actor 22 starts computation
entering thread 1
From compute: thread 7 in actor 22 starts computation
entering thread 2
From compute: thread 8 in actor 22 starts computation
From compute: thread 8 in actor 22 ends computation
Thread 2 arrived at barrier
From compute: thread 9 in actor 22 ends computation
Thread 0 arrived at barrier
From compute: thread 7 in actor 22 ends computation
Thread 1 arrived at barrier
Thread 1 after the barrier
Thread 2 after the barrier
Thread 0 after the barrier
Threads arrive at the barrier in the order in which they were created (0 identies
the main thread in the application). We can observe that a thread is blocked
until all other threads have reached the barrier. When the last thread reaches
the barrier, it posts its event. Thus, when it calls eventWait, all threads have
posted their events and the awaited condition is satised; the thread is not
blocked and can terminate its execution (it is the rst thread which signals that
it has passed the barrier). Other threads are then awoken and resume execution
in the order in which they were blocked (0 and then 1).
9.6. Event sets 273
The same application has been executed within the round robin scheduling
policy: as the computation time is dierent for every thread and is signicant,
time slicing applies eectively and threads arrive at the barrier in a random
order (here 2, 1 and 0):
--> neon-n onceBarrier_u 3 RR & (= 3 is the number of threads
[2] 4614
--> started aid = 22
thread 9 [application's id 1] is created
thread 7 [application's id 2] is created
entering thread 0
From compute: thread 8 in actor 22 starts computation
entering thread 1
From compute: thread 9 in actor 22 starts computation
entering thread 2
From compute: thread 7 in actor 22 starts computation
From compute: thread 7 in actor 22 ends computation
Thread 2 arrived at barrier
From compute: thread 9 in actor 22 ends computation
Thread 1 arrived at barrier
From compute: thread 8 in actor 22 ends computation
Thread 0 arrived at barrier
Thread 0 after the barrier
Thread 2 after the barrier
Thread 1 after the barrier
--> rsh neon akill 22 (=actor is killed
-->
What happens if, after this barrier, the dierent threads have to enter a new
computation cycle and have to meet again at the same barrier? The event set
has to be cleared and the diculty is to clear it properly. An event can be
cleared only when all threads waiting on this event have passed the barrier, and
all events have to be cleared before any thread in the set reaches the barrier
again.
A symmetrical approach cannot be used. We give below the complete source
code of a barrier application where the rst parameter is the total number of
threads and the second one denes the number of steps that each thread will
perform (this value is the number of times the barrier will be passed by each
thread). If other parameters are given, they correspond to a request for round
robin scheduling.
--> cat $CHORUS/sources/synchronization/events/barrier/barrier.c
#include "utilities.h"
#include <sync/chEvent.h>
int numberOfThreads, /* # of participating threads */
numberOfSteps; /* # of iterations (# of rendez-vous) */
274 Chapter 9. Synchronization
KnEventSet arrived, /* for signaling threads arrival */
nextStep; /* for allowing threads continuation */
a thread suspends its execution until the target thread terminates, unless it
already terminated or it was created in the PTHREAD CREATE DETACHED state.
#include <pthread.h>
int pthread mutex init(
pthread mutex t *mutex,
const pthread mutexattr t *mutexAttr
);
If the mutexAttr value is NULL, default mutex attributes are used. If the mutex
is statically allocated, it may be initialized with the default attributes by using
the PTHREAD MUTEX INITIALIZER macro as in the following:
pthread mutex t mutex = PTHREAD MUTEX INITIALIZER;
A mutex may be destroyed by calling the function
#include <pthread.h>
int pthread mutex destroy(pthread mutex t *mutex);
After the call the mutex has an illegal value. This type of call should not be
applied to a locked mutex.
278 Chapter 9. Synchronization
9.7.3.2 Locking a Posix mutex
A mutex may be locked by calling
#include <pthread.h>
int pthread mutex lock(pthread mutex t *mutex);
If the mutex is already locked, the calling thread is blocked. If a thread tries to
lock a mutes that it already locked, a deadlock will occur.
Locking a mutex may also be achieved by a non blocking call:
#include <pthread.h>
int pthread mutex trylock(pthread mutex t *mutex);
The function returns -1 and errno has the value EBUSY.
9.7.3.3 Unlocking a Posix mutex
A thread that previously locked a mutex may release it by calling
#include <pthread.h>
int pthread mutex unlock(pthread mutex t *mutex);
The thread that has been waiting the longest time (if any) for the mutex will
be awakened and will return from its pthread mutex lock call.
9.7.4 Posix condition variables
Condition variables correspond to the pthread cond t type and their use is
associated with Posix mutexes. The principles of use are as follows:
given a var variable of any type, a thread may wait before resuming execu-
tion for a condition to be satised by the value of this variable (this condition
corresponds to the true value of a boolean expression);
a var mutex mutex and a var cond condition variable are associated to this
variable. The mutex is used for protecting operations on var and the condition
variable is used for transmitting its change of state.
9.7.4.1 Initializing a condition variable
A pthread cond t object encapsulates attributes of a variable condition. This
type of variable is initialized by calling the function
#include <pthread.h>
int pthread cond init(
pthread cond t *cond,
pthread condattr t *attr
);
9.7. Posix synchronization mechanism 279
When attr is NULL, the default value is used. A statically allocated condition
variable may be initialized to the default value as in the following sequence:
pthread cond t cond = PTHREAD COND INITIALIZER;
It is possible to destroy a condition variable if no thread is currently blocked on
it by calling the primitive
#include <pthread.h>
int pthread cond destroy(pthread cond t *cond);
This awakens the thread, if any, that has been blocked on the condition for the
longest time. If no thread is currently blocked on the condition variable, the
call has no eect.
It is also possible to awaken all threads that are currently blocked on a condition
by calling
#include <pthread.h>
int pthread cond broadcast(pthread cond t *cond);
280 Chapter 9. Synchronization
9.7.4.4 General outline
We just give here the general outline for using condition variables.
a) from the waiting thread point of view:
/* waiting for var to satisfy a condition */
type var;
pthread cond t var cond;
pthread mutex t var mutex;
. . .
. . .
pthread mutex lock(&var mutex);
while(!condition(var)){
/* waiting for var cond to be signaled */
pthread cond wait(&var cond, &var mutex);
}
/* we can use the variable and resume execution */
.
.
/* var mutex is released */
pthread mutex unlock(&var mutex);
The standard header le errno.h used in the ChorusOS contains the following
macros:
#define __errno ptdErrnoAddr
#define errno (*(__errno( )))
which allow each thread to access its private errno word. The following example
shows that property:
10.2. Standard services 283
-1-> cat $CHORUS/sources/privateData/errno/errno.c
#include "utilities.h"
#include "fcntl.h"
#include "errno.h"
void annex(int i) {
printf("annex %d: address of errno=%p errno=%d\n",
i, &errno, errno);
threadDelay(K_NOTIMEOUT); /* yield the processor */
threadDelete(K_MYACTOR, K_MYSELF);
}
main( ) {
int descrpt;
KnCap actorCap;
actorSelf(&actorCap);
fprintCap(stdout, "actor", &actorCap);
printf("main thread is %d\n", threadSelf( ));
threadDelay(K_NOTIMEOUT); /* yield the processor */
descrpt = open("/file", O_RDONLY); /* non existing file */
if(descrpt == -1)
printf("main: address of errno=%p errno=%d\n", &errno, errno);
newThreadArg((KnPc) annex, K_ACTIVE, "annex", 1, 1);
newThreadArg((KnPc) annex, K_ACTIVE, "annex", 1, 2);
threadDelay(K_NOTIMEOUT); /* yield the processor */
}
-1->
-2-> neon errno_u
neon errno_u
started aid = 25
actor: 20000143 869da80a 19 0
main thread is 9
-2->
These results show that the errno variable has dierent addresses in the dif-
ferent threads: these addresses lie within the rst data region allocated to the
actor and are accessed through the per-thread data service.
10.2.2 Threads' local identiers
A call to the function
#include <pd/chPd.h>
int ptdThreadId(void);
returns the unique thread identier for the thread (it is the same as the thread
local identier returned by a call to threadSelf).
10.3 The private data keys
Like the corresponding POSIX service, the denition of threads' or supervisor
actors' private data is based on the keys concept: this key has the type pdKey
(dened as unsigned int). The keys are opaque indexes which are dynamically
allocated and are used to locate the thread-specic data.
10.4. Chorus per-thread private data 285
At most PTD KEYS MAX (default value is 62) keys may be dened. The values
0 and 1 are associated to errno (PTD ERRNO) and the local thread identier
(PTD THREADID).
10.4 Chorus per-thread private data
This service is provided for all threads (that is, belonging either to user or
supervisor actors).
This call creates a new key whose value is returned at the privateKey address in
the calling actor's address space. This value is an opaque index that is used for
locating thread-specic data. The same key may be used for dierent threads,
but the values that are associated to a key are maintained on a per-thread basis.
This value will persist throughout the life of a thread.
Once a key has been created, the NULL value is associated to the new key for
every active thread. If a new thread is created in an actor, the NULL value will
also be bound to the dierent existing keys.
The optional destructor parameter allows a destructor function to be associated
to a key. Then, when a thread exits, if the value associated to the key is
not NULL, the function will be called with the current value associated to the
key as its unique parameter. For user level threads, the destructor function
will be invoked if the thread calls ptdThreadDelete. For supervisor threads,
the destructor function will be invoked at thread termination (threadDelete,
actorDelete, . . . ).
The function returns 0 (and *privateKey is initialized) if the call is successful
and a positive error value otherwise:
return value error type
PD ENOKEY no more keys available
PD ENOMEM not enough memory to create the key
PD ESERVER the PD manager is not accessible
286 Chapter 10. Private data
A thread-specic key previously allocated by calling ptdKeyCreate may be
deallocated by calling the primitive
#include <pd/chPd.h>
int ptdKeyDelete(PdKey key);
The values associated with the key need not to be NULL but it is the respon-
sibility of the application to perform cleanup operations either before or after
calling ptdKeyDelete. No destructor function is called by ptdKeyDelete.
Once a key has been deleted, it should not be used any more: behavior is un-
dened if it is used.
The function returns 0 if it succeeds and PD EINVAL if key is not a valid value
or PD ESERVER if the PD server is unreachable.
If no value is currently associated to the key in the current thread, the func-
tion returns NULL. If the value of key has not been obtained as result of a
ptdKeyCreate call or if it has been deleted by calling ptdKeyDelete, the eect
of the call is undened.
A thread may also get the value associated to a key in another thread by calling
the primitive
#include <pd/chPd.h>
int ptdRemoteGet(
KnCap *actorCap,
KnThreadLid threadLi,
PdKey key,
void **value
);
The value associated to the key in the thread identied by threadLi in the actor
designated by actorCap is returned at *value address. When it is called in user
mode, actorCap must designate the current actor.
The function returns 0 if successful. Otherwise, it returns a positive error code
(PD EINVAL or PD ESERVER).
int counterValue( ) {
int *cnt;
mutexGet(&mutex);
if(initialized == 0) { /* the key has not been created yet */
/* the key is created */
if (ptdKeyCreate(&counterKey, free) != 0) {
/* the key has not been created: the actor exits */
mutexRel(&mutex);
exit(1);
}
fprintf(stderr, "the key (%d) has been created in actor %d\n",
counterKey, agetId( ));
initialized = 1;
}
10.4. Chorus per-thread private data 289
mutexRel(&mutex);
/* value associated to the key */
cnt = ptdGet(counterKey);
if (cnt == NULL) {
/* if the value is NULL, a counter is allocated */
cnt = (int *) malloc(sizeof (int));
/* the counter is initialized to 0 */
*cnt = 0;
/* the counter is associated to the key */
if(ptdSet(counterKey, cnt) != 0)
threadDelete(K_MYACTOR, K_MYSELF);
}
return (*cnt) ++;
}
-->
main( ) {
int ind;
for(ind = 1; ind < 4; ind ++)
newThreadArg((KnPc) annex, K_ACTIVE, "annex", 1, ind);
annex(0);
}
290 Chapter 10. Private data
Two actors corresponding to the useCounter application are created. We can
observe the creation of the key in the actors, the execution of the dierent
threads and the evolution of the private counter of each thread:
--> neon-n useCounter_u & neon-n useCounter_u &
[1] 3256
[2] 3257
--> started aid = 25
In thread 0 in actor 25
the key (3) has been created in actor 25
actor 25 thread 0: counter = 0
In thread 1 in actor 25
actor 25 thread 1: counter = 0
In thread 2 in actor 25
actor 25 thread 2: counter = 0
In thread 3 in actor 25
actor 25 thread 3: counter = 0
started aid = 24
In thread 0 in actor 24
the key (3) has been created in actor 24
actor 24 thread 0: counter = 0
In thread 1 in actor 24
actor 24 thread 1: counter = 0
In thread 2 in actor 24
actor 24 thread 2: counter = 0
In thread 3 in actor 24
actor 24 thread 3: counter = 0
actor 25 thread 1: counter = 1
actor 24 thread 1: counter = 1
actor 25 thread 3: counter = 1
actor 25 thread 0: counter = 1
actor 24 thread 3: counter = 1
actor 24 thread 0: counter = 1
actor 25 thread 2: counter = 1
actor 24 thread 2: counter = 1
-->
Finally when all threads are terminated, actors have no more threads and are
killed:
--> rsh neon arun cs | grep useCounter
started aid = 26
20000149 869da80a 00000018 00000000 0024 USER STARTED 000 useCounter_u
20000148 869da80a 00000019 000009c3 0025 USER STARTED 000 useCounter_u
--> rsh neon akill 24
--> rsh neon akill 25
-->
10.5. Chorus per-actor private data 291
10.5 Chorus per-actor private data
The primitives presented in this section may only be called from a supervisor
actor even though, in the case of padGet and padSet, the target actor may be
a user actor. Any attempt to use one of these primitives from a user actor will
return the PD EUSER error value.
void shuffle( ) {
int position, current, number, loop1, loop2, *deck, *privateDeck;
if((privateDeck = padGet(K_MYACTOR, poker)) == NULL) {
printf("allocation of privateHand\n");
svPagesAllocate((VmAddr *) &deck, 53 * sizeof(int),
K_NOWAITFORMEMORY, NULL);
for(loop1 = 0; loop1 < 53; loop1 ++)
deck[loop1] = 0;
deck[53] = 51; /* the bottom of the deck */
padSet(K_MYACTOR, poker, deck);
printf("in actor private should be %p\n", deck);
deck = NULL; /* address of deck is hidden and private now */
privateDeck = padGet(K_MYACTOR, poker);
printf("privateDeck is %p\n", privateDeck);
}
number = 52;
for(loop1 = 1; loop1 <= 13; loop1 ++)
for(loop2 = 0; loop2 < 4; loop2 ++) {
position = rand( ) % number;
current = 0;
while(position != 0) {
if(privateDeck[current] == 0) position --;
current ++;}
while(privateDeck[current] != 0) current ++;
privateDeck[current] = loop1;
number --; }
for(loop1 = 0; loop1 < 52; loop1 ++) {
printf("%2d ", privateDeck[loop1]);
if (loop1 % 20 == 19)
putchar('\n');
}
putchar('\n');
printf("top = %d bottom = %d\n",
privateDeck[52], privateDeck[53]);
}
294 Chapter 10. Private data
main( ) {
KnCap actorCap; actorSelf(&actorCap);
printf("main thread is %d\n", threadSelf( ));
fprintCap(stdout, "there is a deck in actor", &actorCap);
padKeyCreate(&poker, (KnPdHdl) terminate);
srand(actorCap.key.keyTail + actorCap.key.keyHead);
shuffle( );
threadDelay(K_NOTIMEOUT);
/* will resume execution when aborted (see 8.8) */
play(poker);
}
We now give the code of the play function. Each player corresponds to a thread
executing a player function; it receives a hand of ve cards extracted from the
top of the deck. This hand is per-thread private data: no other thread, except
a cheating one, should access it. The game has been simplied as each player
receives ve consecutive cards from the deck. Furthermore, we assume in the
implementation that the underlying system was using a fo scheduling pol-
icy: the synchronization between threads is achieved by yielding the processor
(through threadDelay(K NOBLOCK) calls):
--> cat $CHORUS/sources/privateData/poker/play.c
#include "utilities.h"
#include <pd/chPd.h>
KnThreadLid mainThread;
PdKey handKey; int *deck;
void terminateThread(int *hand) {
free(hand);
printf("thread %d is freeing %p\n", ptdThreadId( ), hand);
if (ptdThreadId( ) == mainThread) {
threadDelay(K_NOBLOCK);
printf("thread %d is exiting\n", ptdThreadId( ));
exit(0); }
10.5. Chorus per-actor private data 295
else {
printf("thread %d is leaving\n", ptdThreadId( ));
threadDelete(K_MYACTOR, K_MYSELF); }
}
void player( ) {
int loop, *hand, *privateHand;
printf("thread %d is entering\n", ptdThreadId( ));
if((privateHand = ptdGet(handKey)) == NULL) {
printf("thread %d is allocating privateHand\n", ptdThreadId( ));
hand = (int *) malloc(5 * sizeof(int));
ptdSet(handKey, hand);
printf("in thread %d privateHand should be %p\n",
ptdThreadId( ), hand);
hand = NULL; /* address of hand is hidden and private now */
printf("in thread %d privateHand is %p\n",
ptdThreadId( ), privateHand = ptdGet(handKey));
}
for(loop = 0; loop < 5; loop ++)
privateHand[loop] = deck[deck[52] ++];
/* the thread is loosing the lead */
threadDelay(K_NOBLOCK);
if(ptdThreadId( ) == mainThread)
printf("----------\n");
printf("thread %d has got: ", ptdThreadId( ));
for(loop = 0; loop < 5; loop ++)
printf("%d ", privateHand[loop]);
putchar('\n");
privateHand = NULL;
threadDelay(K_NOBLOCK);
if(ptdThreadId( ) == mainThread)
printf("----------\n");
ptdThreadDelete( );
}
associates the block of memory pointed to by value to the key (normally obtained
by calling call pthread key create)
This call copies count bytes from the address srcAddr in the user address space of
the home actor of the calling thread, into the address dstAddr in the supervisor
address space:
srcAddr User address
space (context
count of the calling actor)
Supervisor
address space
dstAddr
If the function is called from outside a lap handler (which must have been
connected by calling svLapCreate, svMsgHandler, svAbortHandler, svExc-
Handler or svTrapConnect) the K EINVAL value is returned.
deletes the local access point whose descriptor is pointed to by lapDesc. If the
lap was created with the K LAP SAFE option, it will be deleted only when all the
current invocations of the lap will have completed.
It is important to note that symbolic names which could have been associated
to the lap by calling svLapBind are not aected by the lap's deletion and must
be explicitly deleted by calling svLapUnbind.
The call returns K OK if it succeeds, and K EINVAL if lapDesc does not point to
a valid lap descriptor.
11.3.6 Examples
11.3.6.1 Installing a lap in the current actor
We now develop a function which can be used to install a lap in the calling
actor. It will be added to the libutilities.a library and corresponds to the
prototype
int lapInstall(KnLapHdl, void *, char *, char, KnLapDesc *);
which is added in the utilities.h header le associated with our library.
The lapInstall function is passed ve parameters:
306 Chapter 11. Local access points (LAPs)
the handler address which will be associated to the lap;
the cookie which will be passed as second argument to the handler upon
invocation;
a symbolic name which will be bound to the lap;
if the fourth parameter is 'p' the lap will be protected, therefore name
resolution will only be allowed to trusted threads;
the fth parameter is a pointer on a KnLapDesc object which should have
been allocated previously.
--> cat $CHORUS/sources/utilities/lib/lapInstall.c
#include "utilities.h"
int lapInstall(KnLapHdl lapHdl, void *cookie, char *lapName,
char mode, KnLapDesc *lapDesc) {
int result, options;
/* the lap is created int safe mode */
result = svLapCreate(K_MYACTOR, lapHdl, (void *)cookie,
K_LAP_SAFE, lapDesc);
if(result < 0) {
fprintf(stderr, "error on svLapCreate: %s\n", strSysError(result));
return result;
}
/* the symbolic name is bound to the lap */
options = (mode == 'p') ? K_LAP_PROTECTED : 0;
result = svLapBind(lapDesc, lapName, options);
if(result < 0) {
fprintf(stderr, "error on svLapBind: %s\n", strSysError(result));
svLapDelete(lapDesc);
return result;
}
return K_OK;
}
The service will rst copy 16 (0x10) bytes from the addr address in the super-
visor address space into the twoAddrPtr->addr1 address in the user address
space. It will then copy 16 bytes from the twoAddrPtr->addr2 in the user
address space into the addr address in the supervisor address space.
We rst give the code of the function corresponding to the service itself:
--> cat $CHORUS/sources/laps/lapCp/server/userSupCp.c
#include "utilities.h"
typedef struct {
char *addr1;
char *addr2; } twoAddr;
/* a mutex is used for synchronizing executions */
KnMutex mutex = K_KNMUTEX_INITIALIZER;
/* definition and initialization of the supervisor area addr */
char addr[16] = "Initial value";
void handlerCp(twoAddr *twoAddrPtr, void *cookie) {
int result;
fprintf(stderr, "%s\n", cookie);
mutexGet(&mutex);
/* data is copied from addr to the actor's address space */
result = svCopyOut(addr, (VmAddr) twoAddrPtr -> addr1, 16);
if(result < 0) {
fprintf(stderr, "error on svCopyOut: %s\n", strSysError(result));
return; }
/* data is copied from the actor's address space to addr */
result = svCopyIn((VmAddr) twoAddrPtr -> addr2, addr, 16);
if(result < 0) {
fprintf(stderr, "error on svCopyIn: %s\n", strSysError(result));
return; }
mutexRel(&mutex);
}
A lap is associated to this service by executing the lapCp application whose
code is given now:
--> cat $CHORUS/sources/laps/lapCp/server/lapCp.c
#include "utilities.h"
main( ) {
void handlerCp( );
int result;
KnLapDesc lapDesc;
/* request for installing the lap by calling lapInstall */
result = lapInstall((KnLapHdl)handlerCp, "RD-WR", "RdWr", 0, &lapDesc);
308 Chapter 11. Local access points (LAPs)
/* if installation fails, delete region and kill c_actor */
if(result < 0)
exit(2);
fprintf(stderr, "The lap in installed\n");
/* if the lap is installed, the main thread is deleted */
threadDelete(K_MYACTOR, K_MYSELF);
/* actor has no more thread but still exists once the lap is installed */
}
Once the lap is installed, the main thread of the actor is deleted. The actor no
longer has a thread, but on the other hand it has an address space:
--> rsh neon arun cs -la 2
started aid = 23
ChorusOS r4.0.0 Site 0 Time 45m 21
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000026 869da80a 00000002 00000001 0002 SUP STARTED 000 lapCp_s.r
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
20000026 869da80a def no fac324 0 20000026 869da80a
START SIZE OFFSET ALLOC OPTIONS
7bbe8000 00002000 00000000 00002000 WR SU
7bbed000 00001000 00000000 00001000 WR SU
7bbee000 00009000 00000000 00009000 EX SU
-->
At that point, if we try to create a second actor executing the same command,
we get a failure diagnostic due to a con
ict on the lap's name:
--> neon-n lapCp_s.r &
[2] 9464
--> started aid = 23
error on svLapBind: Memory locking conflict
If we delete the c actor owning the lap handler, we can check that the binding
still exists (even if the lap no longer exists):
--> rsh neon akill 2
[1] + Done ........
--> neon-n lapCp_s.r &
[1] 9468
--> started aid = 2
error on svLapBind: Memory locking conflict
main( ) {
int result;
KnCap actorCap;
actorSelf(&actorCap);
fprintCap(stderr, "Installing lap handlers in", &actorCap);
/* first lap with unprotected binding "lapin" is associated to hdl1 */
result = lapInstall((KnLapHdl) hdl1, "first lap", "lapin", 0, &lap1);
11.3. The server's point of view 311
if (result != K_OK)
exit(1);
/* second lap with protected binding "rabbit" is associated to hdl1 */
result = lapInstall((KnLapHdl) hdl1, "second lap", "rabbit", 'p', &lap2);
if (result != K_OK) {
svLapDelete(&lap1);
svLapUnbind("rabbit");
exit(1); }
/* third lap with unprotected binding "vuosi" is associated to hdl2 */
result = lapInstall((KnLapHdl) hdl2, "third lap", "vuosi", 0, &lap3);
if (result != K_OK) {
svLapDelete(&lap1);
svLapUnbind("rabbit");
svLapDelete(&lap2);
svLapUnbind("cony");
exit(1); }
fprintf(stderr, "*** The three laps are installed ***\n");
threadDelay(K_NOTIMEOUT);
fprintf (stderr, "Lap server is aborting .......\n");
svLapDelete(&lap1);
svLapDelete(&lap2);
svLapDelete(&lap3);
svLapUnbind("lapin");
svLapUnbind("rabbit");
svLapUnbind("vuosi");
}
We now create the c actor which installs and owns the lap handlers:
--> neon-n multiLap_s.r &
[2] 9495
--> started aid = 23
Installing lap handlers in: 20000031 869da80a 17 94
*** The three laps are installed ***
Information about the related actor is displayed. We can check that its main
thread is not deleted. It simply enters an abortable innite threadDelay, and
when it aborts, it resumes execution and deletes the laps and unbind the sym-
bolic names:
--> rsh neon arun cs -la 23
ChorusOS r4.0.0 Site 0 Time 50m 4
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000031 869da80a 00000017 00000094 0023 SUP STARTED 001 multiLap_s.r
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0008 140 00000000 00000000 ff6330 0- 1- 0 main
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
20000031 869da80a def no fac0ac 0 20000031 869da80a
312 Chapter 11. Local access points (LAPs)
START SIZE OFFSET ALLOC OPTIONS
7bbdb000 00002000 00000000 00002000 WR SU
7bbde000 0000a000 00000000 0000a000 EX SU
7bbec000 00001000 00000000 00001000 WR SU
-->
The situation is dierent if the lapResolve call does not use this
ag. We
produced a lapCpClient2 u code with the call
result = lapResolve(&lapDesc, "RdWr", 0); }
and the execution trace is the following:
--> neon-n lapCp_s.r & (=
a new server is created
[1] 9608
--> started aid = 2
The lap in installed
--> neon svLapUnbind_s.r RdWr (=
unbind the lap
started aid = 22
Unbinding of RdWr done
--> neon lapCpClient2_u VVVVVVVVVVVVVVVVVVVVV
started aid = 22
The calling thread is eectively blocked. We can observe that killing the actor
owning the lap does not awaken the thread:
-1-> rsh neon akill 2 (=
the server is killed
-1-> rsh neon aps | grep lapCpClient2
0 22 lapCpClient2_u 0 N/A
-1->
We now create (from a dierent shell) a server whose lap is bound to the RdWr
name:
-1-> neon-n lapCp_s.r & (= a new server is created
[1] 12022
-1-> started aid = 2
The lap in installed
RD-WR (=due to the awoken client
The calling thread is awoken and displays the initial string:
11.4. The client's point of view 315
Initial value (=
initial string (rst client)
--> neon lapCpClient2_u XXXXXXXXXXXXXXXXXXXXXX
started aid = 22
VVVVVVVVVVVVVVVV
RD-WR (=
on the server's screen
b) What happens if the actor owning the handler(s) is deleted?
An interesting situation occurs if the actor owning a lap handler is deleted
but its name is not unbound. The name resolution will succeed and the calling
thread will detect an error when trying to access the handler (as the owning
actor no longer exists).
The following trace illustrates what happens when trying to access the handler
after the last lapCp s.r c actor we created has been killed:
--> rsh neon akill 2
[1] + Done ......... (=
on the server's screen
-1->
--> neon lapCpClient2_u YYYYYYYYYYYYYYYYYYY
started aid = 2
error on lapInvoke: Invalid argument
-->
1janot lapin invocation is a tribute to Jean de La Fontaine fable "Le Chat, la Belette et
le petit Lapin"
2roger rabbit invocation is a tribute to the Robert Zemeckis hero
3BugsBunny rabbit invocation is a tribute to Tex Avery
4janiksen vuosi invocation is a tribute to the nnish author Arto Paasilinna's book
316 Chapter 11. Local access points (LAPs)
fprintf(stderr, "Resolution of %s ", argv[2]);
fprintCap(stderr, "succeeds in", &actorCap);
fprintf(stderr, "Thread %d is invoking the handler\n", threadSelf( ));
result = lapInvoke(&lapDesc, argv[1]);
if(result < 0) {
fprintf(stderr, "error in lapInvoke: %s\n", strSysError(result));
exit(1); }
actorSelf(&actorCap);
fprintf(stderr, "Thread %d ", threadSelf( ));
fprintCap(stderr, "is back in actor", &actorCap);
}
-->
In the following trace, we invoke the rst lap whose name is rabbit. The
righthand side corresponds to the server's trace and the left to the client's
trace:
--> neon multiLapCl_u janot lapin
started aid = 2
Resolution of lapin succeeds in: 2000003f 869da80a 2 0
Thread 9 is invoking the handler
Finally, we run the command for invoking the third lap that has been bound to
the vuosi name:
--> neon multiLapCl_u janiksen vuosi
started aid = 2
Resolution of vuosi succeeds in: 20000042 869da80a 2 1
Thread 9 is invoking the handler
Entering handler2: cookie = third lap
Current thread: 9
in actor (from actorSelf): 20000031 869da80a 17 94
Invoker actor: 20000042 869da80a 2 1
First argument: janiksen
We can observe that the execution actor (whose aid is 23) is stopped, not the
home one (whose aid is 2), the K MYACTOR used as argument in the actorStop
call refers to the execution actor. The results of a cs command corroborate this
observation:
-2-> rsh neon arun cs -la 23 (= execution actor (lap server)
started aid = 22
ChorusOS r4.0.0 Site 0 Time 1h 48m 10
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000031 869da80a 00000017 00000094 0023 SUP STOPPED 001 multiLap_s.r
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0008 140 00000000 00000000 ff6330 0- 1- 4 main
......................................
-2-> rsh neon arun cs -la 2 (= home actor (invoker of the lap)
started aid = 22
ChorusOS r4.0.0 Site 0 Time 1h 50m 34
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000042 869da80a 00000002 00000001 0002 USER STARTED 001 multiLapCl_u
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0009 140 00000000 00000000 ff61a4 0- 1- 0 main
......................................
-2->
318 Chapter 11. Local access points (LAPs)
We create a new c actor invoking the lapin handler:
-2-> neon multiLapCl_u janot lapin
started aid = 22
Resolution of lapin succeeds in: 20000046 869da80a 16 1
Thread 7 is invoking the handler
We can observe that the calling thread is stopped when trying to execute the
handler located in the stopped actor.
We nally restart the home actor by using the actorStart utility presented in
section 6.2.4:
-2-> neon actorStart_u 20000031 869da80a 17 94
started aid = 24
-2->
We observe that the rst thread resumes execution and returns in its home
actor while the second thread enters and executes the handler.
K_MYACTOR has been stopped
Leaving handler2: cookie = third lap
Thread 9 is back in actor: 20000042 869da80a 2 1
Current thread: 7
in actor (from actorSelf): 20000031 869da80a 17 94
Invoker actor: 20000046 869da80a 16 1
First argument: janot
Leaving handler1: cookie = first lap
Thread 7 is back in actor: 20000046 869da80a 16 1
Finally, in the following we run two concurrent executions of the same handler,
and we can observe that executions are nested: this is due to the call to the
threadDelay primitive inside the handler.
the resolution of the Universal Time is returned in resolution. This value repre-
sents the smallest possible dierence between two distinct values of the universal
time.
12.2.2 Getting time of day: univTime
A call to the function
#include <date/chDate.h>
int univTime(KnTimeVal *currentTime);
returns the time of day in Universal Time in currentTime. If the time of day is
not set yet, currentTime will be set to an invalid value: tmSec = 0 and tmNSec
= K INVALID NSEC.
In this call
actorCap points to the capability of the actor concerned by the timer;
threadPool points to the descriptor of the thread pool object where the timer
has to be inserted. The corresponding object must have been previously initial-
ized by calling timerThreadPoolInit;
the only recognized value of clockType is currently K CLOCK REALTIME, which
is the system time returned by sysTime;
cookie points to a value which will be provided to threads returning from
timerThreadPoolWait calls applied to this threadPool when this timer expires;
after the call, the value pointed to by timerLi is the local identier of the
newly created timer.
12.3. Chorus timers 327
The function returns K OK if successful, otherwise a negative value to indicate
the error:
return value error type
K EINVAL clockType is not K CLOCK REALTIME or threadPool
has not been initialized
K ENOMEM no more timers can be created
K EFAULT one address is outside the caller's address space
12.3.7 Example
In the example given below:
if the command is called with an argument, the main thread rst sets the
doThreadDelay variable to 1. Then it initializes a thread pool and creates two
timers which it associates to that thread pool. It creates two new threads which
will execute the timerThread function. The two timers are armed:
- one is periodic: it has an initial relative delay of 3 seconds and a period
of 2 seconds;
- the second one is a one-shot timer whose delay is absolute: this delay is
equal to the current time when it is armed plus 14 seconds.
Finally the main thread is deleted;
in the timerThread function called by the two new threads, the calling thread
is blocked (without timeout) on timerThreadPoolWait; it waits for expiration
of a delay corresponding to a timer associated to the pool. When awoken, a
thread displays its local identier, the cookie that is associated to the timer and
the overrun value.
330 Chapter 12. Time management
If the timer was the periodic timer and if doThreadDelay is equal to 1, the
awoken thread sleeps for 7 seconds before calling timerThreadPoolWait once
more. If the timer was the one-shot timer the thread cancels the periodic timer
and exits 20 seconds later.
This can be illustrated as follows:
time
3 5 7 9 11 13 14
--> cat $CHORUS/sources/time/actorTimer/actorTimer.c
#include "utilities.h"
#include <etimer/chEtimer.h>
KnThreadPool pool;
int periodicTimerLocalId, oneshotTimerLocalId;
char *periodic = "Periodic timer"; /* cookie */
char *oneshot = "Oneshot timer"; /* cookie */
int doThreadDelay = 0;
void timerThread( ) {
KnThreadLid myLocalId;
int result;
KnITimer periodicTimer;
char *cookie;
int overrun;
KnTimeVal delay, currentTime;
myLocalId = threadSelf( );
while(1){
/* waiting for a timer expiration */
result = timerThreadPoolWait(&pool, (void **)&cookie, &overrun,
K_NOTIMEOUT);
printf("Thread %d: overrun = %d\n", myLocalId, overrun);
printf(" cookie = %s\n", cookie);
/* the thread has been awoken due to the periodic timer */
if (cookie == periodic) {
/* wait for 7 seconds if doThreadDelay is 1 */
if(doThreadDelay == 1) {
K_MILLI_TO_TIMEVAL(&delay, 7000);
threadDelay(&delay);
}
/* back to the timerThreadPoolWait call */
continue;
}
12.3. Chorus timers 331
/* the thread has been awoken due to the one-shot timer */
else {
periodicTimer.ITmValue.tmSec = 0;
periodicTimer.ITmValue.tmNSec = 0;
periodicTimer.ITmReload.tmSec = 0;
periodicTimer.ITmReload.tmNSec = 0;
timerSet(K_MYACTOR, periodicTimerLocalId, K_TIMER_INTERVAL,
&periodicTimer, NULL);
/* get and print current time */
result = sysTime(¤tTime);
printf(" Current time = %d\n", currentTime.tmSec);
/* wait for 20 seconds before exiting */
K_MILLI_TO_TIMEVAL(&delay, 20000);
threadDelay(&delay);
exit(0);
}
}
}
We rst execute the command without argument: when a thread is awoken due
to the periodic timer, it quickly calls timerThreadPoolWait and is blocked on
this call:
--> neon actorTimer_u
started aid = 23
Threads 8 and 9 have been created
Current time in seconds is: 14914
Thread 8: overrun = 0
cookie = Periodic timer
Thread 9: overrun = 0
cookie = Periodic timer
Thread 8: overrun = 0
cookie = Periodic timer
Thread 9: overrun = 0
cookie = Periodic timer
Thread 8: overrun = 0
cookie = Periodic timer
Thread 9: overrun = 0
cookie = Periodic timer
Thread 8: overrun = 0
cookie = Oneshot timer
Current time = 14928
-->
We now execute the command with an argument. When the rst thread is
awoken due to the periodic timer (which occurs at relative time 3), it sleeps
for 7 seconds. At relative times 5, 7 and 9 the periodic timer expires but
no thread can be awoken as the thread which was awoken earlier due to this
timer has not called timerThreadPoolWait. Thus, an overrun with a value of
3 is created. Furthermore, the thread is not blocked when it eectively calls
timerThreadPoolWait. While it sleeps for 7 seconds, the periodic timer expires
12.4. The VTIMER feature 333
at relative times 11 and 13. The one-shot timer expires at relative time 14 and
the second thread is awoken, it cancels the periodic timer and sleeps for 20
seconds before exiting. When the rst thread calls timerThreadPoolWait (at
approximately relative time 14), it is not blocked and nds an overrun of 2.
Nothing else occurs before the end of the actor:
--> neon actorTimer_u doThreadDelay
started aid = 23
Threads 8 and 7 have been created
Current time in seconds is: 14991
Thread 8: overrun = 0
cookie = Periodic timer
Thread 8: overrun = 3
cookie = Periodic timer
Thread 7: overrun = 0
cookie = Oneshot timer
Current time = 15005
Thread 8: overrun = 2
cookie = Periodic timer
-->
A virtual timeout is set on the execution time of the thread identied by threadLi
in the actor whose capability is pointed to by actorCap. If threadLi has the
K ALLACTORTHREADS value, the timeout will apply to the total execution time of
all threads belonging to the actor. The
ag argument may have the following
values:
- K VTIME INTERNAL: only the internal time (the time consumed when executing
in the home actor) is counted in relation to the timeout;
- K VTIME TOTAL: all execution time (internal and external) is counted. It is im-
portant to note that when a thread is executing in an actor other than its home
actor (when executing a system call or a lap handler), the time it consumes is
charged to its home actor and not to the execution actor.
Once the designated thread or threads have consumed *cpuTimeLimit of ad-
ditional execution time (from the time the timer was initialized), the handler
is entered with vTimeout as its sole argument. The handler is executed by the
controlled thread if the timeout is thread-dedicated or by the next executing
one if it is actor-dedicated. This execution is not performed at interrupt level
12.4. The VTIMER feature 335
which means that there is no restriction on the kernel's services that may be
invoked in the handler. If a timeout occurs while the thread is not executing in
its home actor (during cross-actor invocation), the corresponding handler will
be executed when the thread returns to its home actor.
A call will return K OK if successful, or a negative error value:
value error type
K EINVAL no handler has been specied or the value of
cpuTimeLimit is not valid or
ag is not valid
or threadLi has an incorrect value
K EUNKNOWN actor cannot be reached
Once the thread identied by threadLi in the actor whose capability is given by
actorCap has consumed the CPU time specied by cpuTimeLimit, the lap han-
dler designated by lapDesc will be called with vTimeout as a specic argument.
As for the other primitives,
ag may be K VTIME INTERNAL or K VTIME TOTAL.
The handler will not be invoked at interrupt level but once the thread executes
in its home actor; thus, during cross-actor invocation, execution of virtual timer
handlers is delayed.
The call normally returns K OK if successful, or a negative value (K EINVAL or
K EUNKNOWN) if an error occurred.
struct entry{
int *flag;
KnVirtTimeout vTimeout; } vTimeoutEntry[10];
int one = 1;
handler(KnVirtTimeout *vtimeout) {
int ind = 0;
while(&vTimeoutEntry[ind].vTimeout != vtimeout)
ind ++;
svCopyOut(&one, (VmAddr) vTimeoutEntry[ind].flag, sizeof(int));
vTimeoutEntry[ind].flag = NULL;
return;
}
main( ) {
KnLapDesc lapDesc;
KnCap actorCap;
actorSelf(&actorCap);
srand(actorCap.key.keyHead + actorCap.key.keyTail);
if (lapInstall((KnLapHdl) setTimer, "VTIME", "VTIME", 0, &lapDesc) != K_OK)
exit(1);
threadDelete(K_MYACTOR, K_MYSELF);
}
-->
We intend to install that service. We rst generate a system where the VTIMER
feature is set (within the standard congurations it is not):
--> cd $BUILD_DIR
--> configurator -set VTIMER=true
--> make chorus
..............
Image file: /home/jmr/CHORUS/r4/build/chorus.bmon
Finish mkimage
-->
12.4. The VTIMER feature 345
After booting this new conguration on the neon machine, we install the service
we developed:
--> rsh neon arun cs -lM
started aid = 2
ChorusOS r4.0.0 Site 0 Time 48
.. SCHED_FIFO ... MEM_PRM ... LAPSAFE VTIMER ..... LAPSAFE .....
--> neon-n timeHdlActor_s.r &
[1] 11206
--> started aid = 2
The dierent functions of the object le whose source is given below are just
stubs that perform the right lap invocations:
--> cat $CHORUS/sources/time/timeHdlActor/lib/vTimeoutLib.c
#include "vTimeout.h"
KnLapDesc lapDesc;
int result;
int actorTimerSet(int milliSec) {
result = lapResolve(&lapDesc, "A_SET", K_LAP_NOBLOCK);
if(result != K_OK)
return result;
result = lapInvoke(&lapDesc, (void *) milliSec);
return result;
}
int threadTimerCancel( ) {
result = lapResolve(&lapDesc, "T_CANC", K_LAP_NOBLOCK);
if(result != K_OK)
return result;
result = lapInvoke(&lapDesc, NULL);
return result;
}
12.4.4.5 Execution
a) The rst client consists of a single thread actor which uses the library func-
tions to interrupt a long loop by setting an actor's dedicated virtual timeout:
--> cat $CHORUS/sources/time/timeHdlActor/client1/monoThreadClient.c
#include "vTimeout.h"
main(int argc, char *argv[ ]) {
int milliSec, result, ind, x;
KnCap actorCap;
actorSelf(&actorCap);
fprintf(stderr, "thread %d ", threadSelf( ));
fprintCap(stderr, "in actor", &actorCap);
milliSec = atoi(argv[1]);
result = actorTimerSet(milliSec);
if(result != K_OK) {
fprintf(stderr, "actorTimerSet: %s\n", strSysError(result));
exit(1);
}
for(ind = 0; ind < 50000000; ind ++) x = 1; /* A LONG LOOP */
fprintf(stderr, "after the loop\n");
result = actorTimerCancel( );
if(result != K_OK) {
fprintf(stderr, "actorTimerCancel: %s\n", strSysError(result));
exit(1);
}
}
In the second execution, the delay is now 1/10 second. This delay is not su-
cient and when the virtual timeout expires, the actor is deleted before exiting
its long loop:
-1-> neon monoThreadClient_u 500
started aid = 22
thread 9 in actor: 2000001c 869da80a 16 57
*** setting a timer by actor: 2000001c 869da80a 16 57
*** timeout has expired for actor: 2000001c 869da80a 16 57
-->
void loop( ) {
int ind;
if(threadSelf( ) == timedThread)
threadTimerSet(milliSec);
for(ind = 0; ind < 500000000; ind ++);
fprintf(stderr, "Thread %d after the loop\n", threadSelf( ));
if(threadSelf( ) == timedThread)
threadTimerCancel( );
threadDelete(K_MYACTOR, K_MYSELF);
}
Once the system is booted on the neon machine, we start the server:
--> rsh neon arun cs -lM
started aid = 2
ChorusOS r4.0.0 Site 0 Time 1m 8
.... SCHED_CLASS [ FIFO RR RT ] ... MEM_PRM ... VTIMER ....
--> neon timeHdlActor_s.r &
[1] 12010
-->
causes the current thread to be suspended from execution for the period speci-
ed by the object pointed to by rqtp. The value specied by *rmtp is rounded
to a multiple of the system real-time clock. When returning due to completion
of the wait the call returns 0. If any error occurs, it returns -1 and the errno
variable indicates the error (EFAULT if a pointer is not valid, EINVAL if *rqtp is
invalid or rqtp is NULL, EINTR if the calling thread was aborted). In this last
case, if rmtp is not NULL, the time remaining before the call would have nor-
mally terminated is stored at that location.
12.5. Posix interface 351
A call to the function
#include <time.h>
int clock gettime
clockid t clock id,
struct timespec *tp
);
gets the current value of the clock identied by clock id and stores this value at
the address specied by tp.
When returning from the call, the identier of the new timer is returned at the
address timer id and the timer is disarmed.
The parameter evp must point to an initialized object allocated by the user
with the sigevent structure dened as:
352 Chapter 12. Time management
#include <signal.h>
struct sigevent{
int sigev_notify; /* SIGEV_THREAD */
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
};
union sigval{
int sival_int;
void *sival_ptr;
};
The sigev notify function eld points to the user-dened function which will
be called asynchronously at expiration of the timer. This call is performed by
a thread which is dynamically created at the creation of the timer.
12.5.3.3 Deleting a Posix timer
This operation is performed by a call to the function
#include <time.h>
int timer delete(timer t timer id);
The eect of this call is to disarm and delete the timer identied by timer id.
The handler thread associated to the timer is deleted (if it is actually running,
its execution will nish before deletion).
The function
#include <time.h>
int timer settime(
timer t timer id,
int
ags,
const struct itimerspec *new value,
const struct itimerspec *old value
);
12.5. Posix interface 353
allows the timer identied by timer id to be armed, reset or disarmed according
to the following:
if the it value eld of new value is zero, the timer is disarmed;
if the it value eld of new value is not zero, the timer is armed and the time
of the next expiration is relative to the current time if the TIMER ABSTIME
ag
is not set in the
ags parameter. Otherwise, the time is an absolute time;
if the timer is armed and if the it interval eld of new value is not zero, a
periodic timer is specied. The value of the eld denes the period;
if old value is not NULL, the time remaining before expiration of the previous
timeout will be stored at this address.
The timer gettime function may be used to get the amount of time before
expiration of a given timer:
#include <time.h>
int timer gettime(
timer t timer id,
const struct itimerspec *value
);
Chapter 13
Communications
13.1 Introduction
There are a number of features related to communications services:
MIPC: allows ecient communication between threads of dierent actors which
make up an application through message spaces (mailboxes (mipc));
IPC: threads that do not share memory may communicate. It allows asyn-
chronous or synchronous communication and exports the following abstractions:
- messages which are units of communication;
- ports which are point-to-point communication endpoints;
- groups of ports which allow multi-cast communication.
Within the optional IPC REMOTE feature, the services provided by the IPC fea-
ture are available in a distributed, transparent way. Thus, distributed applica-
tions on dierent sites may communicate as if they were located on the same
one;
within the ACTOR EXTENDED MNGT feature, extended actors may take advan-
tage of the entire C library for dealing with input/output operations compatible
with the Posix standard 1003.1b-1993;
POSIX MQ: provides an implementation of the real-time message queue inter-
face as dened by POSIX 1003.1b;
POSIX SHM provides an implementation of the real-time shared memory inter-
face as dened by the Posix standard 1003.1b-1993. We already presented this
feature in 7.8;
POSIX SOCKETS: provides a Posix interface for sockets' system calls (1003.1g
standard). This service is limited to the AF INET domain but may be extended
to the local domain within AF LOCAL.
355
356 Chapter 13. Communications
13.2 Mailboxes
13.2.1 Introduction
With the MIPC feature, an application composed of one or several actors can
create a shared communication environment which allows messages to be ex-
changed between threads in a very ecient way without copying them.
The MIPC feature is based on the concept of message space which encapsulates
within a single entity:
a set of message pools: these are shared by all the actors of the application.
The various messages exchanged through a message space will be allocated in
one pool. Dierent pools may exist in a given message space (between 1 and
K MSG POOLMAX [16]) and each one is identied inside that message space by an
index in the range between 0 and N 1, where N is the number of message
pools in the message space. The main characteristic of a message pool is the
common length of the messages it contains.
A message pool is dened by two integers which correspond to the elds of the
KnMsgPool structure:
- msgSize denes the size of each message in the pool;
- msgNumber denes how many messages belong to the pool;
a set of message queues: these are used by actors to exchange messages
allocated from the message pools. Messages extracted from message pools and
posted by a thread will be inserted in one queue. A given queue of a message
space may contain messages allocated from dierent pools belonging to that
message space. A message queue is identied in a message space by its index
in the range between 0 and M 1, where M is the number of message queues
in the message space.
A message space must rst be created by a thread by calling msgSpaceCreate,
and must be opened by other actors wanting to use it by calling msgSpaceOpen;
these actors are said to share this message space.
A message space is bound to the actor which created it and it is automatically
destroyed when this actor and all actors having opened it are themselves de-
stroyed.
An actor may use (create or open) more than one message queue. However,
there is no way to post a message from a message space (say A) to a queue of
another message space (say B).
13.2.2 Creating a message space
The creation of a message space is performed by a call to the primitive
13.2. Mailboxes 357
#include <mipc/chMipc.h>
int msgSpaceCreate(
KnMsgSpaceId spaceGid,
unsigned int msgQueueNb,
unsigned int msgPoolNb,
const KnMsgPool *msgPools
);
where
the value spaceGid is chosen by the creator of the message pool space and
constitutes a key which globally identies the message pool. It will be used in
other actors for opening the message pool and it is currently an integer value.
The value K PRIVATEID corresponds to a private key for the actor (the same
way IPC PRIVATE is used as a private key for System V IPC in UNIX systems);
msgPoolNb and msgQueueNb give, respectively, the number of message pools
and the number of message queues of the message space to be created;
msgPools should be an array of msgPoolNb descriptors of message pools.
When returning from a successful call, the message space is created and
the msgPoolNb pools of messages are created; the message pool whose index
is i contains msgPools[i].msgNumber buers. The size of each buer of that
pool is equal to msgPools[i].msgSize;
msgQueueNb empty queues are dened.
The value returned by a successful call is a local identier which will be used
in the actor to manipulate queues and messages in the pool.
The next diagram shows the contents of the message space after its creation:
A message space after it has been created by the call
msgSpaceCreate( spaceGid, msgQueueNb, msgPoolNb, msgPools)
Upon success, the value returned by the call is a local identier which will be
used to manipulate queues and messages in the pool. This local identier may
be used by any thread within the actor. Only one thread of an actor needs to
perform the open operation. Local identiers are not inherited through actor's
creation (actorCreate, afexec, . . . ).
A negative value is returned when an error occurs:
return value interpretation
K EINVAL spaceGid is not a correct identier
K EOVERLAP insucient space in address space
K ENOMEM system out of resources
Not msgRemove
Allocated Queued
allocated
msgAllocate msgPut (send)
the rst message, if any, with the highest priority pending behind the message
queue with index queueIndex, in the message space whose local identier is
spaceLid, is extracted from that message queue. No data is copied but the
address in the address space of the thread's actor is returned through the pointer
msgAddr.
If srcActor is not NULL and the call succeeds, it will contain the source actor's
unique identier upon return, only the ui eld of its capability is given (the
key eld is secret).
If the message queue is empty, the calling thread is normally blocked except if
a timeout has been specied through the waitLimit parameter.
A successful call returns K OK, or one of the following negative values otherwise:
return value interpretation
K EINVAL spaceLid or waitLimit are invalid
K EARGS msgQueueId is not valid
K EFAULT wrong address for msgAddr
K ETIMEOUT timeout expired
K EABORT invoking thread was aborted while waiting
The main thread of a rst actor will create the message space and will post
various messages which will be passed as arguments of the command (a string,
an index of the message queue which will be 1 or 2 and a priority will be given
for every message). A message will be allocated either in the pool of small
messages or in the pool of large messages depending on its size. When it has
posted all its messages, the thread will get a message from the reserved queue
with an index of 0, this message will be posted as a tiny message (extracted
from the corresponding pool) by the rst thread which tries to get a message
into the message space. This ensures that when the main thread terminates, at
least one other thread will be using the message space and thus, the message
space will not be deleted.
--> cat $CHORUS/sources/ipc/msgQueue/msgQueue1/msgQueue1.c
/* the command is called with arguments
message_1 queue_1 priority_1 ... message_n queue_n priority_n */
#include "msgQueue.h"
KnMsgPool msgPool[NB_MSG_POOLS]; /* array of messages pools */
KnCap srcActorCap, actorCap;
actorSelf(&actorCap);
fprintCap(stderr, "actor", &actorCap);
364 Chapter 13. Communications
/* the message space is created */
msgPool[LARGE_POOL].msgSize = LARGE_MSG_SZ;
msgPool[LARGE_POOL].msgNumber = NB_LARGE_MSG;
msgPool[SMALL_POOL].msgSize = SMALL_MSG_SZ;
msgPool[SMALL_POOL].msgNumber = NB_SMALL_MSG;
msgPool[TINY_POOL].msgSize = TINY_MSG_SZ;
msgPool[TINY_POOL].msgNumber = NB_TINY_MSG;
msgSpaceLi = msgSpaceCreate(MESSAGE_SPACE, NB_MSG_QUEUES,
NB_MSG_POOLS, msgPool);
if (msgSpaceLi < 0) {
fprintf(stderr, "error on msgSpaceCreate: %s\n",
strSysError(msgSpaceLi));
exit(2);
}
/* every message passed as argument is posted */
for(ind = 1; ind < argc; ind += 3) {
queueIndex = atoi(argv[ind + 1]);
if (queueIndex == 0) {
fprintf(stderr, "queue 0 is reserved\n");
continue;
}
msgPriority = atoi(argv[ind + 2]);
/* a message is allocated in the pool fitting its length */
msgLength = strlen(argv[ind]) + 1; /* + 1 because of null character */
poolIndex = (msgLength <= SMALL_MSG_SZ) ?SMALL_POOL : LARGE_POOL;
result = msgAllocate(msgSpaceLi, poolIndex, msgLength,
K_NOTIMEOUT, &msgAddr);
if(result != K_OK) {
fprintf(stderr, "error on msgAllocate: %s\n", strSysError(result));
exit(2);
}
/* the allocated message is initialized */
strcpy(msgAddr, argv[ind], msgLength);
/* the message is now posted in the right queue */
/* with the given priority */
result = msgPut(msgSpaceLi, queueIndex, msgAddr, msgPriority);
if(result != K_OK) {
fprintf(stderr, "msgPut(%s): %s\n", msgAddr,
strSysError(result));
msgFree(msgSpaceLi, msgAddr);
}
else {
fprintf(stderr, "%s ", (poolIndex == SMALL_POOL)?"small" : "large");
fprintf(stderr,
"message %s has been posted in queue %d with prio %d\n",
msgAddr, queueIndex, msgPriority);
}
}
13.2. Mailboxes 365
/* the thread waits until a client arrives and
posts a message in queue 0 */
result = msgGet(msgSpaceLi, 0, K_NOTIMEOUT, &msgAddr, &srcActorCap.ui);
if (result != K_OK) {
fprintf(stderr, "error on msgGet: %s\n", strSysError(result));
exit(2); }
fprintCap(stderr, "TINY MESSAGE RECEIVED FROM ACTOR", &srcActorCap);
msgFree(msgSpaceLi, msgAddr);
}
In the following execution, we can observe that the main thread (we call it th0)
is blocked after having allocated 4 messages from the pool of small messages
and one from the pool of large messages when it tries to allocate a fth message
in the pool of small messages:
-1-> rsh neon arun cs -lM (=
check if MIPC feature is active
started aid = 23
ChorusOS r4.0.0 Site 0 Time 1h 5m 32
............................... MIPC ............
-1-> neon-n msgQueue1_u aaaaa 1 2 bbbbbbbbbbb 2 3 ccccc 0 2 dddd 2 2 \
eeeee 1 3 ffff 4 1 ggggggg 2 3 hhhhhh 1 1 iiiiiiiiiiiiiiii 2 3&
started aid = 23
actor: 20000038 869da80a 17 1
small message aaaaa has been posted in queue 1 with prio 2
large message bbbbbbbbbbb has been posted in queue 2 with prio 3
queue 0 is reserved
small message dddd has been posted in queue 2 with prio 2
small message eeeee has been posted in queue 1 with prio 3
msgPut(ffff): Arguments inconsistency
small message ggggggg has been posted in queue 2 with prio 3
At that point, the two queues have the following contents that have been ordered
according to the priorities of the messages and the order in which they were
inserted (for each message we have noted its priority and the pool to which it
was allocated):
queue 1: eeeee [3] (S), aaaaa [2] (S)
queue 2: bbbbbbbbbbb [3] (L), ggggggg [3] (S), dddd [2] (S)
We now give the code of an application which extracts messages from queues
identied by their index that are passed as arguments. The main thread of the
actor rst tries to allocate a message in the pool of tiny messages, this allocation
is done in non blocking mode. Thus, if another thread arrived earlier, there is no
longer a tiny message, the thread is not blocked and can continue. After having
received a message, the message is freed 10 seconds later (this delay simulates
a "long" processing and a side eect is that the thread yields the processor).
366 Chapter 13. Communications
Thus the thread th0 that posts messages in our example is unblocked and, when
it is scheduled, can post and allocate more messages.
--> cat $CHORUS/sources/ipc/msgQueue/msgQueue2/msgQueue2.c
/* the command is called with arguments queue_1 ... queue_n */
#include "msgQueue.h"
KnCap srcActorCap, actorCap;
int ind, result;
unsigned int queueIndex;
char *msgAddr;
int msgSpaceLi;
KnTimeVal delay;
We now create two c actors for this application whose main threads are des-
ignated by th1 and th2. The information displayed by the threads shows how
messages are extracted from the queues and how a blocked thread is awoken
when messages are freed:
-2-> neon-n msgQueue2_u 1 2 2 & neon-n msgQueue2_u 2 1 2 1 &
[1] 5492
[2] 5493
-2-> started aid = 22
actor: 20000039 869da80a 16 1
TINY HAS BEEN POSTED BY: 20000039 869da80a 16 1
message eeeee from queue 1 by: 20000039 869da80a 16 1
and sent by: 20000038 869da80a 0 0
At that point, th1 has posted the tiny message, has read the rst message in
queue 1 and has called threadDelay; thus, it yields the processor and thread
th2 is activated:
started aid = 24
actor: 2000003a 869da80a 18 1bc
someone arrived before: IPC time-out
message bbbbbbbbbbb from queue 2 by: 2000003a 869da80a 18 1bc
and sent by: 20000038 869da80a 0 0
Thread th2 rst tries to post a tiny message; there are no more messages in
the corresponding pool. As the allocation's request is non blocking, the thread
gets an error return value (K ETIMEOUT) and gets the rst message from queue
2. The thread calls threadDelay. The three threads are sleeping (thread th0
is not awoken since no message has been freed). Thread th1 is awoken rst and
frees the message it extracted from the queue:
message eeeee is freed
Thread th0 is awoken, posts its last two messages, reads the tiny message and
terminates:
368 Chapter 13. Communications
small message hhhhhh has been posted in queue 1 with prio 1
large message iiiiiiiiiiiiiiii has been posted in queue 2 with prio 3
TINY MESSAGE RECEIVED FROM ACTOR: 20000039 869da80a 0 0
-1->
d) the last step before building an image consists of selecting a site number.
Within Chorus IPC every node has a unique site number which is a 32 bits
unsigned integer. The site number is part of the unique identier of an ob-
ject created on that site (it corresponds to the tail of that unique identier).
This number is provided to the kernel at boot time and is the value of the
chorusSiteId tunable parameter whose default value is 0. When the target is
booted with the standard Chorus boot monitor, as we did, the site number of
the target is the whole IP address which is retrieved when booting the system
within the RARP protocol. A site number may also be provided by setting the
chorusSiteId tunable parameter as in
--> configurator -set chorusSiteId=3
-->
370 Chapter 13. Communications
It is worth noting that if a static denition of site numbers is adopted, as many
system images as target machines must be built, each one must dene a specic
site number (unlike a dynamic computation of the site number where the same
image may be used for dierent targets). Furthermore, a static value of the
site number takes precedence over the value dynamically provided by the boot
program, if any.
In any case, if no site number is provided (either statically or dynami-
cally, that is, if chorusSiteId is 0) the remote IPC will not be started
even if the feature is congured.
e) once the system has been booted within the IOM IPC feature and an IP ad-
dress has been assigned to the target, the IOM actor includes a module which
will act as a driver for use by the remote IPC and needs to be attached to an
Ethernet device recognized by the IOM. This operation is performed by using
the ethIpcStackAttach built-in command of C INIT called as
ethIpcStackAttach [ethernet device name]
where ethernet device name is the full pathname of the Ethernet device in the
target device tree which is displayed on the console at boot time. This pathname
needs to be provided only if the target owns several Ethernet controllers.
We summarize below the sequence of operations to boot, on neon and carbon
ix86 targets, a ChorusOS image allowing remote IPC between entities executing
on these systems (we will use the site number provided at boot time and the
same image will be loaded on both systems):
--> cd $BUILD_DIR
--> configurator -set IPC=true
--> configurator -set IPC_REMOTE=true
--> configurator -set IOM_IPC=true
--> make chorus
...............
Start mkimage
...............
Image file: /home/jmr/CHORUS/r4/build/chorus.bmon
Finish mkimage
--> su
Password:
# cd /tftpboot
# cp $BUILD_DIR/chorus.bmon 869DA806.ChorusOS.4.0
# ln 869DA806.ChorusOS.4.0 869DA80a.ChorusOS.4.0
# exit
--> rsh neon reboot
shutdown in progress ..
-->
13.3. The ChorusOS IPC 371
console neon !
My IP 134.157.168.10, RARP Server IP 134.157.168.9
Loading file 869DA80A.ChorusOS.4.0 on server 134.157.168.9: loaded!
............................
C_INIT: rshd started
134.157.168.9:/home/jmr/CHORUS/r4/build/root/ on / (nfs)
C_INIT: init in unsecured mode
--> rsh neon ethIpcStackAttach
ipcEthDtLink Init OK
localIpAddr: 134.157.168.10
bcastIpAddr: 134.157.255.255
13.3.2 Ports
13.3.2.1 Overview
Ports are resources belonging to an actor which are shared by all threads of the
actor to send and receive messages. At any given time, a port belongs to one
and only one actor. Each actor has a default port created at its creation time.
Furthermore, except for the default port of an actor, a port can migrate (its
owning actor changes).
Messages are always sent from one port to another. A queue of messages is
attached to every port containing all the messages sent to the port and not
yet consumed. This queue can contain at most K CPORTQUEUE messages (it is a
tunable parameter whose default value is 32).
Given the identication of a destination port, if the remote communication
feature is enabled, the port localization procedure is the following:
the kernel looks for the information in a cache where the last localizations
were registered;
the kernel uses a few hints, such as nding the port on its creation site;
nally the kernels sends a broadcast localization request.
The use of ports to exchange messages between threads is illustrated by the
372 Chapter 13. Communications
following diagram. Each thread of an actor may use any port belonging to its
owning actor to send messages to another port:
supervisor actor:
it owns 1 port
and no thread
at present
writes, at the portUid address, the unique identier of the port and returns
K OK. In case of failure a negative error code is returned: K EINVAL if portLid
is not a valid identier within the current actor or K EFAULT if an address is
outside the current actor's address space.
Conversely, a call to
#include <ipc/chIpc.h>
KnPortLid portLi(KnUniqueId *portUid);
returns the local identier of the port whose unique identier is pointed to by
portUid (the port must be owned by the current actor). In case of failure a
13.3. The ChorusOS IPC 373
negative error value is returned: K EINVAL if portUid is not the unique identier
of a port owned by the current actor or K EFAULT.
Here is a simple example using these functions for the default port of an actor:
-2-> cat $CHORUS/sources/ipc/portUi/portUi.c
#include <chorus.h>
KnTimeVal delay;
KnUniqueId portUid;
KnPortLid localId;
main( ) {
portUi(&portUid, K_DEFAULTPORT);
printf("uiHead = %x uiTail = %x\n",
portUid.uiHead, portUid.uiTail);
localId = portLi(&portUid);
printf("K_DEFAULTPORT = %x Local identifier = %x\n",
K_DEFAULTPORT, localId);
K_MILLI_TO_TIMEVAL(&delay, 30000);
threadDelay(&delay); /* sleep for 30 seconds */
}
-2-> neon-n portUi_u &
[1] 5563
-2-> started aid = 24
uiHead = 2000003d uiTail = 869da80a
K_DEFAULTPORT = def0 Local identifier = def0
While the main thread is sleeping, the cs command is used to get information
about the actor:
-1-> rsh neon arun cs -la 11
started aid = 24
ChorusOS r4.0.0 Site 0 Time 3h 3m 20
ACTOR-UI KEY LID TYPE STATUS TH# NAME
2000003e 869da80a 00000018 000001e3 0024 SUP STARTED 001 cs
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
0007 140 00000010 00000010 ff6390 0- 0- 0 main
PORT-UI PORT-LI ENABLED CTX MSG# ACTOR-UI
2000003e 869da80a def no ea6e44 0 2000003e 869da80a
START SIZE OFFSET ALLOC OPTIONS
7bbd1000 00004000 00000000 00004000 WR SU
7bbd5000 00010000 00000000 00010000 EX SU
7bbeb000 00002000 00000000 00002000 WR SU
-1->
-2->
puts the port with local identier portLid in the actor whose capability is pointed
to by actorCap into the ENABLED state. The priority parameter denes the
priority of the messages received on the port (a low value indicates a high
priority) with regard to messages received on another "ENABLED" port.
When calling the function
#include <ipc/chIpc.h>
int portDisable(
KnCap *actorCap,
KnPortLid portLid
);
the port that has portLid as its local identier in the actor whose capability is
pointed to by actorCap enters the DISABLED state.
These two functions return K OK upon success. A negative error code is returned
in case of failure: K EINVAL if portLid is not a valid port within the actor,
K UNKNOWN if actorCap does not correspond to a reachable actor or K EFAULT.
or
13.3. The ChorusOS IPC 375
#include <ipc/chIpc.h>
int portDeclare(
KnCap *actorCap,
KnUniqueId *portUid
);
This call corresponds to a request to delete the port whose local identier is
portLid in the actor whose capability is pointed to by actorCap.
A side eect of port deletion is to delete all the messages currently in the
queue associated to that port. Furthermore, all threads blocked in a receive call
(ipcReceive) are awoken and the corresponding calls return the K ENOPORT
value.
Finally, all current RPC transactions (corresponding to calls to ipcCall) are
canceled ed. The K OK value is returned when the call succeeds, or a negative
error value K EINVAL, K EUNKNOWN or K EFAULT otherwise.
is a request to insert the port with the unique identier pointed to by portUid
into the group whose capability is pointed to by portGroupCap.
The function returns K OK when successful, or one of the following negative error
codes:
13.3. The ChorusOS IPC 379
return value interpretation
K EINVAL incorrect group capability
K EUNKNOWN incorrect port unique identier
K EFAULT address out of address space
K ENOMEM system out of resources
K EPRIV unauthorized threads for system groups
The next sequence follows the two preceding sequences and is a request to insert
the port we created into the group whose capability was just allocated:.
portLid = portCreate(K_MYACTOR, &portUid);
groupLid = grpAllocate(K_STATUSER, &groupCap, STAMP);
if((result = grpPortInsert(&groupCap, &portUid) < 0 )) {
fprintf(stderr, "error on grpPortInsert: %s\n", strSysError(result));
actorDelete(K_MYACTOR); }
#include <stdio.h>
#include <chorus.h>
#include <ipc/chIpc.h>
int result;
char annex[K_CMSGANNEXSIZE];
KnCap actorCap;
384 Chapter 13. Communications
KnUniqueId portUid;
KnIpcDest dest;
KnMsgDesc sndMsg;
13.3.6.5 Example
We now develop a server whose architecture corresponds to the following dia-
gram:
portUid : port's global unique identier
portLid : port's local identier
Every thread
in the actor request request request
has its own
request
requestBody
reply
on its stack requestBody requestBody requestBody
b) The server
An echo server creates a new port that it inserts in the static user port group
corresponding to the ECHO STAMP value selected. Then it enters an innite loop
where it waits for messages. Each time a message arrives, it echoes it on its
standard error output and sends it back to the client:
--> cat $CHORUS/sources/ipc/service/server/server.c
#include "service.h"
#define N_THREADS 2 /* number of annex threads */
KnUniqueId portUid; /* port's unique identifier */
KnPortLid portLid; /* port's local identifier */
KnCap groupCap ; /* port group capability */
int groupLid; /* port group local identifier */
KnMutex mutex = K_KNMUTEX_INITIALIZER; /* to synchronize executions */
char replyBody[BODY_SIZE] = "Initial value"; /* shared by all threads */
void echoService( ) {
int result; /* return values */
char requestBody[BODY_SIZE];
KnMsgDesc request, /* received message's descriptor */
reply; /* reply message's descriptor */
388 Chapter 13. Communications
/* Prepare the message descriptor for ipcReceive */
request.bodyAddr = (VmAddr) requestBody;
request.bodySize = BODY_SIZE; /* size of allocated space for message */
request.annexAddr = NULL; /* no annex in the message */
/* Prepare the message descriptor for ipcReply */
reply.bodyAddr = (VmAddr) replyBody;
reply.bodySize = BODY_SIZE; /* size of allocated space for message */
reply.annexAddr = NULL; /* no annex in the message */
/* enter the service loop */
while(1) {
bzero(requestBody, BODY_SIZE); /* the buffer is filled with 0 */
/* read a message from the port */
result = ipcReceive(&request, &portLid, -1);
if(result < 0) {
fprintf(stderr, "++ thread %d ++: error on ipcReceive: %s\n",
threadSelf( ), strSysError(result));
continue;
}
fprintf(stderr, "++ thread %d has received a message\n", threadSelf( ));
fprintf(stderr, " size = %d\n", result);
fprintf(stderr, " body: %s\n", request.bodyAddr);
mutexGet(&mutex);
/* the message is sent back as a reply */
if((result = ipcReply(&reply)) < 0) {
fprintf(stderr, "error on ipcReply: %s\n", strSysError(result));
continue;
}
bcopy(requestBody, replyBody, BODY_SIZE);
mutexRel(&mutex);
}
}
main( ) {
int ind, result;
/* create a new port in the actor */
if((portLid = portCreate(K_MYACTOR, &portUid)) < 0 ) {
fprintf(stderr, "error on portCreate: %s\n",
strSysError(portLid));
actorDelete(K_MYACTOR);
}
/* Allocate a port group's capability */
if((groupLid = grpAllocate(K_STATUSER, &groupCap, ECHO_STAMP)) < 0) {
fprintf(stderr, "error on grpAllocate: %s\n",
strSysError(groupLid));
actorDelete(K_MYACTOR);
}
13.3. The ChorusOS IPC 389
/* insert the port in the group */
if((result = grpPortInsert(&groupCap, &portUid) < 0 )) {
fprintf(stderr, "error on grpPortInsert: %s\n", strSysError(result));
actorDelete(K_MYACTOR);
}
fprintf(stderr, "main thread is %d\n", threadSelf( ));
/* create N_THREADS new threads */
for(ind = 0; ind < N_THREADS; ind ++) {
result = newThread((KnPc) echoService, K_ACTIVE, "echoTh");
fprintf(stderr, "thread %d is created\n", result);
}
echoService( ); /* the main thread participates to the service */
}
c) An asynchronous client
In the following code, the client sends a message by calling ipcSend. The
corresponding command has two parameters:
- the rst one is the message that will be sent;
- the second one denes the mode used for sending the message: if the rst
character is b the message is send in broadcast mode, otherwise it is sent in
functional mode.
--> cat $CHORUS/sources/ipc/service/client1/ipcClient1.c
#include "service.h"
KnCap groupCap; /* port group's capability */
KnPortLid portLid; /* port' local identifier */
int groupLid; /* group' local identifier */
int result; /* return values */
KnMsgDesc sndMsg; /* message descriptor for send */
KnMsgDesc rcvMsg; /* message descriptor for receive */
KnIpcDest destination; /* destination of message */
char message[BODY_SIZE]; /* buffer for receiving reply's body */
int mode; /* sending mode */
d) Execution
We rst try to execute the various clients while no server is available. The
rst is performed on a system where the IPC REMOTE feature is not active:
--> rsh neon arun cs -lM
started aid = 24
ChorusOS r4.0.0 Site 0 Time 4h 7m 32
............ IPC_L ............ (= local ipc
--> neon ipcClient1_u ABCDEFGHIJK f
started aid = 24
error on ipcSend: Unreachable destination
--> neon ipcClient1_u ABCDEFGHIJK b
started aid = 24
error on ipcSend: Unreachable destination
-->
In this call
394 Chapter 13. Communications
srcPortLi is the local identier of the source port for sending the message and
receiving the answer. As usual, K DEFAULTPORT can be used;
delay is a timeout for the operation and is expressed as a number of milli-
seconds;
requestMsgDesc points to the structure describing the request message to be
sent;
requestDest points to the structure describing the destination of the message.
The various elds of the structure play the same role as their corresponding
arguments of the ipcSend function. It is important to note that K BROADMODE
addressing is forbidden for RPC;
responseMsgDesc points to the structure describing the memory block where
the answer should be received. The elds are interpreted in the same way as the
elds of the corresponding arguments of the ipcReceive primitive. It is impor-
tant to note that the message received as answer does not become the current
message for the thread. This means that ipcReply, ipcSave, ipcGetData and
ipcSysInfo cannot be applied to the reply to the call.
The following negative values may be returned when an error occurs:
return value error type
K EABORT calling thread was aborted and message could not reach destination
K BADMODE broadcast mode forbidden for RPC
K EFULL communication system is saturated or destination queue is full
K EINVAL incorrect port local identier
K EUNKNOWN incorrect group port identier
K EFAULT address out of address space
K TOOMUCH body size too large
K ETIMEOUT timeout occurred
We now give the source code of a command for initiating an rpc transaction
towards a given destination port. The corresponding command will be passed
the unique identier of the destination port as rst and second arguments,
the annex of the message to send as third argument and its body as the last
argument:
--> cat $CHORUS/sources/ipc/msgCall/msgCall.c
#include <stdio.h>
#include <chorus.h>
#include <ipc/chIpc.h>
KnCap actorCap;
KnUniqueId portUid;
KnIpcDest dest;
KnMsgDesc sndMsg, rcvMsg;
char annex[K_CMSGANNEXSIZE], char buffer[1024];
13.3. The ChorusOS IPC 395
main(int argc, char *argv[ ]) {
int result;
printf("Local identifier of thread calling ipcSend: %d\n", threadSelf( ));
sscanf(argv[1], "%x", &result);
portUid.uiHead = result;
sscanf(argv[2], "%x", &result);
portUid.uiTail = result;
/* preparing destination descriptor */
dest.target = portUid;
/* preparing message descriptors */
/* bzero(annex, K_CMSGANNEXSIZE) */;
strcpy(annex, argv[3]);
sndMsg.annexAddr = (VmAddr) annex;
sndMsg.bodyAddr = (VmAddr) argv[4];
sndMsg.bodySize = strlen(argv[4]) + 1;
rcvMsg.flags = 0;
rcvMsg.annexAddr = (VmAddr) annex;
rcvMsg.bodyAddr = (VmAddr) buffer;
rcvMsg.bodySize = 1024;
result = ipcCall(&sndMsg, K_DEFAULTPORT, &dest, &rcvMsg, -1);
if(result < 0)
fprintf(stderr, "error on ipcSend: %s\n", strSysError(result));
else
fprintf(stderr, "result = %d\n", result);
}
The following traces illustrate the use of dierent functional addressing modes.
The rst sequence is similar to the rst one executed with ipcClient1 clients:
13.3. The ChorusOS IPC 397
-1-> carbon ipcServer_u
started aid = 2
main thread is 14
thread 9 is created
thread 12 is created
started aid = 2
-Client->
We retrieve the unique identiers of the ports belonging to the server on the
carbon node:
-Client-> rsh carbon arun cs -la 2
started aid = 22
................................
PORT-UI PORT-LI .....
20000016 869da806 def ........
40000001 869da806 0000 .......
................................
-Client->
We rst execute a client on neon using the associative functional mode. It
requests that its message be sent to a port belonging to the group and located
on the same node as the object whose unique identier is "20000016 869da806"
(it is the default port of the server running on carbon). Thus the message is
sent to that server:
398 Chapter 13. Communications
-Client-> neon ipcClient2_u CC u 20000016 869da806
started aid = 22
The last client is executed on carbon and uses the exclusive functional mode.
It requests its message to be sent to a member of the group whose unique
identier is dierent from "40000001 869da806" which is the port's UI of the
server running on carbon. Therefore, the message is sent to the server running
on neon:
-Client-> carbon ipcCLient2_u DD x 40000001 869da806
started aid = 22
++ thread 12 has received a message
size = 3
body: DD
*** Client has received: BBB
-Client->
The portLid argument is not actually used and the K NONEPORT value should be
used. This call returns a local identier for the saved message. This identier
will be used later to restore the message as the current message. The number of
messages that can be saved in a given actor is limited to the K CMSGSAVEDMAX
value (default is 16).
A side eect of a call to the ipcSave function is that the current message no
longer exists after it has been performed.
The call returns K OK upon success, otherwise a negative error code: K ENOMEM if
the maximum number of saved messages has been reached by the calling actor
and K EINVAL if no current message exists.
svMsgHandler
Supervisor actor
a thread of the
pool of system threads
is executing in the actor when
the calling thread is invoking
ipcSend for executing the handler
KnCap actorCap;
KnPortLid portLid;
KnUniqueId portUid;
KnMsgDesc msg;
int result;
char annex[K_CMSGANNEXSIZE];
static buffer2[1024];
Afterwards, messages are sent to the port by using the msgSnd application we
wrote in 13.3.6.2.
2 a rst message is sent from a user actor with the msgSnd u command:
-2-> neon msgSnd_u 0x40000009 0x869da80a annexM bodyM
started aid = 22
Local identifier of thread calling ipcSend: 9
-2->
As a consequence of the execution of this command, the main thread of the su-
pervisor actor is awoken and the message is displayed. The handler is connected
to the port and therefore the next call to ipcReceive fails; the -1 return value
indicates that a handler is connected to the port:
First ipcReceive: a message has been received on port 0
+++ annex: annexM
+++ body: bodyM
Handler has been connected
error on 2-nd ipcReceive: Invalid argument
2 a second message is sent from a user actor with the same application:
13.4. Message handlers 407
-2-> neon msgSnd_u 0x40000009 0x869da80a annex body
started aid = 22
Local identifier of thread calling ipcSend: 14
-2->
The message handler is invoked. The result shows that it is executed by a new
thread whose local identier is 2 and the message was sent by thread 14:
==> Entering message handler for port 0
+++ thread's local identifier: 2
+++ cookie value: cookieMessage
+++ source port: 20000038 869da80a
+++ Size of message body: 5
+++ body: body
+++ annex: annex
==> Exiting message handler
We can check that thread 2, which executed the message handler, is a thread of
the kern actor and is dedicated to that purpose (its name is IpcPortHandler):
--> rsh neon arun cs -la 1
started aid = 22
ChorusOS r4.0.0 Site 0 Time 5h 9m 42
ACTOR-UI KEY LID TYPE STATUS TH# NAME
20000001 869da80a 00000001 00000001 0001 SUP STARTED 009 kern
THREAD-LI PRIORITY TT IT CTX SC-MS-PN
[ ]
........... more lines ...................
0002 094 00000000 00000000 ff6c20 0- 1- 0 IpcPortHandler
[ ]
........... more lines ...................
-->
2 we now send a third message using the msgCall application which initiates
a RPC:
-2-> neon msgCall_u 0x40000009 0x869da80a thirdAnnex thirdBody
started aid = 22
Local identifier of thread calling ipcSend: 14
error on ipcSend: Unknown Chorus Nucleus error
-2->
As the thread initiating the rpc transaction belongs to the same site as the
destination site, the handler is executed by the thread itself (its local identier
is 14). If the call had been initiated from a dierent site, the handler would
have been executed by a system thread. Here, the message has to be explicitly
copied into the actor's address space by using svCopyIn. Furthermore, the re-
turn value of ipcCall is -1, the message handler has returned the value which
was transmitted to the calling thread:
408 Chapter 13. Communications
==> Entering message handler for port 0
+++ thread's local identifier: 14
+++ cookie value: cookieMessage
+++ source port: 2000003b 869da80a
+++ Size of message body: 10
+++ call to svCopyIn
+++ body: thirdBody
+++ call to svCopyIn
+++ annex: thirdAnnex
==> Exiting message handler
13.5 Miscellaneous
13.5.1 Getting a port sequence number
As previously mentioned, the kernel stores the count of messages that have been
received on a given port as the port sequence number. The value of this number
for a given port may be retrieved by calling the function
#include <ipc/chIpc.h>
int portGetSeqNum(
KnCap *actorCap,
int portLi,
KnEvtNum *seqNum
);
When returning from a successful call which returns the value K OK, seqNum
contains the sequence number corresponding to the port whose portLi is the
local identier in the actor whose capability is pointed to by actorCap. Error
values are K EINVAL, K EUNKNOWN or K EFAULT.
main( ) {
int loop1, loop2;
actorSelf(&actorCap);
fprintCap(stdout, "Actor's capability", &actorCap);
printf("Thread's identification: %d\n", threadSelf( ));
threadScheduler(K_MYACTOR, K_MYSELF, ¶m, NULL);
param.fifoPriority = 240;
threadScheduler(K_MYACTOR, K_MYSELF, NULL, ¶m);
printf("Hello. I'm gonna loop\n");
for(loop1 = 0; loop1 < 0x80000000; loop1 ++) {
for(loop2 = 0; loop2 < 8; loop2 ++);
if( loop1 % 0x20000000 == 0)
printf("loop1 = %x\n", loop1);
}
printf("after the loop\n");
}
-1-> neon longLoop_u
started aid = 22
Actor's capability: 2000001f 869da80a 16 0
Thread's identification: 10
416 Chapter 14. Abortions, exceptions and traps
Hello. I'm gonna loop
loop1 = 0
loop1 = 20000000
-2-> neon threadAbort_u 2000001f 869da80a 16 0 10
started aid = 23
-2->
loop1 = 40000000
loop1 = 60000000
after the loop
-1->
For a situation where abortion has no eect (it may even be not consumed), it
is possible to dene handlers that will be called automatically when a thread is
aborted. This type of abort handler will be invoked either immediately if the
aborted thread was executing in its home actor, or when the aborted thread
returns to executing in its home actor (for example, returns from a lap invoca-
tion).
A vector of K ACTOR ABORTVECT MAX abort handlers may be connected. The
handlers will be called in order until the abortion is considered as processed or
until all handlers have been called. K ACTOR ABORTVECT MAX [1] is the default
value meaning that only one abort handler may be connected. An abort han-
dler is a LAP handler whose rst parameter is a pointer to a KnActorAbortDesc
object automatically allocated and initialized by the system when an abortion
occurs. The KnActorAbortDesc type has two elds:
KnAbortStatus *abortStatus: points to a KnAbortStatus object with two
possible values
- K ABORT INPROGRESS: this value indicates that abortion has not yet been
correctly processed, and the eld is set to that value when the rst abort
handler is called;
- K ABORT COMPLETED: indicates that abortion has been correctly processed.
An abort handler can set the eld to this value to indicate that the han-
dler succeeded in processing the abortion. No other handler in the abort
handlers vector will be called;
KnThreadCtx *threadCtx: allows access to the register context of the thread
(which is machine dependent). A handler may possibly modify this context to
resolve abortion.
The second parameter of the handler is the "cookie value" which is dened
when creating the corresponding LAP.
The ABORTED state is cleared by the thread before calling the handler.
Remark If abortion occurs while a thread is blocked in an abortable call,
abortion is consumed by a return from that call. It means that the abort
14.2. Abort handlers 417
handler, if any, will not be called.
An abort handler may take any appropriate action when called such as: deleting
the aborted thread, deleting the entire actor, signaling a debugger, modifying
the thread's context so that it resumes execution elsewhere. It is executed by
the aborted thread. Supervisor actors may set abort handlers for their own
threads, user actors cannot as abort handlers must be provided by supervisor
actors. Therefore user actors need to rely on a supervisor manager.
14.2.2 Consulting the abort handlers vector
A supervisor thread may consult the vector of abort handlers currently installed
for a given actor by calling the function
#include <exec/chExec.h>
int svActorAbortHandlerGetConnected(
KnCap *actorCap,
int vectIndex,
KnLapDesc *currentHandler
);
14.2.5 Example
We develop a small utility that connects an abort handler externally for an
existing actor. The target actor's capability is passed as argument to the com-
mand. The abort handler which is connected through a lap is a very simple
one. It just displays some general information about the executing thread and
picks a random number that determines if the abortion has to be considered as
processed; in that case the thread's home actor is deleted, otherwise execution
continues until the next abortion:
14.2. Abort handlers 419
--> cat $CHORUS/sources/handlers/abort/abortHdlConnect.c
#include "utilities.h"
#include <lap/chLap.h>
We now return to the longLoop example. We create an actor for that applica-
tion and use the previous utility to install the abort handler for that actor:
-1-> neon-n longLoop_u
started aid = 23
Actor's capability: 2000003d 869da80a 17 138
Thread's identification: 9
Hello. I'm gonna loop
loop1 = 0
The newHandler argument is a LAP which has been previously created and
associated to a function (by calling svLapCreate). When an exception of any
type occurs during execution of a thread of the actor whose capability is actor-
Cap, this function will be automatically called unless another exception handler
in a position lower than vectIndex has resolved the exception (terminated after
having written K EXC COMPLETED to the right place).
Upon success, the function returns K OK, otherwise one of the following negative
values is returned:
Value Interpretation
K EINVAL inconsistent actor's capability
K EUNKNOWN actor's capability does not correspond to a local actor
K EBUSY a handler is already attached to a valid lap
Exception handlers are executed by the faulting thread itself. Upon return, the
thread resumes its execution if the exception has been resolved. Otherwise, the
Chorus nucleus deletes the thread.
424 Chapter 14. Abortions, exceptions and traps
14.3.4 Disconnecting an exception handler
A supervisor thread may disconnect a particular exception handler specied by
its position in the exception handlers vector of an actor by calling
#include <exec/chExec.h>
int svActorExcHandlerDisconnect(
KnCap *actorCap,
int vectIndex,
KnLapDesc *currentHandler
);
In this call
actorCap points to the target actor's capability;
vectIndex is the index, in the vector of the exception handler, to be discon-
nected;
if currentHandler is not K CONNECTED LAP, the LAP descriptor it points to
must be equal to the LAP descriptor installed in the exception handlers vector
at position vectIndex.
Invalid calls may return the following values:
Value Interpretation
K EINVAL inconsistent actor's capability or currentHandler is not
K CONNECTED LAP and does not match the currently installed lap
K EUNKNOWN actor's capability does not correspond to a local actor
14.3.5 Examples
We present two examples that allow resolution of memory fault exception oc-
curring during threads' execution. In the rst example, an exception handler
is externally connected to a given actor. In the second example, we develop a
service that any thread may invoke for resolving exceptions which occur in its
home actor.
14.3.5.1 Example 1
First of all, in the following example, a one page region starting at address
0xb000000 is created in the actor and the main (and only) thread of the actor
successively tries to write in all the words that follow. The thread will normally
execute while writing in the region but as soon it tries to write onto the page
following that region an exception occurs (instructions in comments will be used
later):
14.3. Exception handlers 425
--> cat $CHORUS/sources/handlers/exceptions1/actorExcept/actorExcept.c
#include "utilities.h"
KnCap actorCap;
KnTimeVal delay;
KnRgnDesc rgnDesc;
main( ) {
int result, *ptr;
rgnDesc.startAddr = 0xb0000000;
rgnDesc.size = 0x1000;
rgnDesc.options = K_WRITABLE;;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
actorSelf(&actorCap);
fprintCap(stdout, "Capability", &actorCap);
K_MILLI_TO_TIMEVAL(&delay, 30000);
threadDelay(&delay);
ptr = (int *) rgnDesc.startAddr;
printf("initial value of ptr: %p\n", ptr);
while(1) *ptr ++ = 1000;
}
--> neon actorExcept_u
started aid = 23
Capability: 20000050 869da80a 17 0
initial value of ptr: 0xb0000000
Segmentation fault thread 9 PC ffff608c faultAddr b0001000
-->
When the segmentation fault exception occurs, the faulty address is printed
(here b0001000).
We now develop an application that allows to install exception handlers exter-
nally for a given actor. The corresponding command has the following compo-
nents:
a main function executed by the main thread. The command receives as an
argument the capability of an actor to which exception handlers must be con-
nected. Two laps are created and used for connecting exception handlers. In
the code given below, before connecting a handler, we disconnect the handler
that was previously connected, if any. Two handlers will be connected (one in
position 0 and the other one in position 1). Thus, when an exception occurs
during execution of a thread of the target actor, the rst handler is called and
if it cannot solve the exception, the second one is called in turn. Once the two
handlers are installed, the main thread enters an abortable innite delay. When
it is aborted, it deletes the invoker actor; as we will see in the handlers' codes,
this occurs when exception cannot be solved (the last handler will then abort
the main thread of the actor owning the handlers).
426 Chapter 14. Abortions, exceptions and traps
--> cat $CHORUS/sources/handlers/exceptions1/server/exceptHdlConnect.c
#include "utilities.h"
/* faulty actor's capability: it will be set by an except hdl */
extern KnCap invokerCap;
void exceptHandler1(KnActorExcDesc *, char *),
exceptHandler2(KnActorExcDesc *, char *);
/* local identifier of the main thread: used by except hdl */
KnThreadLid mainThread;
the second handler simply returns when it is invoked during the rst phase
(K EXC RECOVER value of excPhase). As there is no handler any more, the sec-
ond phase (K EXC TERMINATE) is entered. The second handler is immediately
called (reverse order during this phase). The handler aborts the main thread of
the actor owning the handlers and terminates the faulty thread.
14.3. Exception handlers 429
We now give the code corresponding to that handler:
--> cat $CHORUS/sources/handlers/exceptions1/server/exceptHandler2.c
#include "utilities.h"
extern KnThreadLid mainThread;
KnCap invokerCap;
Page allocated by
rgnAllocate
client
rgnAllocate rgnAllocate rgnAllocate server
PAGE FAULT
PAGE FAULT PAGE FAULT
exception PAGE FAULT
LAP exception
LAP handler exception
ALLOC
not resolved
The server maintains a linked list of these objects and can thus terminate its
clients properly. All operations on that list will be protected by a mutex.
The most important work performed by this function is the creation of a lap for
the client, and the connection of an exception handler for the client to that lap.
When called, the exception handler will automatically receive as an argument
a pointer to a clientDesc item associated with the client.
Remark: a single lap could have been used for all clients' exception handlers
but it would have been necessary to retrieve the invoker of a call and to search
the corresponding entry in the list of clientDesc items.
432 Chapter 14. Abortions, exceptions and traps
The code of the clientHandler function is the following:
--> cat $CHORUS/sources/handlers/exceptions2/server/clientHandler.c
#include "allocate.h"
extern KnMutex mutex;
extern clientDesc *clientHead;
extern void allocateHandler(KnActorExcDesc *hdlParam, clientDesc *client);
Remark. In the current development, the server does not allow clients to leave
the automatic allocation service once it has adhered to this service. Only actors
where a thread provokes an exception which cannot be resolved are really struck
o the list of clients. A realistic service should provide this function.
b) The following client rst adheres to the service. Then, it allocates a
one page region for writing data and enters a loop where it writes further and
further into the region, provoking successive page faults which are resolved by
the exception handler. A small delay has been introduced between two write
operations: thus, we can easily execute concurrent clients. The client prints
a message each time it is going to write at the beginning of a page (that is
before provoking a PAGE FAULT exception) and each time it has written at
the beginning of a page (that is after the exception's resolution).
--> cat $CHORUS/sources/handlers/exceptions2/client/allocateClient.c
/* first argument: maximum number of calls
second argument: filling character */
#include "allocate.h"
main(int argc, char *argv[ ]) {
int result, maxCall;
char *ptr;
KnCap actorCap;
KnTimeVal delay;
KnLapDesc lapDesc;
KnRgnDesc rgnDesc;
436 Chapter 14. Abortions, exceptions and traps
if(argc != 3) {
printf("Bad usage\n");
exit(1);
}
actorSelf(&actorCap);
fprintCap(stdout, "Actor's capability", &actorCap);
/* get lap to access the service */
result = lapResolve(&lapDesc, NAME_SERVICE , K_LAP_NOBLOCK);
if(result != K_OK) {
printf("lap resolution failed: %s\n", strSysError(result));
exit(1);
}
maxCall = atoi(argv[1]);
/* invoke the service: an exception handler is connected */
result = lapInvoke(&lapDesc, &maxCall);
if (result != K_OK) {
printf("Lap invocation failed !!!!: %s\n", strSysError(result));
exit(1);
}
K_MILLI_TO_TIMEVAL(&delay, 1); /* short delay between two write */
/* allocate a first one page region at address 0xb0000000 */
rgnDesc.startAddr = 0xb0000000;
rgnDesc.options = K_WRITABLE;
rgnDesc.size = vmPageSize( );
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if (result != K_OK) {
printf("error on rgnAllocate: %s\n", strSysError(result));
/* should inform the service here of termination ..... */
exit(2);
}
ptr = (char *) rgnDesc.startAddr;
/* enter the loop for writing in the page */
while(1) {
if((int) ptr % 0x1000 == 0)
printf("before writing, ptr = %p\n", ptr);
*ptr = argv[2][0];
if((int) ptr % 0x1000 == 0)
printf("after writing, ptr = %p\n", ptr);
ptr ++;
threadDelay(&delay);
}
}
c) Execution
In the following results, the left side shows the results displayed by the server and
the right the results of the clients. Once the server has been created, we execute
two clients, the rst one writes the A character and requires the resolution of two
14.3. Exception handlers 437
PAGE FAULT exceptions, the second one writes the B character and requires
the resolution of only one PAGE FAULT exception:
-1-> neon excServer_s.r
started aid = 23
-2-> neon allocateClient_u 2 A
started aid = 24
Actor's capability: 20000057 869da80a 18 1f3
before writing, ptr = 0xb0000000
after writing, ptr = 0xb0000000
[server] New client: 20000057 869da80a 18 1f3
before writing, ptr = 0xb0000000
after writing, ptr = 0xb0000000
-3-> allocateClient_u 1 B
started aid = 25
Actor's capability: 20000058 869da80a 19 1
[server] New client: 20000058 869da80a 19 1
before writing, ptr = 0xb0000000
after writing, ptr = 0xb0000000
before writing, ptr = 0xb0001000
[server] Exception handler called from: 20000057 869da80a 18 1f3
[server] Region allocated at address 0xb0001000
after writing, ptr = 0xb0001000
before writing, ptr = 0xb0001000
[server] Exception handler called from: 20000058 869da80a 19 1
[server] Region allocated at address 0xb0001000
after writing, ptr = 0xb0001000
before writing, ptr = 0xb0002000
[server] Exception handler called from: 20000057 869da80a 18 1f3
[server] Region allocated at address 0xb0002000
after writing, ptr = 0xb0002000
before writing, ptr = 0xb0002000
[server] Exception handler called from: 20000058 869da80a 19 1
[server] Too many calls ...
[server] terminate client: 20000058 869da80a 19 1
-3->
before writing, ptr = 0xb0003000
[server] Exception handler called from: 20000057 869da80a 18 1f3
[server] Too many calls ...
[server] terminate client: 20000057 869da80a 18 1f3
-2->
We now terminate the server. We retrieve its capability through the cs com-
mand and abort its main thread to terminate properly:
-2-> rsh neon arun cs -la 23
started aid = 24
ChorusOS r4.0.0 Site 0 Time 4h 22m 56
438 Chapter 14. Abortions, exceptions and traps
ACTOR-UI KEY LID ......
20000055 869da80a 00000017 000001e8 0023 ......
THREAD-LI ........
0009 ........
................
-2-> neon threadAbort_u 20000055 869da80a 17 1e8 9
started aid = 24
-2->
[server] Server terminating by abort... CLEANING
-1->
a) The server
The f subsystem1.h le contains the characteristics of the subsystem (trap
number, the index of the various system calls, the number of system calls and
the maximum number of parameters on a system call):
--> cat $CHORUS/sources/subsystem1/include/f_subsystem1.h
#include <exec/p_chTrap.h>
#define TRAP_SUBSYSTEM1 TRAP62 /* selected trap number is 62 */
#define SUBSYSTEM1_ADD 0 /* add is the first system call */
#define SUBSYSTEM1_MULT 1 /* mult is the second syst. call */
#define SUBSYSTEM1_NB_ENTRIES (1+SUBSYSTEM1_MULT) /* 2 system calls */
#define MAXPARAMS 3 /* at most 3 parameters for a system call */
When started, the main thread creates a lap descriptor associated to the sub-
system1Trap function and connects it to the selected trap. Before connecting
it, the previously connected trap handler, if any, is disconnected (only one trap
handler can exist for a given trap number). Finally the main thread terminates:
442 Chapter 14. Abortions, exceptions and traps
--> cat $CHORUS/sources/subsystem1/server/subSyst1.c
#include "utilities.h"
#include "f_subsystem1.h"
void subsystem1Trap(KnSysTrapDesc *, char *);
main( ) {
KnLapDesc lapDesc;
int result;
result = svSysTrapHandlerDisconnect(TRAP_SUBSYSTEM1, K_CONNECTED_LAP);
if(result != K_OK)
printf("Error on svSysTrapHandlerDisconnect: %s\n",
strSysError(result));
result = svLapCreate(K_MYACTOR, (KnLapHdl) subsystem1Trap,
NULL, K_LAP_SAFE, &lapDesc);
if(result != K_OK) {
printf("Error on svLapCreate: %s\n", strSysError(result));
exit(1);
}
result = svSysTrapHandlerConnect(TRAP_SUBSYSTEM1, &lapDesc);
if(result != K_OK) {
printf("Error on svSysTrapHandlerConnect: %s\n", strSysError(result));
exit(1);
}
printf("Trap handler is connected\n");
threadDelete(K_MYACTOR, K_MYSELF);
}
When the trap handler is called due to a trap related to a system call performed
by an application
it rst identies the system call. The service was developed on a 486 system,
the system call index is extracted from the lower 16 bits of the eax general
purpose register of the processor; we will see how it is loaded into that register
before the trap;
then, the parameters are extracted from the user stack and copied to a vector.
For this kind of system and hardware, the value of the esp3 eld of the hard-
ware context gives access to the stack's pointer. A table gives, for each system
call, the name of the function to be called and the number of parameters to
be copied. In this example, the systemCallAdd and systemCallMult functions
have three parameters and a systemCallNull function has been added for pro-
cessing bad calls. Parameters are copied from the user address space to the
supervisor address space by means of the svCopyIn function.
We can now give the corresponding code:
14.4. Traps and subsystems 443
--> cat $CHORUS/sources/subsystem1/server/services.c
#include <stdio.h>
#include "f_subsystem1.h"
b) The library
The add and mult functions simply correspond to small assembly sequences
that initialize the eax register and generate the trap as follows:
--> cat $CHORUS/sources/subsystem1/lib/add.s
.file "add.s"
#include <exec/f_chTrap.h>
#include <prof/chAsmEnt_f.h>
#include "f_subsystem1.h"
.text
.align 4
.globl add
add:
movl $SUBSYSTEM1_ADD, %eax
int $TRAP_SUBSYSTEM1
ret
--> cat $CHORUS/sources/subsystem1/lib/mult.s
.file "mult.s"
#include <exec/f_chTrap.h>
#include <prof/chAsmEnt_f.h>
#include "f_subsystem1.h"
.text
.align 4
.globl mult
mult:
movl $SUBSYSTEM1_MULT, %eax
int $TRAP_SUBSYSTEM1
ret
The server subdirectory contains the server.c le and its Imakefile used for
building the Makefile by calling ChorusOSMkMf:
APPLI=$(CHORUS)/sources/subsystem1
INCLUDES=-I$(CHORUS)/include -I$(APPLI)/include
SRCS=subSyst1.c services.c
OBJS=subSyst1.o services.o
SupActorTarget(subSyst1_s.r, $(OBJS), $(CHORUS)/lib86/libutilities.a)
Depend($(SRCS))
The lib subdirectory contains the add.c, assAdd.s, mult.c and assMult.s
les and the Imakefile
APPLI=$(CHORUS)/sources/subsystem1
INCLUDES=-I$(APPLI)/include
SRCS=add.s mult.s
OBJS=add.o mult.o
LibraryTarget(libsubsystem.a, $(OBJS))
Depend($(SRCS))
After creating the various Makefiles in the directories, we simply execute the
make command in the application's root directory. Then we install the binaries
for the server and the application in the bin86 directory.
446 Chapter 14. Abortions, exceptions and traps
e) Starting the subsystem and running the application
First we start the application for the subsystem:
-1-> neon subSyst1_s.r
started aid = 23
Error on svSysTrapHandlerDisconnect: Invalid argument
Trap handler is connected
In this call
the object pointed to by timeout is opaque, its elds are not accessible to the
user and must have been allocated previously. This argument should not point
to an existing pending timeout;
14.5. Timeout handlers 447
waitLimit points to an object which contains the value of the timer. The
resolution of this argument (that is, the smallest value between two distinct
values) may be obtained by calling
#include <exec/chTimeout.h>
int svTimeoutGetRes(KnTimeVal *resolution);
ag must be equal to 0;
lapDesc points to a lap descriptor associated to an interrupt handler which
will be invoked when the amount of time specied by waitLimit has elapsed.
This handler will be executed in supervisor mode and its code and data must
be locked in memory. When invoked, it will be passed timeout as parameter.
The function returns K OK when the call succeeds and K EINVAL otherwise.
14.5.2 Canceling a timeout
A timeout may be canceled by calling the primitive
#include <exec/chTimeout.h>
int svSysTimeoutCancel(KnTimeout *timeout);
If the timeout referenced by the argument is still pending (the previous svSys-
TimeoutSet has not yet been satised), it is immediately canceled and the
call returns 1. Otherwise, the timeout has expired and the handler has been
executed, the call then returns 0.
14.5.3 Example
In the next example, the main thread of the actor associates a lap in its actor to a
timer which is set to 2 seconds. The corresponding handler simply increments an
external variable : this variable will count the number of times the lap is entered.
Once the timer is activated, the thread enters a rst loop. We observe that the
loop is interrupted, when the delay expires, the thread executes the handler and
the received variable is modied. In the rst version (appliTimeout1), the
timer is not rearmed: the thread loops for ever in the second loop. In the second
version (appliTimeout2), the timer is rearmed and thus the thread exits the
loop since the handler is called a second time:
--> cat $CHORUS/sources/time/lowLevel/appliTimeout.c
#include <stdio.h>
#include <chorus.h>
#include <exec/chTimeout.h>
KnTimeVal delay;
KnTimeout timeout;
KnLapDesc lapDesc;
int ind, result, received = 0;
448 Chapter 14. Abortions, exceptions and traps
void handler(KnTimeout *timeout, char *cookie) {
received ++;
}
main( ) {
result = svLapCreate(K_MYACTOR, (KnLapHdl) handler, "cookie", 0, &lapDesc);
if(result != K_OK) {
printf("svLapCreate: %s\n", strSysError(result));
exit(2);
}
K_MILLI_TO_TIMEVAL(&delay, 2000);
result = svSysTimeoutSet(&timeout, &delay, 0, &lapDesc);
if(result != K_OK) {
printf("first svSysTimeoutSet: %s\n", strSysError(result));
exit(2);
}
while(received == 0) ; /* busy waiting until received is non-zero */
printf("after first loop\n");
/* in the second version, rearm the timer */
result = svSysTimeoutSet(&timeout, &delay, 0, &lapDesc);
if(result != K_OK) {
printf("second svSysTimeoutSet: %s\n", strSysError(result));
exit(2);
}
*/
while(received == 1) ; /* busy waiting until received is not -1 */
printf("after second loop\n");
}
--> neon appliTimeout1_s.r
started aid = 2
after first loop
-2-> rsh neon akill 2 (= kill the actor
-2->
--> neon appliTimeout2_s.r
started aid = 2
after first loop
after second loop
-->
Chapter 15
The MEM VIRTUAL memory
management model
15.1 Introduction
15.1.1 General overview
We now complete our presentation of memory management by presenting the
MEM VIRTUAL model. The corresponding kernel's module, which is used
when both the VIRTUAL ADDRESS SPACE and ON DEMAND PAGING features are
enabled, supports full virtual memory with swapping in and out on secondary
devices.
We will rst revisit some aspects of memory management in the perspective of
this model (support for multiple protected address spaces and specic regions'
attributes or address space duplication, for instance).
Then we will present specic services provided by this model:
when secondary storage exists, the possibility of using more virtual memory
than available physical memory;
automatic swapping of pages when appropriate;
abstractions exported by the MEM VIRTUAL module:
- the segment is the unit of representation of information (quite similar to
a le). Segments can be mapped within regions of actors' address spaces
and are managed by dedicated actors called mappers;
- a local cache is used by the nucleus to manage the segments in use.
the interaction between the kernel and mappers is performed by using services
provided by the IPC feature to exchange messages according to a standard
protocol.
449
450 Chapter 15. The MEM VIRTUAL memory management model
The general organization and the relations between these dierent abstractions
is summarized in the next gure:
local cache
region
messages
actor 1
segment
mapper
region
page present in local cache
actor 2
kernel page not present in local cache
15.1.2 Segments
As previously mentioned, a segment is the unit of representation of information
in the system within the MEM VIRTUAL model. A segment may, for example,
be a persistent le or a temporary object (its lifetime is tied to a specic actor)
and is generally located in secondary storage. Segments are designated by capa-
bilities. They are not managed by the kernel but by independent actors named
mappers: identication of segments, denition of access rules and representa-
tion of objects are dened by the mappers. A default mapper is invoked by the
kernel to create external objects for swapping memory objects. A given mapper
may manage dierent segments; the capabilities of these various segments will
generally have the same ui eld which corresponds to a port of the mapper (for
example its default port), and the key eld of the dierent capabilities will be
used by the mapper to identify the dierent segments and to set protections on
them.
Firstly, segments may be mapped (partially or totally) in actors' regions: it is
the memory management which provides the mapping between the regions and
the segments. Dierent actors may share the same segment even if they do not
15.2. Basic concepts 451
belong to the same site. In this case, no coherency is provided between the
dierent views, specic coherency mappers have to be provided. Segments may
also be explicitly accessed without mapping them in an address space.
15.1.3 Local caches
Kernels do not manage segments; they see segments through local caches. For
every mapped segment on a site, the corresponding kernel encapsulates, in a
unique local cache, the physical memory containing the part of the segment that
is used. When accessing a segment, the kernel accesses the corresponding local
cache. If the data is not present, a page fault occurs and the kernel invokes the
mapper managing the segment through a generic interface. Thus, accessing a
segment requires access to a local cache and may imply moving data between
that cache and the corresponding segment. This means that communication
between the kernel and the mapper in charge of the segment has to established.
Coherency between dierent mappings of the same segment on a given site is
ensured by the local cache mechanism. The MEM VIRTUAL model provides
facilities for controlling the state and the contents of local caches.
15.1.4 The generic interface
This denes the structure of the messages exchanged between the kernel and a
mapper in order to move data between a segment and its corresponding cache
or the converse. Three fundamental types of messages facilitate this type of
data exchange:
the kernel may request a mapper to move the contents of a fragment of a given
segment into its local cache by sending a specic message (called a MpPullIn
message);
the kernel sends a MpPushOut message to a mapper; this message contains
data used by the mapper to update a segment;
the kernel may ask a mapper to get specic access rights to a fragment of a
segment though a MpGetAccess message.
15.2 Basic concepts
15.2.1 General aspects
We rst download an image of ChorusOS using the MEM VIRTUAL memory
module onto the neon machine:
Cold reboot ...
........................
My IP 134.157.168.10, RARP Server IP 134.157.168.9
We can check the activity of the module within the memVersion application and
the fact that the kernel has 8 threads instead of 3 within the MEM PROTEC-
TED module (see 3.8.3.1):
--> neon memVersion_u
started aid = 2
Virtual memory
--> rsh neon arun cs | grep kern
started aid = 2
20000001 869da80a 00000001 00000001 0001 SUP STARTED 008 kern
-->
The values of the default mapper and the marks may be set by a supervisor
thread through a call to the primitive
#include <mem/chMem.h>
int vmSetPar(KnVmPar *vmParam);
Note that the actor owns 4 regions instead of 3 when using MEM PROTEC-
TED. The region starting at 0xfffec000 has been split into two because of
dierent opaque values.
We create a 3 page region whose start address is 0xb0000000 in that actor using
the allocRgn application developed in 7.4.2.2:
--> neon allocRgn_u 20000036 869da80a 2 f7 0xb0000000 0x3000 1
started aid = 22
--> neon listRegions_u 20000036 869da80a 2 f7 | grep 0xb0000000
started aid = 22
start = 0xb0000000 size = 0x3000 [0x20002]WR ND [0] [0]
-->
We can observe that the region has been created with the K NODEMAND attribute
although it was not required.
We nally use the modAttr application to change the protection attributes (the
middle page of the region is no longer writable) and the region splits into 3
regions:
--> neon modAttr_u 20000036 869da80a 2 f7 0xb0001000 0x1000 r
started aid = 22
--> neon listRegions_u 20000036 869da80a 2 f7 | grep 0xb
started aid = 22
start = 0xb0000000 size = 0x1000 [0x20002]WR ND [0] [0]
start = 0xb0001000 size = 0x1000 [0x20000]ND [0] [0]
start = 0xb0002000 size = 0x1000 [0x20002]WR ND [0] [0]
-->
456 Chapter 15. The MEM VIRTUAL memory management model
15.4 Duplicating an actor address space
In 7.6.2 we presented services to duplicate a part of an actor's address space
into another one (rgnMapFromActor and rgnInitFromActor). These services
are available within the MEM VIRTUAL module, but this module provides a
more complete service which raises the limitations of these services with regard
to the fact that the duplicated address range must not contain holes.
This type of call is allowed only if the address space of the target actor is empty.
The
ags parameter may be 0, K INHERITSHARE or K INHERITCOPY. For each
region in the address space of the source actor, a region is created in the target
actor with same start address, same size and same attributes. Furthermore,
depending on the value of the inheritance attribute of the source region and/or
the value of
ags the region may be or not duplicated:
if the
ags parameter is 0, the duplication mode of the region depends on the
inheritance attribute of the corresponding region in the source actor:
- if this attribute is K INHERITSHARE, the new region accesses the same
physical data as the corresponding source region;
- if this attribute is K INHERITCOPY, the new region is a copy of the original
one;
- if this attribute is neither K INHERITSHARE nor K INHERITCOPY, the region
is not duplicated (it is not created);
15.4. Duplicating an actor address space 457
if either K INHERITSHARE or K INHERITCOPY is passed as the flags argument
to rgnDup,
- if the value is K INHERITSHARE, each new region is created as if rgnMap-
FromActor was called, that is, the new region accesses the same physical
data as the corresponding region of the source actor;
- if the value is K INHERITCOPY, the new region is created as if rgnInit-
FromActor was called, each new region is a copy of the corresponding
region of the source actor.
This enables
ags specied at region creation time in the source actor to be
overwritten.
If the call is successful K OK is returned, otherwise one of the following error
values may be returned when the call fails:
return value error type
K EINVAL inconsistent actor capability
K EUNKNOWN unknown actor
K EFAULT address out of address space
K ENOMEM system out of resources
K ENOEMPTY the target address range is not empty
15.4.2 Example
15.4.2.1 The application
In the following rgnDup.c example:
the capability of an actor is passed as argument (four rst parameters). It
identies the actor whose address space will be duplicated when calling rgnDup;
the fth parameter denes the mode of duplication (0, 1 for K INHERITCOPY
and 2 for K INHERITSHARE) to be passed when calling rgnDup;
the main thread of the current c actor creates an empty actor by calling
actorCreate. This actor has the same privilege as the source actor;
the source actor's address space is duplicated in the new actor by calling
rgnDup.
--> cat $CHORUS/sources/memory/rgnDup/rgnDup.c
#include "utilities.h"
main(int argc, char *argv[ ]) {
int result, option;
KnActorPrivilege privilege;
KnCap sourceActorCap, newActorCap;
readCap(argv + 1, &sourceActorCap);
sscanf(argv[5], "%d", &option);
458 Chapter 15. The MEM VIRTUAL memory management model
/* getting source actor's privilege */
actorPrivilege(&sourceActorCap, &privilege, NULL);
/* creating an empty actor */
result = actorCreate(K_MYACTOR, &newActorCap, privilege, 0);
if(result < 0) {
fprintf(stderr, "Error on actorCreate: %s\n", strSysError(result));
exit(1); }
actorName(&newActorCap, NULL, "emptyActor");
fprintCap(stderr, "a new actor has been created", &newActorCap);
/* duplication of the regions of the source actor */
switch(option) {
case 0: result = rgnDup(&newActorCap, &sourceActorCap, 0);
if(result < 0) {
fprintf(stderr, "rgnDup: %s\n", strSysError(result));
exit(1); }
break;
case 1: result = rgnDup(&newActorCap, &sourceActorCap, K_INHERITCOPY);
if(result < 0) {
fprintf(stderr, "rgnDup: %s\n", strSysError(result));
exit(1); }
break;
case 2: result = rgnDup(&newActorCap, &sourceActorCap, K_INHERITSHARE);
if(result < 0) {
fprintf(stderr, "rgnDup: %s\n", strSysError(result));
exit(1); }
}
}
We now apply the rgnDup application to that actor with 0 as last parameter:
an empty actor is created and regions are duplicated from the previous actor by
calling rgnDup. As 0 is passed as argument, only regions having an inheritance
attribute will be duplicated. Here only two regions have this property:
--> neon rgnDup_u 20000040 869da80a 2 14c 0
started aid = 22
a new actor has been created: 20000047 869da80a 17 1
--> neon listRegions_u 20000047 869da80a 17 1
started aid = 22
The actor has 2 regions
start = 0xb1000000 size = 0x1000 [0x60002]WR IS ND [0] [0]
start = 0xb2000000 size = 0x1000 [0xa0002]WR IC ND [0] [0]
-->
We use the physAddr application (see 7.2.6) to compare the physical addresses
corresponding to the virtual starting addresses of the two actors' regions. Vir-
tual address 0xb1000000 corresponds to the same physical one in both actors,
unlike 0xb2000000 which corresponds to two dierent ones.
--> neon physAddr_u 20000040 869da80a 2 14c 0xb1000000
started aid = 22
virt addr b1000000 ==> phys addr 0xe9d000 in: 20000040 869da80a 2 14c
--> neon physAddr_u 20000047 869da80a 17 1 0xb1000000
started aid = 22
virt addr b1000000 ==> phys addr 0xe9d000 in: 20000047 869da80a 17 1
--> neon physAddr_u 20000040 869da80a 2 14c 0xb2000000
started aid = 22
virt addr b2000000 ==> phys addr 0xec3000 in: 20000040 869da80a 2 14c
--> neon physAddr_u 20000047 869da80a 17 1 0xb2000000
started aid = 22
virt addr b2000000 ==> phys addr 0xe7a000 in: 20000047 869da80a 17 1
-->
We repeat the same operation with the K INHERITCOPY argument rst; all re-
gions are duplicated and the same logical addresses in both actors correspond
to dierent physical ones, even if the source region has the K INHERITSHARE
attribute:
460 Chapter 15. The MEM VIRTUAL memory management model
--> neon rgnDup_u 20000040 869da80a 2 14c 1
started aid = 22
a new actor has been created: 2000004f 869da80a 18 1fb
--> neon listRegions_u 2000004f 869da80a 18 1fb
started aid = 22
The actor has 7 regions
start = 0xb0000000 size = 0x1000 [0x20002]WR ND [0] [0]
start = 0xb1000000 size = 0x1000 [0x60002]WR IS ND [0] [0]
start = 0xb2000000 size = 0x1000 [0xa0002]WR IC ND [0] [0]
start = 0xfffec000 size = 0x2000 [0x21002]AW WR ND [ffffc7c7] [ffffdc3e]
start = 0xfffee000 size = 0x4000 [0x21042]AW WR ND FZ [0] [0]
start = 0xffff3000 size = 0x9000 [0x21042]AW WR ND FZ [0] [0]
start = 0xffffc000 size = 0x3000 [0x21041]AW EX ND FZ [0] [0]
--> neon physAddr_u 20000040 869da80a 2 14c 0xb1000000
started aid = 22
virt addr b1000000 ==> phys addr 0xe9d000 in: 20000040 869da80a 2 14c
--> neon physAddr_u 2000004f 869da80a 18 1fb 0xb1000000
started aid = 22
virt addr b1000000 ==> phys addr 0xea4000 in: 2000004f 869da80a 18 1fb
-->
We nally use rgnDup with the K INHERITSHARE argument, all regions are du-
plicated and the same logical addresses correspond to the same physical ones
(even if in a region with the K INHERITCOPY attribute):
--> neon rgnDup_u 20000040 869da80a 2 14c 2
started aid = 22
a new actor has been created: 20000052 869da80a 19 1
--> neon listRegions_u 20000052 869da80a 19 1
started aid = 22
The actor has 7 regions
start = 0xb0000000 size = 0x1000 [0x20002]WR ND [0] [0]
start = 0xb1000000 size = 0x1000 [0x60002]WR IS ND [0] [0]
start = 0xb2000000 size = 0x1000 [0xa0002]WR IC ND [0] [0]
start = 0xfffec000 size = 0x2000 [0x21002]AW WR ND [ffffc7c7] [ffffdc3e]
start = 0xfffee000 size = 0x4000 [0x21042]AW WR ND FZ [0] [0]
start = 0xffff3000 size = 0x9000 [0x21042]AW WR ND FZ [0] [0]
start = 0xffffc000 size = 0x3000 [0x21041]AW EX ND FZ [0] [0]
--> neon physAddr_u 20000040 869da80a 2 14c 0xb2000000
started aid = 22
virt addr b2000000 ==> phys addr 0xec3000 in: 20000040 869da80a 2 14c
--> neon physAddr_u 20000052 869da80a 19 1 0xb2000000
started aid = 22
virt addr b2000000 ==> phys addr 0xec3000 in: 20000052 869da80a 19 1
-->
15.5. Regions and segments 461
15.4.2.3 The case of supervisor actors
We rst create a supervisor actor for the noDelay3 application and create three
regions in that actor, as before (we use the K ANYWHERE
ag):
--> neon-n noDelay3_s.r &
[2] 6593
--> started aid = 22
In this call
actorCap points to the capability of the target actor. It may take the
K MYACTOR or K SVACTOR values (in the latter case, the region will be allocated
in the supervisor address space and will not be attached to any actor, it will
thus have to be explicitly deallocated);
rgnDesc and segDesc describe, respectively, the target region and the source
segment range. If K FILLZERO is set in the options eld of *rgnDesc, the range
[rgnDesc.startAddr+segDesc.size-1:rgnDesc.startAddr+rgnDesc.size-1]
of addresses will be zero-lled as described in the following gure:
segDesc.startOffset
rgnDesc.startAddr
segDesc.size
0 0 0 0 0 0 0 0
............
0 0 0 0 0 0 0 0
the region
the segment
The primitive returns K OK upon success, otherwise one of the following negative
values is returned:
15.5. Regions and segments 463
value error type
K EUNKNOWN unknown actor
K EINVAL inconsistent actor capability
K EFAULT address out of address space
K EROUND rgnDesc.startAddr is not page-aligned or
segDesc.startOffset is not page-aligned
K ESPACE invalid value startAddr or rgnDesc.size is 0
K EOFFSET segment range out of the segment
K ESIZE segDesc.startOffset is greater than rgnDesc.size
K EOVERLAP allocation of the region would lead to overlapping or
K ANYWHERE option and not enough space
K EMAPPER the mapper does not respect the generic protocol
K ENOMEM system is out of resources
The parameters are interpreted in the same way as the corresponding ones in
the rgnInit primitive except that
the size eld of segDesc is ignored, the mapped range of the segment is equal
to the size of the new region;
the K FILLZERO
ag is ignored;
the K NOSYNC
ag may be set in the flags eld of rgnDesc. In that case, the
kernel writes back dirty pages of the segment as late as possible; otherwise, the
kernel guarantees that any modication of a segment is written back within a
period of time dened by a system conguration parameter.
If the call is successful, the primitive returns K OK. Otherwise it returns the
same negative values as for rgnInit.
464 Chapter 15. The MEM VIRTUAL memory management model
15.6 Explicit access to a segment
15.6.1 Reading data from a segment
Data may be directly read from a segment into the address space of an actor
by calling the primitive
#include <mem/chMem.h>
int sgRead(
KnObjDesc *srcSegDesc,
KnCap *dstActorCap,
VmAddr dstAddress,
VmFlags
ags
);
The srcSegDesc argument denes which segment and which range of that seg-
ment is to be read. The dstActorCap and dstAddress parameters dene in which
actor and at which address the data read are to be written. The K MYACTOR and
K SUPERVISOR values may be used for destActorCap.
When returning from a call, the value of the size eld of the object pointed to
by srcSegDesc is the number of bytes that have been eectively read, even if an
error occurred.
The function returns either K OK if the call was successful, or a negative value
if an error occurred:
When returning from a call, the segcap is initialized only if the calling thread
executes in the privileged mode or belongs to a system actor.
The statistics of a local cache may also be got by using the capability of the
corresponding segment through a call to the primitive
#include <mem/chMem.h>
int sgStat(
KnCap *segmentCap,
KnLcStat *statPointer
);
In that case, the lccap eld is initialized only if the calling thread executes in
the privileged mode or belongs to a system actor.
These two primitives return K OK if they succeed, and a negative value otherwise
(K EUNDEF if the segment is not cached on the site, or K EFAULT if there is data
outside the address space of the calling actor).
15.7.3 Flushing and controlling access to a local cache
The following
ags correspond to specic actions performed when
ushing a
local cache:
K COPYBACK: when
ushing a cache, all the data is written back to the segment
even if it has not been modied and the rights accesses are not changed;
15.7. Explicit access to a local cache 467
K WRITABLE: when
ushing a cache, all the modied data is written back to
the segment and the rights accesses are not modied;
K READABLE: when
ushing a cache, all the modied data is written back to
the segment and the rights accesses are set to read-only;
K FREEZE: when
ushing a cache, all the modied data is written back to the
segment and the rights accesses are set to non-accessible;
K NOACCESS: when
ushing a cache, all the modied data is written back to
the segment and the data is invalidated;
K DESTROY: the data are simply invalidated (they are not written back);
K ASYNC: the write operations are asynchronously performed;
K PAGEFAULT: the operation is the result of a page fault.
A
ush on a range of a local cache may be performed by calling the primitive
#include <mem/chMem.h>
int lcFlush(
KnObjDesc *lcDesc,
VmFlags
ags,
unsigned long orderNb
);
The lcDesc parameter points to an object describing the range as dened in the
rgnInit primitive.
All local caches associated to a segment on the local site may be
ushed by
calling the primitive
#include <mem/chMem.h>
int sgFlush(
KnObjDesc *segDesc,
VmFlags
ags
);
Finally, protections may be changed without invalidating or writing back the
data by calling the primitive
#include <mem/chMem.h>
int lcSetRights(
KnObjDesc *lcDesc,
VmFlags
ags,
unsigned long orderNb
);
The only
ags that are recognized are K READABLE, K FREEZE and K NOACCESS.
468 Chapter 15. The MEM VIRTUAL memory management model
15.7.4 Initializing a local cache
Write access may be given for a local cache by calling the primitive
#include <mem/chMem.h>
int lcFillZero(
KnObjDesc *lcDesc,
VmFlags
ags,
unsigned long orderNb
);
A side eect of this call is to mark as modied and to initialize to 0 the range
between lcDesc->startOffset and lcDesc->startOffset+lcDesc->size.
kernelSrv( ) {
pageSize = vmPageSize( );
rgnDesc.startAddr = NULL;
rgnDesc.size = pageSize;
rgnDesc.options = K_ANYWHERE | K_WRITABLE | K_NODEMAND;
result = rgnAllocate(K_MYACTOR, &rgnDesc);
if(result != K_OK) {
printf("error on rgnAllocate: %s\n", strSysError(result));
exit(1);
}
476 Chapter 15. The MEM VIRTUAL memory management model
while(1) {
printf("Mapper : Service Kernel ... %d\n", threadSelf( ));
/* prepare to receive request from the kernel */
msgDesc.flags = K_ABORTABLE;
msgDesc.bodyAddr = rgnDesc.startAddr;
msgDesc.bodySize = pageSize;
msgDesc.annexAddr = (VmAddr) (&annexRequest);
msgSize = ipcReceive(&msgDesc, &kernelPortLi, K_NODELAY);
switch (annexRequest.service) {
case KN_MPIN : pullInSrv(&msgDesc);
continue;
case KN_MPGETACCESS :
getAccessSrv(&msgDesc);
continue;
case KN_MPOUT :
pushOutSrv(&msgDesc);
continue;
default :
printf ("Mapper : Service Kernel Unknown %d\n",
annexRequest.service);
annexReply.error = K_ENOTIMP;
msgDesc.bodyAddr = NULL;
msgDesc.bodySize = 0;
ipcReply(&msgDesc);
}
}
}
pullInSrv(KnMsgDesc *msgDesc) {
printf("Mapper: mpPullIn (%ld)\n", annexRequest.pullIn.segkey.keyHead);
/* WARNING BUG!: annexRequest.pullIn.dataOffset need to be translated */
printf(" ==> dataOffset : %ld\n",
- (8192 + annexRequest.pullIn.dataOffset));
printf(" ==> dataSize : %ld\n", annexRequest.pullIn.dataSize);
/* WARNING BUG!: annexRequest.pullIn.requiredDataOffset need to be translated */
printf(" ==> requiredDataOffset : %ld\n",
- (8192 + annexRequest.pullIn.requiredDataOffset));
printf(" ==> requiredDataSize : %ld\n",
annexRequest.pullIn.requiredDataSize);
printf(" ==> accessOffset : %ld\n", annexRequest.pullIn.accessOffset);
printf(" ==> accessSize : %ld\n", annexRequest.pullIn.accessSize);
printf(" ==> requiredAccessOffset : %ld\n",
annexRequest.pullIn.requiredAccessOffset);
printf(" ==> requiredAccesssize: %ld\n",
annexRequest.pullIn.requiredAccessSize);
printf(" ==> requiredAccess: %s ",
(annexRequest.pullIn.requiredAccess & K_WRITABLE)? "RW" : "RO");
15.9. Example 477
if(annexRequest.pullIn.requiredAccess & K_PAGEFAULT)
printf("PAGEFAULT ");
if(annexRequest.pullIn.requiredAccess & K_GETATTR)
printf("GETTATTR");
putchar('\n');
annexReply.pullIn.grantedAccess = K_MPIVER1|K_CLUSTER ;
if(annexRequest.pullIn.requiredAccess & K_WRITABLE)
annexReply.pullIn.grantedAccess |= K_WRITABLE;
annexReply.pullIn.inClusterSize = pageSize;
annexReply.pullIn.outClusterSize = pageSize;
annexReply.pullIn.returnAccessOffset = annexRequest.pullIn.accessOffset;
annexReply.pullIn.returnDataOffset = annexRequest.pullIn.dataOffset;
/* WARNING BUG!: annexRequest.pullIn.dataOffset need to be translated */
result = getData(&annexRequest.pullIn.segkey,
msgDesc -> bodyAddr,
- (8192 + annexRequest.pullIn.dataOffset),
annexRequest.pullIn.dataSize);
printf("getData = %d\n", result);
msgDesc -> annexAddr = (VmAddr) (&annexReply);
if(result < 0)
annexReply.pullIn.diag = result;
else {
annexReply.pullIn.diag = K_OK;
annexReply.pullIn.returnAccessSize =
annexRequest.pullIn.accessSize;
annexReply.pullIn.returnDataSize = annexRequest.pullIn.dataSize;
msgDesc -> bodySize = result;
}
ipcReply(msgDesc);
}
getAccessSrv(KnMsgDesc *msgDesc) {
printf("Mapper: mpGetAccess (%ld)\n", annexRequest.getAccess.segkey.keyHead);
printf(" ==> accessOffset : %ld\n", annexRequest.getAccess.accessOffset);
printf(" ==> accessSize : %ld\n", annexRequest.getAccess.accessSize);
printf(" ==> requiredAccessOffset : %ld\n",
annexRequest.getAccess.requiredAccessOffset);
printf(" ==> requiredAccessSize : %ld\n",
annexRequest.getAccess.requiredAccessSize);
printf(" ==> requiredAccess : %s\n",
(annexRequest.getAccess.requiredAccess & K_WRITABLE)
? "RW" : "RO");
annexReply.getAccess.diag = K_OK;
annexReply.getAccess.grantedAccess = K_MPIVER1|K_CLUSTER ;
if(annexRequest.getAccess.requiredAccess & K_WRITABLE)
annexReply.getAccess.grantedAccess |= K_WRITABLE;
478 Chapter 15. The MEM VIRTUAL memory management model
annexReply.getAccess.returnAccessOffset =
annexRequest.getAccess.accessOffset;
annexReply.getAccess.returnAccessSize =
annexRequest.getAccess.accessSize;
msgDesc -> bodyAddr = NULL;
msgDesc -> bodySize = 0;
msgDesc -> annexAddr = (VmAddr) (&annexReply);
ipcReply(msgDesc);
}
pushOutSrv(KnMsgDesc *msgDesc) {
printf("Mapper: mpPushOut (%ld)\n", annexRequest.pushOut.segkey.keyHead);
/* WARNING BUG!: annexRequest.pushOut.dataOffset need to be translated */
printf (" ==> dataOffset : %ld\n",
- (8192 + annexRequest.pushOut.dataOffset));
printf(" ==> dataSize : %ld\n",
annexRequest.pushOut.dataSize);
msgDesc -> annexAddr = (VmAddr) (&annexReply);
/* WARNING BUG!: annexRequest.pushOut.dataOffset need to be translated */
result = putData( &annexRequest.pushOut.segkey,
msgDesc -> bodyAddr,
- (8192 + annexRequest.pushOut.dataOffset),
annexRequest.pushOut.dataSize);
if(result < 0)
annexReply.pushOut.diag = result;
else
annexReply.pushOut.diag = K_OK;
msgDesc -> bodyAddr = NULL;
msgDesc -> bodySize = 0;
ipcReply(msgDesc);
}
15.9.2 A mapper
We now give an example of a very simple mapper:
the mapper manages a single 2 page segment and the corresponding space is
allocated in the supervisor address space by using the supervisor space allocator
(svPagesAllocate). Its contents are interpreted as an array of integers whose
initial value is f0, 1, 2, ..., 2047g;
the value of kernelPortLi used in the kernelSrv function is the local iden-
tier of a new port created in the actor. The unique identier of this port is
used to build the segment capability: it is the ui eld of this capability. The
key's head and tail both have 4 as value. This capability is displayed and will
allow us to use the segment to create other actors and threads easily;
the kernelSrv function is then called to answer the kernel's requests that will
arrive on this port;
15.9. Example 479
the two specic getData and putData functions are dened. They simply
copy from the segment to the body of the reply, or the converse, by using the
svMemRead and svMemWrite utilities.
main( ) {
result = svPagesAllocate((VmAddr *) &segment, 1500 * sizeof(int),
K_NOWAITFORMEMORY, K_SVACTOR);
if(result != K_OK) {
printf("error on svPagesAllocate: %s\n", strSysError(result));
exit(2); }
pageSize = vmPageSize( );
for (ind = 0; ind < 1500; ind ++)
segment[ind] = ind;
kernelPortLi = portCreate(K_MYACTOR, &kernelPortUi);
segCap.ui = kernelPortUi;
segCap.key.keyHead = segCap.key.keyTail = 4;
fprintCap(stdout, "segment", &segCap);
kernelSrv( );
}
- We now start the application: the mapper is installed and a user actor client
mapping the segment onto a region with the K NOSWAPOUT attribute is then
executed. The various messages which are printed show the operations that the
client performs as well the messages that are received by the mapper due to
these operations.
-1-> neon-n mapper_s.r
started aid = 22
segment: 40000026 869da80a 4 4
Mapper : Service Kernel ... 9
-2-> neon clientMap_u 40000026 869da80a 4 4
started aid = 2
Mapper: mpPullIn (4)
==> dataOffset : 0
==> dataSize : 4096
==> requiredDataOffset : 0
==> requiredDataSize : 0
==> accessOffset : 0
==> accessSize : 4096
==> requiredAccessOffset : 0
==> requiredAccesssize: 1
==> requiredAccess: RW PAGEFAULT GETTATTR
482 Chapter 15. The MEM VIRTUAL memory management model
getData = 4096
Mapper : Service Kernel ... 9
Mapper: mpPullIn (4)
==> dataOffset : 4096
==> dataSize : 4096
==> requiredDataOffset : 4096
==> requiredDataSize : 0
==> accessOffset : 4096
==> accessSize : 4096
==> requiredAccessOffset : 4096
==> requiredAccesssize: 1
==> requiredAccess: RW PAGEFAULT
getData = 4096
Mapper : Service Kernel ... 9
operation = 1, index = 911
==> new value of segment[911] = 1132
Mapper: mpPushOut (4)
==> dataOffset : 0
==> dataSize : 4096
Mapper : Service Kernel ... 9
operation = 0, index = 2042
==> segment[2042] = 2042
operation = 1, index = 8
==> new value of segment[8] = 1641
Mapper: mpPushOut (4)
==> dataOffset : 0
==> dataSize : 4096
Mapper : Service Kernel ... 9
operation = 1, index = 1415
==> new value of segment[1415] = 1324
Mapper: mpPushOut (4)
==> dataOffset : 4096
==> dataSize : 4096
Mapper : Service Kernel ... 9
-2->
2 rst, when the client requests the mapping of the segment, the mapper
receives, for each page of the mapped segment, a MpPullIn request: as
the segment is mapped for writing the K WRITABLE
ag is also set. In fact,
the thread calling rgnMap is blocked until all requests are satised. This
specic treatment is due to the use of the K NOSWAPOUT
ag for the region;
2 when returning from rgnMap, the rst operation requested is to write
a new value (1132) at the position 911. The kernel just sends a Mp-
PushOut request to the mapper to update the permanent segment, the
oset is 0 because the update concerns the rst page of the segment;
15.9. Example 483
2 the next operation is a request to read the element in position 2042.
The page is present and thus the operation is immediate, no message is
transmitted to the mapper;
2 the third operation is a request to write 1641 in element 8 (in the rst
page of the segment). A MpPushOut message is sent to the mapper;
2 the last operation is a request to write 1324 in element 1415: a Mp-
PushOut message is sent to the mapper to update the second page of
the segment (oset is 4096).
- The situation is dierent if, once the mapper is started, the rst client we
execute is a modied version where the region mapping the segment does not
own the K NOSWAPOUT attribute:
-1-> neon-n mapper_s.r &
started aid = 22
segment: 40000028 869da80a 4 4
Mapper : Service Kernel ... 9
-2-> neon clientMap_u 40000028 869da80a 4 4
started aid = 2
operation = 0, index = 714
Mapper: mpPullIn (4)
==> dataOffset : 0
==> dataSize : 4096
==> requiredDataOffset : 0
==> requiredDataSize : 0
==> accessOffset : 0
==> accessSize : 4096
==> requiredAccessOffset : 0
==> requiredAccesssize: 1
==> requiredAccess: RO PAGEFAULT GETTATTR
getData = 4096
Mapper : Service Kernel ... 9
==> segment[714] = 714
operation = 1, index = 1880
Mapper: mpPullIn (4)
==> dataOffset : 4096
==> dataSize : 409b
==> requiredDataOffset : 4096
==> requiredDataSize : 0
==> accessOffset : 4096
==> accessSize : 4096
==> requiredAccessOffset : 4096
==> requiredAccesssize: 1
==> requiredAccess: RW PAGEFAULT
484 Chapter 15. The MEM VIRTUAL memory management model
getData = 4096
Mapper : Service Kernel ... 9
==> new value of segment[1880] = 2825
Mapper: mpPushOut (4)
==> dataOffset : 4096
==> dataSize : 4096
Mapper : Service Kernel ... 9
operation = 1, index = 279
Mapper: mpGetAccess (4)
==> accessOffset : 0
==> accessSize : 4096
==> requiredAccessOffset : 0
==> requiredAccessSize : 1
==> requiredAccess : RW
Mapper : Service Kernel ... 9
==> new value of segment[279] = 1268
Mapper: mpPushOut (4)
==> dataOffset : 0
==> dataSize : 4096
Mapper : Service Kernel ... 9
operation = 0, index = 290
==> segment[290] = 290
-2->
2 rst, when the client requests the mapping of the segment the mapper
does not receive any message;
2 the rst operation is a read operation for the element in position 714.
This element belongs to the rst page of the segment: the kernel sends
a MpPullIn message to the mapper to get the corresponding data. The
data are only readable;
2 the second operation is a write on the element in position 1880. This page
is not yet mapped: the kernel sends a MpPullIn request to the mapper.
The page is mapped in the region and is writable. Once it is loaded, the
client writes the new value, which is 2825. This operation provokes the
sending of a MpPushOut message to the mapper; thus the mapper may
update the persistent copy of the modied page;
2 the third operation is a write on the element in position 279. This el-
ement belongs to the rst page of the segment which is present but is
not writable. A MpGetAccess request is sent by the kernel in order to
acquire the writable rights. Once the mapper has replied, the element is
modied (new value is 1268) and the mapper receives a MpPushOut
notication;
15.9. Example 485
2 the last operation is to read the value of the element in position 290.
As the page is already present and is readable, no message is sent to
the mapper, the operation may be directly performed (the value 290 is
displayed).
In this last context, at that point the two pages of the segment are present in
the local cache and are writable. We start another client:
-2-> neon clientMap_u 40000028 869da80a 4 4
started aid = 2
operation = 0, index = 517
==> segment[517] = 517
operation = 1, index = 1163
==> new value of segment[1163] = 232
Mapper: mpPushOut (4)
==> dataOffset : 4096
==> dataSize : 4096
Mapper : Service Kernel ... 9
operation = 0, index = 550
==> segment[550] = 550
operation = 1, index = 1044
==> new value of segment[1044] = 1549
Mapper: mpPushOut (4)
==> dataOffset : 4096
==> dataSize : 4096
Mapper : Service Kernel ... 9
-2->
We can observe that read access is performed without messages and that modi-
cations of elements provoke MpPushOut messages to update the correspond-
ing pages.
We rst execute the clientSgRead application to read some values: the rst
operation reads an unmodied value and the second one a value that has been
previously modied by a client. We can observe that the mapper does not
receive any message since the two pages are present and valid in the local cache:
A rst execution forces local cache
ushing without modifying access rights by
using the K COPYBACK
ag. We can observe that the kernel sends two Mp-
PushOut requests and that a reader may still read, no request is sent to the
mapper:
-2-> neon clientSgFlush_u 40000028 869da80a 4 4 C
started aid = 2
Mapper: mpPushOut (4)
==> dataOffset : 0
==> dataSize : 4096
Mapper : Service Kernel ... 9
488 Chapter 15. The MEM VIRTUAL memory management model
Mapper: mpPushOut (4)
==> dataOffset : 4096
==> dataSize : 4096
Mapper : Service Kernel ... 9
-2-> neon clientSgRead_u 40000028 869da80a 4 4 1200
started aid = 2
value of segment[1200] = 100
-2->
In the second execution, the clientSgFlush client uses the K NOACCESS
ag.
As no data has been modied, no MpPushOut request is sent, but the data
are invalidated. Thus, if a client tries to access a page a MpPullIn request
is sent to the mapper. We can observe that it is not a page fault (the page is
present but is not valid):
-2-> neon clientSgFlush_u 40000028 869da80a 4 4 N
started aid = 2
-2-> neon clientSgRead_u 40000028 869da80a 4 4 1200
started aid = 2
489
490 Chapter 16. The ChorusOS driver framework
20000008 869da80a 00000008 00000008 0008 SUP STARTED 000 D_mc146818
20000009 869da80a 00000009 00000001 0009 SUP STARTED 000 D_ns16550
2000000a 869da80a 0000000a 0000000b 0010 SUP STARTED 000 D_epic100
2000000b 869da80a 0000000b 00000000 0011 SUP STARTED 000 D_dec21x4x
2000000c 869da80a 0000000c 0000000c 0012 SUP STARTED 000 D_el3
2000000d 869da80a 0000000d 00000001 0013 SUP STARTED 000 D_smc1660
2000000e 869da80a 0000000e 0000000f 0014 SUP STARTED 000 D_ne2000
2000000f 869da80a 0000000f 00000000 0015 SUP STARTED 000 D_sram
20000010 869da80a 00000010 00000010 0016 SUP STARTED 000 D_idebios
These dierent actors correspond to the following drivers and devices:
D pci: driver for a generic PCI bridge (properties in genericProp.h le);
D pcienum: the pci enumerator driver. It is a probe driver: it scans the pci
bus to detect all connected devices and updates the device tree;
D pci pic: Intel8259 PIC driver in pci class (hardware related denitions in
i8259.h le and properties in picProp.h le);
D pci isa: driver for generic PCI/ISA bridge (properties in genericProp.h
le);
D i8254: timer device driver for Intel for Intel i8254 timer chip (hardware
related denitions in i8254.h le and properties in i8254Prop.h le);
D mc146818: real time clock device driver for Motorola MC146818 chip (hard-
ware related denitions in mc146818.h le and properties in mc146818Prop.h
le);
D ns16550: serial Line Device Driver (asynchronous mode) for NS16450,
INS8250A, NCS16C450, INS82C50A, NS16C552 and NS16550 chips (hardware re-
lated denitions in ns16550.h le and properties in ns16550Prop.h le);
D epic100: pci Ethernet device driver for epic100 Ethernet controller (prop-
erties in epic100Prop.h le and private hardware denitions in epic100.h
le);
D dec21x4x: Ethernet device driver for dec21x4x PCI Ethernet controllers
[dec 21040, 21041, 21140 and 21143 Ethernet controllers] (private denitions
in dec21x4x.h le and properties in dec21x4xProp.h le);
D el3: Ethernet device drive for 3Com etherlinkIII controller (private def-
initions in el3.h le and properties in el3Prop le);
D smc1660: isa Ethernet device driver for WD80X3, SmcElite16, SmcEtherEZ
and SmcEliteUltra boards which are based on the DP8390/NS32490D Network
Interface Controller;
D ne2000: Ethernet device driver for Bus ethernet controllers NE2000 PCI and
NE2000 ISA (private denitions in ne2000.h le and properties in ne2000Prop.h
le);
16.1. Introduction 491
D sram: SRAM
ash driver (properties in sramProp.h le);
D idebios: generic IDE device driver (properties in genericProp.h le).
In this chapter, we present the general principles of the interaction between these
dierent components organized into dierent layers which re
ect the hardware
organization of buses and devices and their interactions. More detailed infor-
mation is available in the ChorusOS Device Driver Framework Guide. On-line
manuals for the dierent functions of the APIs are available in section 9 (ddi,
9dki, 9ddi, 9drv...) by using the man command.
CPU bus
DevRegEntry
DrvRegEntry
driver
(serv. ops
routines)
driver global drv data device handle device
registry local device data registry
This structure allows to dene the services which are specic to a hardware de-
vice of a given class:
16.2. The components of the framework 497
the dev class eld species the name of the device class (for instance "bus",
, ,
"isa" "uart" "ide-hba" "ether", , and so on). The value of this eld (to-
gether with the number of the version specied when registering a device) denes
the API implemented by the driver instance;
the dev ops eld points to a structure allowing access to services specic to the
hardware device of the class. The various functions corresponding to these ser-
vices are static functions of the text region and are exported when the device is
registered. The clients may retrieve the functions by invoking svDeviceLookup
and svDeviceEntry calls. The types of the services and their number depend
on the class of devices. For each class of devices, the section 9ddi of the manual
gives the list of the services;
the dev id eld points to the device descriptor which contains all the local
data of the driver instance. The corresponding structure which is opaque to
clients depends on the device (some examples of this type of handle are the
types Smc1660Data, Ne2000Data, Epic100, and so on). This handle is passed
back to the corresponding driver each time a driver service routine is invoked;
the dev node points to the device node (in the device tree).
If we consider the smc1660 ethernet driver, the data of a driver instance are
packed in a Smc1660Data object dened in the smc1660.h le. We extracted
the denitions of some of its elds:
typedef struct Smc1660Data {
DevNode node; /* pointer to the device node in the tree */
char *path; /* absolute pathname of the device node */
int pathSize; /* size of absolute pathname */
DevRegId devRegId; /* Device registry id */
DevRegEntry entry; /* device registry entry */
.....[more elds] ...................
} Smc1660Data;
A driver instance is created when the drv init service routine of the driver
(previously registered in the driver registry) is invoked by the parent bus driver
(for our example, the isa bus driver). We just extracted information directly
related to the device registry service from the smc1660.c driver's source le:
/* prototypes of the Ethernet driver down-call operations */
/* They are retrieved by clients from the device registry */
KnError open(void *devId, void *clientCookie, EtherCallBack *clientOps);
void close (void *devId);
KnError frameTransmit(void *device_id, NetFrame *outFrame);
KnError frameReceive(void *device_id, NetFrame *inFrame);
void promiscuousEnable(void *device_id);
void promiscuousDisable(void *device_id);
void setMulticastAddr (void *devId, uint32_f nb, EtherMcast *mcastAddrs);
498 Chapter 16. The ChorusOS driver framework
/* definition of the object used as dev_ops field for DevRegEntry object */
static EtherDevOps smc1660Ops = {
ETHER_VERSION_INITIAL,
open,
close,
frameTransmit,
frameReceive,
promiscuousEnable,
promiscuousDisable,
setMulticastAddr
};
/* Prototype of release handler called by the device registry */
/* whether special events are signaled */
void relHandler(DevRegEntry *entry);
Imakefile generic.c
ide genericConf.h
idebios
Imakefile
Imakefile Imakefile
rtc
mc146818 mc146818.c
mc146818Prop.h
Imakefile i8254.c
timer
i8254 i8254Prop.h
Imakefile
Imakefile Imakefile
uart ns16550.c
ns16550 ns16550.h
ns16550Prop.h
the dki tree and the ddi tree: they contain header les exporting the DKI and
the DDI sets of APIs:
busywait busywait.h : precise busy wait API
drvreg drvreg.h : driver registry API
devreg devreg.h : device registry API
devtree devtree.h : device tree API
dki intr intr.h : i386at interrupt API
io io.h : specic io operations
ioswap ioswap.h : swapped input/output API
mkalloc mkalloc.h : memory allocation API
thread call.h : DKI thread calls
include/chorus
bus : common 32 bits bus drivers properties
bus.h
busCom busCom.h: bus communication driver interface
dma dma.h : ISA/DMA device driver interface
ide ide.h : IDE bus driver interface
isa isa.h : ISA bus driver interface
ddi ether.h : Ethernet device driver interface
net etherProp.h : Ethernet device driver properties
pci pci.h : PCI bus driver interface
prop.h : common drivers generic properties
rtc rtc.h : RTC device interface
timer timer.h : timer device interface
uart uart.h : UART device driver
vme vme.h: VME bus driver interface
500 Chapter 16. The ChorusOS driver framework
16.2.6 General tools
16.2.6.1 Message logging
Drivers must use the dedicated DKI MSG, DKI ERR, DKI WARN or DKI PANIC macros
to display messages like in the following sequence:
....................
KnError res = svDriverRegister(&smc1660Drv);
if(res != K_OK) {
DKI_ERR(("%s: error -- svDriverRegister( ) failed (%d)\n",
smc1660Drv.drv_name, res));
}
....................
16.2.6.2 An Imakefile
We just give the example of the Imakefile used to build the smc1660 ethernet
driver:
--> pwd
/opt/SUNWconn/SEW/4.0/chorus-x86/src/nucleus/bsp/drv/src/net/ether/smc1660
--> ls
Imakefile smc1660.c smc1660.h smc1660Prop.h
--> cat Imakefile
CSRCS = smc1660.c
OBJS = $(CSRCS:.c=.o)
BuiltinDriver(D_smc1660.r, $(OBJS), $(DRV_LIBS)\
$(NUCLEUS_DIR)/lib/ddi/net.s.a)
DistProgram(D_smc1660.r, $(DRV_DIST_BIN)$(REL_DIR))
Depend($(CSRCS))
DistFile(smc1660Prop.h, $(DRV_DIST_INC)$(REL_DIR))
-->
#include <dki/dki.h>
void dtreeNodeDetach(DevNode node);
If node designates the root node, the device tree is emptied.
16.3.4.3 Nodes properties
A device property is a name/value pair where name is a null terminated ASCII
string and value is a sequence of bytes specied by a length/address pair. It is
important to note that the property value format is property specic.
a) Getting the name of a device node
A call to the primitive
#include <dki/dki.h>
DevProperty dtreePathGet(
DevNode node,
char *buf
);
returns in buf the absolute pathname of a device node. The trailing component
of the pathname is the name of the node (it corresponds to the value of the
node property). If this property does not exist, the trailing path of the returned
pathname is set to "???". A call to the primitive
#include <dki/dki.h>
int dtreePathLeng(DevNode node);
returns the number of bytes to allocate to store the absolute pathname (the
length of the pathname plus one).
If we consider the code of the drv int service routine of a driver (for instance
for the smc1660 driver ethernet), we have the following sequence:
504 Chapter 16. The ChorusOS driver framework
static void drv_init(DevNode myNode, void *bufOps, void *busId) {
int pathSize;
char *path;
................
/* get pathname in the device tree : */
/* 1) get the size; 2) allocate space; 3) get the pathname */
pathSize = dtreePathLength;
path = (char *) svMemAlloc(pathSize);
if (path == NULL) {
DKI_ERR(("%s: error -- no enough memory\n", SMC1660_DRV_NAME));
return;
}
dtreePathGet(myNode, path);
................
b) Getting the properties of a node
The rst property within the list of properties of a device node is searched by
calling
#include <dki/dki.h>
DevProperty dtreePropFind(
DevNode node,
char *name
);
In this call, the function returns the rst property whose name matches the
name parameter, if the name argument is not NULL. It returns the rst property
from the list, regardless of its name; if name is NULL. Once the rst property is
found, a subsequent property in the list may be retrieved by calling
#include <dki/dki.h>
DevProperty dtreePropFindNext(
DevProperty prop,
char *name
);
This call searches the next property within the properties list: the prop param-
eter denes the current position within the properties list. The call returns the
next property following the one specied whose name matches the name string
if name is not NULL, and the next property from the list if name is NULL.
c) Getting attributes of a property
The length of the value of a property is returned by calling
#include <dki/dki.h>
unsigned int dtreePropLength(DevProperty prop);
16.3. DKI common services 505
The name of a given property is returned as an ASCII character string by calling
#include <dki/dki.h>
char *dtreePropName(DevProperty prop);
A pointer to the rst byte of the value of a given property is returned by calling
#include <dki/dki.h>
void *dtreePropValue(DevProperty prop);
The value which is returned may be used by the driver to read or write the
value directly (the pointer would be cast to the appropriate type.
d) Allocating/freeing a property object
A new device property object is allocated by calling
#include <dki/dki.h>
DevProperty dtreePropAlloc(
char *name,
int length
);
In this call, name species the name of the property and length gives the length
of the property value. When returning from a successful call, a non-zero prop-
erty object is returned; its value is undened and it is not attached to any device
node.
A previously allocated property object (which is not attached to any device
node) may be freed by calling
#include <dki/dki.h>
void dtreePropFree(DevProperty prop);
e) Attaching/detaching a property object to a device node
Once it has been allocated and initialized, a property object may be added to
the list of properties of a device node by calling
#include <dki/dki.h>
void dtreePropAttach(
DevNode node,
DevProperty prop
);
Conversely, a property object is detached from the node property list of a device
node by calling
#include <dki/dki.h>
void dtreePropDetach(DevProperty prop);
506 Chapter 16. The ChorusOS driver framework
16.3.4.4 Examples
a) In this rst example, the client application browses the device tree and for
each node it displays:
- its level in the device tree;
- the address (in the kernel address space) of the data structure corresponding
to the node;
- its absolute pathname and the value of the node property;
- the list of its properties: for each property, it displays the name of the property
and the length of its value;
- the code which is produced must be a supervisor binary code.
--> cat $CHORUS/sources/framework/devtree/Imakefile
SRCS=devtree.c
OBJS=devtree.o
SupActorTarget(devtree_s.r, $(OBJS),)
UserActorTarget(devtree_u, $(OBJS),)
Depend($(SRCS))
--> cat $CHORUS/sources/framework/devtree/devtree.c
#include <stdio.h>
#include <chorus.h>
#include <dki/dki.h>
DevProperty devProperty;
main( ){
DevNode devNode = dtreeNodeRoot( );
explore(devNode, 0, "");
}
explore(DevNode devNode, int level, char *str){
char newstr[64];
char pathName[256];
if (devNode != NULL){
printf("%s[level %d]: node's address %p\n", str, level, devNode);
bzero(pathName, 256);
dtreePathGet(devNode, pathName);
printf("%spathname: %s\n", str, pathName);
devProperty = dtreePropFind(devNode, "node");
if(devProperty != NULL)
printf("%snode = %s\n", str, dtreePropValue(devProperty));
properties(devNode, str);
printf("--------------------------------------------------------\n");
strcpy(newstr, str);
strcat(newstr, " ");
explore(dtreeNodeChild(devNode), level + 1, newstr);
explore(dtreeNodePeer(devNode), level, str);
}
}
16.3. DKI common services 507
properties(DevNode devNode, char *str){
int number = 0;
devProperty = dtreePropFind(devNode, NULL);
while(devProperty != NULL){
if(number % 3 == 0)
printf("%s ", str);
printf("%s[%d] ", dtreePropName(devProperty),
dtreePropLength(devProperty));
devProperty = dtreePropFindNext(devProperty, NULL);
if (++number % 3 == 0)
putchar('\n');
}
if(number % 3 != 0)
putchar('\n');
}
--> neon devtree_s.r started aid = 2
node = mc146818
node[9] driver[29] io-regs[8]
intr[12] prof-tick[0] active[0]
--------------------------------------------------------
[level 3]: node's address 0xbb0760
pathname: /pci/pci-isa/ns16550-1
node = ns16550-1
node[10] io-regs[8] intr[12]
dbg-link[0]
--------------------------------------------------------
[level 3]: node's address 0xbb06d8
pathname: /pci/pci-isa/ns16550-2
node = ns16550-2
node[10] io-regs[8] intr[12]
driver[21]
--------------------------------------------------------
[level 3]: node's address 0xbb0640
pathname: /pci/pci-isa/generic-ide
node = generic-ide
node[12] driver[20] active[0]
--------------------------------------------------------
[level 4]: node's address 0xb9d7e0
pathname: /pci/pci-isa/generic-ide/ide0
node = ide0
node[5] ide-port-conf[16]
--------------------------------------------------------
[level 3]: node's address 0xbb05e8
pathname: /pci/pci-isa/smc1660-0
node = smc1660-0
16.3. DKI common services 509
driver[22] node[10] intr[12]
io-regs[8] mem-rgn[8] active[0]
link-throughput[4] ether-addr[6]
--------------------------------------------------------
[level 2]: node's address 0xbb0a48
pathname: /pci/i8259
node = i8259
node[6] driver[18] pic-intr-num[4]
pic-intr-cfg[128] io-regs[32] system-pic[0]
active[0]
--------------------------------------------------------
[level 1]: node's address 0xb9d6c0
pathname: /rd
node = rd
node[3] driver[14] active[0]
--------------------------------------------------------
-->
b) The second example is once more extracted from the code of the smc1660
ethernet driver and more precisely from the code of the drv init service routine.
The io-regs (whose alias is the symbolic constant ISA PROP IO REGS)is used
by the driver to retrieve the structure which denes the rules used for mapping
the registers of an ISA device into an I/O region. The value of this property is
a pointer to a IsaPropIoRegs object which can be used by the io map service
routine of the parent isa bus driver to map the device registers into the I/O
region. This set of operation requires to open a connection between the device
and the bus driver to get an identication on the bus: it is performed by calling
the open service routine of the ISA bus:
static void drv_init(DevNode myNode, void *bufOps, void *busId) {
................
IsaBusOps *isaOps = (IsaBusOps *) busOps;
DevProperty property;
KnError res;
IsaPropIoRegs *ioRegs;
Smc1660Data *smc1660;
................
/* open parent ISA bus to get an identification for the device */
res = isaOps -> open ((IsaId) busId, myNode,
... more arguments,
&smc1660 -> isaDevId);
................
/* map device I/O registers : */
/* 1) get the property; 2) get its value; 3) use the bus service */
property = dtreePropFind(myNode, ISA_PROP_IO_REGS);
ioRegs = (IsaPropIoRegs *) dtreePropValue(property);
510 Chapter 16. The ChorusOS driver framework
res = isaOps -> io_map (smc1660 -> isaDevId, ioRegs,
... [more arguments] ...);
................
}
a node whose name is given is searched in the children's list of the parent node
(name gives the value which must match the value of the node property of
the searched node). The function returns the matching node if any and NULL
otherwise.
c) Allocating and attaching a property
A property may be allocated, initialized and attached to a given node by calling
#include <dki/dki.h>
DevProperty dtreePropAdd(
DevNode node,
char *name,
void *value,
unsigned int length
);
16.3. DKI common services 511
16.3.5 Driver registry
The DKI module implements a data base of drivers currently supported by the
system. A driver should perform a svDeviceRegister when starting. This
data base is accessed by drivers to get the pointer to the driver related to a
given device. The DrvRegEntry type is used here.
16.3.5.1 Registering a driver
A driver entry may be added to the driver registry by calling
#include <dki/dki.h>
KnError svDriverRegister(DrvRegEntry *driverEntry);
NULL is returned if the driver registry is empty. When returning from the call,
the rst entry is locked in the driver registry and cannot be unloaded. It can
be unlocked by calling svDriverRelease or svDriverUnregister.
The entry following a given one in the driver registry is returned by calling
#include <dki/dki.h>
DrvRegId svDriverLookupNext(DrvRegId drvRegId);
In this call, drvRegId must be a value returned by a svDriverLookupFirst
or svDriverLookupNext previous call. If drvRegId does not point to the last
element of the driver registry, a non NULL value is retuned and the entry is
locked. A lock on a given entry is released by calling
#include <dki/dki.h>
void svDriverRelease(DrvRegId drvRegId);
Finally, a driver entry may be removed from the driver registry by calling
#include <dki/dki.h>
KnError svDriverUnregister(DrvRegId drvRegId);
The value K EBUSY is returned if the driver entry is locked in the driver registry
or if an instance of the device driver is locked in the device registry.
16.3.5.3 Example
In this example, we just display the names of all drivers which have been reg-
istered, and for each one the class of its parent and the service routines which
are dened:
--> cat $CHORUS/sources/framework/drvRegistry/drvRegistry.c
#include <stdio.h>
#include <chorus.h>
#include <dki/dki.h>
DrvRegId drvId;
DrvRegEntry *drvEntry;
main( ){
drvId = svDriverLookupFirst( );
if (drvId == NULL){
printf("Driver registry is empty\n");
return(0);
}
printf("%-30s%-15s%s\n", " NAME", "PARENT's CLASS ", " ROUTINES");
while(drvId != NULL){
drvEntry = svDriverEntry(drvId);
printf("%-30s %-11s ",
drvEntry -> drv_name, drvEntry-> bus_class);
if(drvEntry -> drv_probe != NULL) printf(" probe");
if(drvEntry -> drv_bind != NULL) printf(" bind");
if(drvEntry -> drv_init != NULL) printf(" init");
if(drvEntry -> drv_unload != NULL) printf(" unload");
putchar('\n');
drvId = svDriverLookupNext(drvId);
}
}
16.3. DKI common services 513
--> neon drvRegistry_s.r
started aid = 2
NAME PARENT's CLASS ROUTINES
sun:x86-bios-(bus,pci) i386 bind init
sun:pci-enumerator- pci probe unload
sun:pci-i8259-pic pci init
sun:pci-bios-(bus,isa) pci bind init
sun:bus-i8254-timer bus init
sun:bus-mc146818-(rtc,timer) bus init
bindonly bus probe bind init
sun:pci-epic100-ether pci bind init unload
sun:pci-dec21x4x-ether pci bind init unload
sun:bus-el3-ether bus init unload
sun:isa-smc1660-ether isa init unload
sun:bus-ne2000-ether bus init
sun:bus-ne2000-ether pci bind
sun:dki-sram-flash common init unload
sun:bus-generic-ide bus init unload
sun:ram--disk common probe init
-->
a client requests for a device entry in the device registry matching specied
device class and logical unit. In the call:
- devClass is the device class;
- devVersion is the minimum device driver version required;
- devUnit is the logical unit in the device class;
- clientHandler is the event handler which is called when a device event is
signaled;
- clientCookie denes the rst parameter of clientHandler;
- clientId is an output parameter which will be used to identify the client token
for the matching entry when calling svDeviceEntry or svDeviceRelease.
If the call succeeds, the call returns K OK and the corresponding entry in the
device registry is locked until a subsequent svDeviceRelease call (this lock may
not be exclusive whether it has been specied multi-clients when allocating it
(svDeviceAlloc).
If the call fails, a negative error code is returned:
return value interpretation
K EBUSY the entry matching devClass and devUnit has been found
but it is single client and it is already locked by another client
K EUNKNOWN no device entry matching devClass
K EUNDEF no device entry matching devClass within devUnit but there is one
with a greater unit value
or there is an entry matching devClass and devUnit but devVersion
is greater than the provided one
K ETOOMUCH no entry matching devUnit in devClass
K ENOMEM no enough space
b) Getting the device driver instance associated with a client
When a client calls
#include <dki/dki.h>
DevRegEntry *svDeviceEntry(DevClientId clientId);
516 Chapter 16. The ChorusOS driver framework
it gets a pointer to the DevRegEntry object corresponding to the driver instance
previously locked by the client: clientId should have been returned by a previous
call to svDeviceLookup.
c) Releasing a lock on device entry
When calling
#include <dki/dki.h>
void svDeviceRelease(DevClientId clientId);
a client releases the lock on the device entry specied by clientId.
#include <dki/dki.h>
void usecBusyWait(unsigned int micro);
the calling thread busy waits for at least micro micro-seconds (micro must be
in the range [1:1000]).
16.3.7.3 System event management
This service is provided to the lowest-layer level drivers. They allow event han-
dlers to be registered for all running drivers: typically a reboot will propagate
a specic event from the micro-kernel to the lowest-layer level drivers which
recursively propagate this event to the upper-layer drivers by calling their event
handler.
When a lowest-layer level driver establishes a connection between the device
driver and the DKI module by calling svDkiOpen, one argument of the call
corresponds to the device driver handler which is invoked by the DKI when an
event occurs. Such a connection is released by calling svDkiClose.
The svDkiEvent call is used to start propagating a given event to the device
driver hierarchy.
16.3.7.4 Global interrupt masking
These services may be used by drivers to protect critical sections from inter-
rupts.
The imsIntrMask f function masks all maskable interrupts at processor level.
It also increments the imsIntrMaskCount f kernel variable.
The imsIntrUnmask f function rst decrements the imsIntrMaskCount f ker-
nel variable. It unmasks interrupts at processor level, if imsIntrMaskCount f
becomes equal to zero.
16.3.7.5 Thread preemption disabling
This service allows a driver to disable (or enable) the preemption of the current
thread: two macros ENABLE PREEMPT() and DISABLE PREEMPT() are provided.
518 Chapter 16. The ChorusOS driver framework
16.4 DKI specic services
16.4.1 Specic input/output services
Various optimized routines are available to handle byte swapping: they should
be used whenever a host bus driver needs to handle dierent byte ordering
between buses (for each function the sux xx gives the the bit length of the
data on which the service applies:
uint16 f loadSwap 16(uint16 f *addr); loads data from address addr
int32 f loadSwap 32(uint32 f *addr); & returns the corresponding
uint64 f loadSwap 64(uint64 f *addr); byte swapped value
void storeSwap 16(uint16 f *addr, uint16 f value); stores at the address
void storeSwap 32(uint32 f *addr, uint32 f value); addr the value
void storeSwap 64(uint64 f *addr, uint64 f value); swapped
void swap 16(uint16 f *addr); swaps in place the bytes of
void swap 32(uint32 f *addr); the data stored at
void swap 64(uint64 f *addr); the addr address
16.4.2 Processor family specic services
These are dened and available only for a given processor family and are used
only by the low-level drivers (for example those for buses and devices directly
connected to the processor bus). The types of services provided depend on the
processor family. They may concern:
interrupt management: a driver may perform the following operations:
- attach or detach a handler to a given interrupt;
- mask or unmask an interrupt attached to a handler;
cache management: this service allows the host bus to
ush or invalidate
caches;
specic input/output operation: this provides an interface to processor-specic
I/O (ports or memory mapped operations).
16.5 Device Driver Interface (DDI)
16.5.1 Introduction
This denes several layers of APIs between components: a specic API is dened
for each class of bus or device. These APIs may be divided into main classes:
bus drivers APIs: they are built upon the DKI services and are used when
building the lower layers of drivers components. Every bus is a particular device
which has its own characteristics: a bus (bridge) driver is associated to a bus.
It exports an API which allows development of device drivers for that kind
of bus regardless of the platform. Nevertheless, the APIs of a particular class
16.5. Device Driver Interface (DDI) 519
of buses is specic to that class. But, in order to allow the development of
multi-bus device drivers, a subset of the APIs of some classes of bus drivers
(presently PCI, ISA/EISA and VME buses) has been abstracted: this subset
constitutes the Common Bus Driver Interface. Using only services provided
by the Common Bus Driver Interface when developing a device driver leads to
multi-bus drivers (working for buses for which this subset is provided);
device drivers APIs: they are typically built upon the the bus driver APIs. A
specic API is provided for every class of devices (for instance UART, TIMER,
RTC or ETHERNET).
16.5.2 Common bus services
The services provided to the device drivers through this API (which is presently
supported by the PCI, ISA and VME buses) are:
the bus/device driver connection's establishment;
the management of interrupts: handlers' connection and disconnection, han-
dlers' invocation and interrupts enabling and disabling);
access to input/output registers: mapping and unmapping of I/O registers;
memory mapping of regions.
We now quickly present the services provided by this restricted API whose
interface is dened in the ddi/bus/bus.h.
16.5.3 Common bus properties
Name Property Value's format/type
BUS PROP INTR interrupt source bus class specic
BUS PROP IO REGS input/output registers bus class specic
BUS PROP MEM RGN memory region bus class specic
BUS PROP DMA BURST DMA burst sizes BusPropDmaBurst
BUS PROP MIN SIZE minimal DMA size BusPropDmaMinSize
BUS PROP BYTE ORDER bus byte order constants
BUS BYTE ORDER BIG
orBUS BYTE ORDER LITTLE
BUS PROP CLOCK FREQ Bus clock frequency BusPropClockFreq
As it can be seen in the previous table, the format of the value of the rst three
properties ("intr", "io-regs" and"mem-rgn") is specic to the bus class: the
common bus API allows a driver to get a pointer to the value of a property
without knowing the format of the value
16.5.4 Common bus driver events
Events drivers are associated to the following events which may be delivered to
device drivers by the bus driver:
520 Chapter 16. The ChorusOS driver framework
BUS SYS SHUTDOWN: it noties a driver that the system is going to be shutdown;
BUS DEV SHUTDOWN: it noties a driver that the device should be shutdown
(the device driver should notify driver clients);
BUS DEV REMOVAL: it noties that a device has been removed from the bus.
The BusEvent type is dened as the enumeration of these three values.
16.5.5 Handlers
Four kinds of handlers may be dened by drivers within the common bus driver
API. They are invoked by the bus driver (upcalls) whenever the corresponding
event occurs. They correspond to the following:
a) Interrupt handlers
This type of handler is called when an interrupt occurs and they correspond to
the BusIntrHandler type:
typedef enum {
BUS_INTR_UNCLAIMED = 0, /* unclaimed interrupt */
BUS_INTR_CLAIMED, /* claimed interrupt */
BUS_INTR_ACKNOWLEDGED /* claimed and acknowledged interrupt */
} BusIntrStatus;
typedef BusIntrStatus (*BusIntrHandler) (void *devIntrCookie);
As we will see, an interrupt handler can be connected to a given interrupt source
by using the intr attach service routine (the devIntrCookie argument of the
handler is dened when the handler is installed);
b) Event handlers
This type of handler is called when an event of the type dened in 16.5.4 occurs
and they correspond to the BusEventHandler type:
typedef KnError (*BusEventHandler) (
void *devEventCookie,
BusEvent busEvent,
void *arg );
In a call,
- the value of the devEventCookie argument is a cookie value specied when
installing the handler;
- the busEvent argument corresponds to the bus event which occurred;
- arg points to a structure specic to the event.
c) Load handlers
This type of handler is called when a new driver is dynamically loaded. It has
a sole argument which is a cookie dened when installing the handler:
typedef void (*BusLoadHandler) (void *devLoadCookie);
16.5. Device Driver Interface (DDI) 521
d) Error handlers
This type of handler is called by the bus driver whenever either an I/O or a
memory access is aborted because of an error. Its rst argument is a cookie
which is dened when installing the handler and the second argument is a
pointer to a BusError object giving information about the fault:
typedef enum {
BUS_ERR_UNKNOWN /* unknown bus error */
BUS_ERR_INVALID_SIZE /* invalid access size */
} BusErrorCode;
typedef struct {
BusErrorCode code; /* error type */
BusSize offset; /* faulted address offset within region */
} BusError;
typedef void (*BusErrHandler) (
void *devErrCookie,
BusError busError,
void *arg );
16.5.6 Common bus driver service routines
The BusOps structure denes the dierent service routines of the API and it
has the following eld:
a) open: open the connection to a given bus driver
This is the rst operation that a device driver must issue to the bus driver: it
establishes a connection between the device specied by its device node devNode
and the bus driver identied by the bus driver instance busId:
KnError (*open)(
BusId busId,
DevNode devNode,
BusEventHandler devEventHandler,
BusLoadHandler devLoadHandler,
void *devCookie,
BusDevId *devIdent );
In that call,
- devEventHandler species the handler which will be invoked by the bus driver
when an event occurs;
- devLoadHandler species the load handler associated to the driver. It is not
NULL only for bus drivers supporting dynamic loadable device drivers;
- the devCookie argument species the rst argument passed when calling de-
vEventHandler or devLoadHandler.
Upon success, the function returns K OK and an opaque identication is returned
by the bus driver and written at devIdent address. This identier will be used
in subsequent calls issued to the bus driver. If an error occurs the call returns
a negative error code.
522 Chapter 16. The ChorusOS driver framework
b) close: close a bus connection
It is the last call which is issued to the bus driver by the device driver:
void (*close)(BusDevId devIdent);
c) intr attach and intr find: connect an interrupt handler to an interrupt source
An interrupt handler is connected to a given interrupt source by calling the
function
KnError (*intr attach)(
BusDevId devIdent,
void *devIntrSource,
BusIntrHandler devIntrHandler,
void *devIntrCookie,
BusIntrOps **intrOps,
BusIntrId *intrIdent
);
- devIdent identies the device (its value has been set by a previous open call);
- devIntrSource is the value of the "intr" resource: it is extracted from the
value of the "intr" property which is returned by the device tree interface as
a pointer to an array of descriptors (thus, this value may specify a number of
interrupt lines). The common bus driver interface provides a service routine
which allows to get the value of one element of that array without knowing
the format of its value. It corresponds to the intr find eld in the BusOps
structure:
void *(*intr find)(
void *propertyVector,
int index
);
- devIntrHandler is the driver to connect and devIntrCookie its rst parameter
when it is called;
- the intrOps array is returned by the call and it specied methods which allow
enabling or disabling interrupt for the given source;
- the value intrIdent returned by the call is used to identify the interrupt source
in the intr detach call and method invocations.
d) intr detach: disconnect a given interrupt handler from its interrupt source
When calling the function
void (*intr detach)(BusIntrId intrIdent);
the handler connected to the interrupt source identied by intrIdent (previously
returned when calling intr attach) is disconnected.
e) io map and io regs find: map an i/o range within bus space
A contiguous I/O region can be mapped to the supervisor address space by
calling
16.5. Device Driver Interface (DDI) 523
KnError (*io map)(
BusDevId devIdent,
void *ioRegs,
BusErrHandler devErrHandler,
void *errCookie,
BusIoOps **ioOps,
BusIoId *ioIdent
);
- devIdent identies the device (its value has been set by a previous open call);
- ioregs species a contiguous set of i/o registers which should have been ob-
tained from the array associated to the "io-regs" property of the device within
the device tree by using the io regs find service routine
void *(*io regs find) (
void *propertyVector,
int index
);
- devErrHandler is an error handler and devErrCookie its rst parameter when
it is called;
- the ioOps array is returned by the call and it specied methods for the mapped
region;
- the value ioIdent returned by the call is used to identify the interrupt source
in the io unmap call and methods invocations.
f) io unmap: unmap an i/o range previously mapped
When calling the function
void (*io unmap)(BusIoId ioIdent);
the region whose identier is ioIdent, previously returned when mapping it with
io map, is unmapped.
g) mem map and mem rgn find: map a memory region within bus space
A memory region may be mapped from a device to the supervisor address space
by calling
KnError (*mem map)(
BusDevId devIdent,
void *memRgn,
BusMemAttr memAttr,
BusErrHandler errHandler,
void *errCookie,
void **memAddr,
BusMemId *memIdent );
The memRgn argument points to an element of the array returned from the
device tree and corresponding to the memory region resource property whose
524 Chapter 16. The ChorusOS driver framework
name is "mem-rgn". This element can be obtained by calling
void *(*mem rgn find) (
void *propertyVector,
int index );
525
526 Appendix A. System calls error codes
Symbolic value Decimal value Message
K EABORTRPC -18 Aborted RPC notification
K EMAPPER -19 Mapper access error
K ENOEMPTY -20 Non empty address space
K EOFFSET -21 Bad segment offset
K EOVERLAP -22 Region overlapping
K EPROT -23 Region access rights violation
K ESPACE -24 Address space violation
K EUNDEF -25 Undefined default mapper
K EARGS -26 Arguments inconsistency
K EBADORDER -27 Bad restart order
K EBUSYPORT -28 Port in use
K ELINKFAILURE -29 Link failure during remote IPC
K EBADMSG -30 Invalid message identifier
K ENOTAVAILABLE -31 System call not available
-32 Unknown Chorus Nucleus error
Specic positive error values are used for the primitives related to the private
data of actors and threads:
Symbolic value Decimal value Error type
PD ENOKEY 1 no more keys available
PD ENOMEM 2 no more memory available
PD EINVAL 3 invalid argument
PD ESERVER 4 cannot connect to server
PD EUSER 5 called from user mode
Finally, when using the API provided by the actor manager and Posix functions,
an invalid call returns -1 and the external errno variable is set to a value
corresponding to the error encountered. The standard function
void perror(char *personal message);
prints the personal message and a standard message corresponding to the cur-
rent value of errno (remember that the value of errno is not reset when a
call succeeds and therefore, its value is only signicant after a failed call). The
standard le <errno.h> should be included when using this facility.
Appendix B
Index and list of predened
constants
We rst give the list of the predened constants dened in the kernel's interface:
the constants are presented in dierent groups corresponding to the categories
of objects that are associated with them.
a) Actors
Symbolic name Type Interpretation Page
K ACTIVE int active state for the actor 127
K ACTORNAMEMAX int maximum length of actors' name 99
K MYACTOR KnCap * current actor 99
K STOPPED int stopped state for the actor 127
K SUPACTOR int supervisor actor 101
K SYSTEMACTOR int system actor 101
K USERACTOR int user actor 101
b) Threads
Symbolic name Type Interpretation Page
K ACTIVE int active state for the actor 211
K ALLACTORTHREADS KnThreadLid all actor's threads 237
K CURRCTX int thread's hardware context 239
K DEFAULT STACK SIZE int system stack's default size 212
K DEFAULT START INFO int default type of thread's initial state 212
K INACTIVE int inactive state for the actor 211
K MYSELF KnThreadLid current thread 198
K SOFTCTX int thread's software context 239
K SUPTHREAD int supervisor thread 212
K THREADNAMEMAX int maximum length of thread's name 198
K USERTHREAD int user thread 212
527
528 Appendix B. Index and list of predened constants
c) Memory, regions, address space
Symbolic name Type Interpretation Page
K ANYWHERE int region's start address not specied 149
K COPYBACK int executable region 466
K DESTROY int executable region 467
K EXECUTABLE int executable region 150
K FILLZERO int region is lled with 0 149
K FREEALL int all regions in an actor 167
K GETATTR int attribute in a MpGetAccess request to get 472
additional information
K INHERITCOPY int region is copied when duplicated 454
K INHERITSHARE int shared region when duplicated 454
K NOACCESS int invalidate data when
ushing 467
K NODEMAND int no page demand for a region 453
K NOSWAPOUT int no swap for a region 453
K NOWAITFORMEMORY int don't wait if no available memory 183
K PAGEFAULT int operation due to a page fault 467
K READABLE int readable region (value is 0) 149
K RESTRICTIVE int address in a given range 149
K SUPERVISOR int access only for supervisor threads 150
K SVACTOR KnCap * supervisor address space 152
K WRITABLE int writable region 150
KN MPCREATE int service is a request to create an object 470
KN MPGETACCESS int service is a request to get access rights 471
KN MPIN int service is a request to update local cache 472
from segment
KN MPOUT int service is a request to update segment 473
from local cache
KN MPRELEASE int service is a request to release an object 471
d) Events
Symbolic name Type Interpretation Page
K EVENT AND int all events in a set 269
K EVENT OR int at least on one event in set 269
e) Scheduling
Symbolic name Type Interpretation Page
K FIFO PRIOMAX int maximum priority for FIFO scheduling 201
K FIFO PRIOMIN int minimum priority for FIFO scheduling 201
K PRIOMIN int minimum priority 200
K PRIOMAX int maximum priority 200
K RR PRIOMAX int maximum priority for RR scheduling 201
K RR PRIOMIN int minimum priority for RR scheduling 201
K SCHED DEFAULT int default (FIFO) scheduling class 202
K SCHED FIFO int FIFO scheduling class 201
K SCHED RR int round robin scheduling class 201
529
f) Time, delays
Symbolic name Type Interpretation Page
K ABORTABLE int abortable call 382
K CLOCK REALTIME int real time clock 326
K NOBLOCK KnTimeVal * null delay 208
K NODELAY int operation with no nite delay 387
K NOTIMEOUT KnTimeVal * abortable innite delay 208
K NOTIMEOUT NOABORT KnTimeVal * not abortable innite delay 208
K TIMER ABSOLUTE int absolute time 328
K TIMER INTERVAL int relative time 328
K VTIME INTERNAL int just consider internal time 334
K VTIME TOTAL int consider all execution time 334
g) Laps
Symbolic name Type Interpretation Page
K CONNECTED LAP KnLapDesc * current connected lap 418
K LAP NOBLOCK KnTimeVal * non blocking name resolution 312
K LAP PROTECTED int protected name resolution 304
K LAP SAFE int lap in safe mode 305
K LAPNAMEMAX int max lap name's length 304
h) Communication
Symbolic name Type Interpretation Page
K ANY MSGPOOL unsigned int any message in a pool 360
K ANYENABLED int any enabled port 385
K BROADMODE int broadcast communication 381
K CMSGANNEXSIZE int size of message's annex 380
K CMSGSAVEDMAX int max. number of saved messages 400
K CMSGSIZEMAX int max. size of message's body 381
K CPORTQUEUE int max. number of messages in a port queue 373
K DEFAULTPORT int default port local identier 372
K DYNAMIC int dynamic ports group (no stamp) 378
K FUNCMODE int functional mode of communication 381
K FUNCUMODE int associative functional mode 381
K FUNCXMODE int exclusive functional mode 381
K KILLMSGS int migrating a port without messages 409
K MSG POOLMAX int max. number of pools in a message space 356
K MSG PRIOMAX int max. priority of message in a msg space 360
K PRIVATEID KnMsgSpaceId private key for a message pool 357
K STATICSYS int static system port group 378
K STATUSER int static ports group (with stamp) 378
K USERANNEX int message annex in current actor's 380
address space
K USERBODY int message body in current actor's 380
address space
K WITHMSGS int migrating a port with messages 409
530 Appendix B. Index and list of predened constants
i) Handlers
531
532 Appendix C. Index and list of predened types
type's name interpretation Page
KnMsgDesc message descriptor 380
KnMsgHead header's descriptor of a message 398
KnMsgHdl message handler 402
KnMsgPool descriptor of a message pool 356
KnMsgSpaceId message space identication 357
KnMutex mutex (mutual exclusion semaphore) 257
KnObjDesc object's descriptor 461
KnPc entry point 121
KnPdHdl private data destructor handler 285
KnPortLid port's local identier 372
KnRgnDesc region's descriptor 148
KnRgnStat region's status (when calling rgnStat) 155
KnRrThParms round robin scheduling attributes 202
KnRtMutex real time mutex 260
KnSem general semaphore 261
KnSysTrapDesc parameter of a trap handler 439
KnThreadCtx thread's registers context 239
KnThreadDefaultSched generic scheduling attributes 203
KnThreadLid thread's local identier 198
KnThreadPool descriptor of a threads pool 326
KnThreadPriority thread's priority 203
KnThreadPrivilege thread's privilege 212
KnThreadSoftCtx software thread's context 239
KnThreadStat thread's descriptor 235
KnThreadStatus thread's status 211
KnThSem thread semaphore 252
KnTimeout timeout 446
KnTimeVal time interval 321
KnUniqueId unique identier 84
KnVirtTimeout virtual timeout 334
KnVmPar memory management parameters 452
KnVmStat memory statistics 142
pdKey key of private data 285
PhAddr physical address 151
VmAddr virtual address 99
VmFlags virtual memory
ags 148
VmSize region's size 148
Appendix D
Index and list of primitives
We list here the dierent primitives of the interface and for each of these prim-
itives we specify the feature which provides it: MEM* must be interpreted as
any memory memory management model and CORE as the executive.
Primitive Features Page
,
exit exit ACTOR EXTENDED MNGT 131
acap ACTOR EXTENDED MNGT 100
acreate ACTOR EXTENDED MNGT 118
acred ACTOR EXTENDED MNGT 110
actorCreate CORE 126
actorDelete CORE 129
actorName CORE 99
actorPrivilege CORE 102
actorSelf CORE 99
actorStart CORE 101
actorStat CORE 105
actorStop CORE 101
afexecl afexecle afexeclp ACTOR EXTENDED MNGT 117
afexecv afexecve afexecvp ACTOR EXTENDED MNGT 112
agetalparam ACTOR EXTENDED MNGT 123
agetId ACTOR EXTENDED MNGT 100
akill ACTOR EXTENDED MNGT 131
aload ACTOR EXTENDED MNGT 120
alParamBuild ACTOR EXTENDED MNGT 123
alParamSize ACTOR EXTENDED MNGT 123
alParamUnpack ACTOR EXTENDED MNGT 123
astart ACTOR EXTENDED MNGT 125
astat ACTOR EXTENDED MNGT 107
await ACTOR EXTENDED MNGT 133
awaits ACTOR EXTENDED MNGT 132
eventClear EVENT 269
eventInit EVENT 267
eventPost EVENT 268
533
534 Appendix D. Index and list of primitives
Primitive Features Page
eventWait EVENT 268
ftruncate POSIX SHM 193
grpAllocate IPC 377
grpPortInsert IPC 378
grpPortRemove IPC 379
ipcCall IPC 393
ipcGetData IPC 399
ipcReceive IPC 384
ipcReply IPC 386
ipcRestore IPC 400
ipcSave IPC 400
ipcSend IPC 382
ipcSysInfo IPC 399
ipcTarget IPC 381
lapDescDup CORE 303
lapDescIsZero CORE 302
lapDescZero CORE 302
lapInvoke CORE 312
lapResolve LAPBIND 312
lcCap MEM VIRTUAL 465
lcClose MEM VIRTUAL 465
lcFillZero MEM VIRTUAL 468
lcFlush MEM VIRTUAL 467
lcOpen MEM VIRTUAL 465
lcRead MEM VIRTUAL 468
lcSetRights MEM VIRTUAL 467
lcStat MEM VIRTUAL 466
lcWrite MEM VIRTUAL 469
mmap POSIX SHM 193
msgAllocate MIPC 359
msgFree MIPC 362
msgGet MIPC 361
msgPut MIPC 360
msgRemove MIPC 362
msgSpaceCreate MIPC 357
msgSpaceOpen MIPC 358
munmap POSIX SHM 194
mutexGet CORE 257
mutexInit CORE 257
mutexRel CORE 257
mutexTry CORE 257
padGet CORE 292
padKeyCreate CORE 291
padKeyDelete CORE 291
padSet CORE 292
535
Primitive Features Page
portCreate IPC 374
portDeclare IPC 375
portDelete IPC 376
portDisable IPC 374
portEnable IPC 374
portLi IPC 372
portMigrate IPC 408
portUi IPC 372
ptdErrnoAddr CORE 282
ptdGet CORE 287
ptdKeyCreate CORE 285
ptdKeyDelete CORE 286
ptdRemoteGet CORE 287
ptdRemoteSet CORE 286
ptdSet CORE 286
ptdThreadDelete CORE 287
ptdThreadId CORE 284
rgnAllocate MEM* 158
rgnDup MEM VIRTUAL 456
rgnFree MEM* 167
rgnInit MEM VIRTUAL 462
rgnInitFromActor MEM PROTECTED MEM VIRTUAL 178
rgnMap MEM VIRTUAL 463
rgnMapFromActor MEM* 171
rgnSetInherit MEM VIRTUAL 454
rgnSetOpaque MEM VIRTUAL 454
rgnSetPaging MEM VIRTUAL 454
rgnSetProtect MEM* 150
rgnStat MEM* 154
rtMutexGet RTMUTEX 260
rtMutexInit RTMUTEX 260
rtMutexRel RTMUTEX 260
rtMutexTry RTMUTEX 260
semInit SEM 261
semP SEM 262
semV SEM 262
sgFlush MEM VIRTUAL 467
sgRead MEM VIRTUAL 464
sgStat MEM VIRTUAL 466
sgWrite MEM VIRTUAL 465
shm open POSIX SHM 192
shm unlink POSIX SHM 192
svActorAbortHandlerConnect CORE 417
svActorAbortHandlerDisconnect CORE 418
svActorAbortHandlerGetConnected CORE 417
536 Appendix D. Index and list of primitives
Primitive Features Page
svActorExcHandlerConnect CORE 423
svActorExcHandlerDisconnect CORE 424
svActorExcHandlerGetConnected CORE 423
svActorVirualTimeoutCancel VTIMER 337
svActorVirualTimeoutSet VTIMER 335
svCopyIn MEM* 301
svCopyInString MEM* 302
svCopyOut MEM* 302
svGetInvoker CORE 305
svLapBind LAPBIND 304
svLapCreate CORE 303
svLapDelete CORE 305
svLapUnbind LAPBIND 304
svMemRead MEM* 187
svMemWrite MEM* 188
svMsgHandler IPC 403
svMsgHdlReply IPC 403
svPagesAllocate MEM* 183
svPagesFree MEM* 184
svSysTimeoutCancel CORE 447
svSysTimeoutGetRes CORE 447
svSysTimeoutSet CORE 446
svSysTrapHandlerConnect CORE 440
svSysTrapHandlerDisconnect CORE 440
svSysTrapHandlerGetConnected CORE 441
svThreadVirtualTimeoutCancel VTIMER 337
svThreadVirtualTimeoutSet VTIMER 336
svTrapHandlerConnect CORE 440
svTrapHandlerDisconnect CORE 440
svTrapHandlerGetConnected CORE 441
svVirtualTimeoutCancel VTIMER 336
svVirtualTimeoutSet VTIMER 334
sysGetConf CORE 89
sysGetEnv ENV 90
sysPoll CORE 94
sysRead CORE 93
sysReboot CORE 95
sysSetEnv ENV 91
sysTime CORE 322
sysTimeGetRes CORE 322
sysUnsetEnv ENV 91
sysWrite CORE 93
threadAbort CORE 232
threadAborted CORE 232
537
Primitive Features Page
threadActivate CORE 229
threadContext CORE 239
threadCreate CORE 211
threadDelay CORE 208
threadDelete CORE 226
threadName CORE 199
threadScheduler CORE 203
threadSelf CORE 198
threadSemInit CORE 252
threadSemPost CORE 253
threadSemWait CORE 253
threadStart CORE 230
threadStat CORE 235
threadStop CORE 230
threadTimes VTIMER 237
timerCreate TIMER 326
timerDelete TIMER 327
timerSet TIMER 328
timerThreadPoolInit TIMER 326
timerThreadPoolWait TIMER 328
uiBuild IPC 85
uiClear IPC 84
uiEqual IPC 84
uiGetSite IPC 85
uiIsLocal IPC 85
uiLocalSite IPC 87
uiSite IPC 85
uiValid IPC 84
univTime DATE 323
univTimeAdjust DATE 324
univTimeGetRes DATE 323
univTimeSet DATE 324
virtualTimeGetRes DATE 333
vmPageSize MEM* 141
vmPhysAddr MEM PROTECTED MEM VIRTUAL 151
vmSetPar MEM* 453
vmStat MEM* 142
Bibliography
G.R. Andrews
"Concurrent Programming: principles and practice "
The Benjamin/Cummings Publishing Company Inc, 1991
M. Bach
"The design of the Unix Operating System"
Prentice Hall, 1986
K. Bostic, M.J. Karels, M.K. McKusick and J.S. Quaterman
"The design and implementation of the 4.4 BSD UNIX operating system"
Addison-Wesley, 1996
J. Boykin, D. Kirschen, A. Langerman and S. LoVerso
"Programming under Mach"
Addison Wesley, 1993
D. Buttlar, B. Nichols and J. Proulx Farrel
"Pthreads Programming"
O'Reilly and Associates, Inc, 1996
ChorusOS 4.0 Device Driver Framework Guide
Sun Microsystems, 1999
ChorusOS 4.0 Installation guide
Sun Microsystems, 1999
ChorusOS 4.0 Programmer's Reference Manual
Sun Microsystems, 1999
ChorusOS 4.0 User's Reference Manual
Sun Microsystems, 1999
539
540 Bibliography
G. Coulouris, J. Dollimore and T. Kindberg
"Distributed systems", 2nd edition
Addison Wesley, 1992
P. DuBois
"Software Portability with imake", 2nd edition
O'Reilly and Associates, Inc, 1996
Intel
"Intel Microprocessors"
Intel Literature, 1990
S.J. Leer, M.K. McKusick, M.J. Karels and J.S. Quaterman
"The design and implementation of the 4.3 BSD UNIX operating system"
Addison-Wesley, 1989
Sun Embedded Workshop 4.0. Getting started guide
Sun Microsystems, 1999
Tanenbaum A.
"Distributed Operating Systems"
Prentice Hall, 1995
Index
/etc/ethers le, 36 arun command, 49, 50, 56, 57, 97, 108, 115,
/etc/inetd.conf le, 37 130, 195, 489
/etc/security le, 43 asynchronous communication, 382{384, 389
/tftpboot directory, 38
rc.chorus les, 44 barrier, 270{276
/tftpboot directory, 370 basic prole, 29
boot monitor, 35
booting ChorusOS, 34{36, 95
abort handlers, 126, 196, 231, 414{421 broadcast, 377, 381{383, 389
aborted state, 231, 232, 234 building an image, 24{26, 344, 349, 368,
aborting a thread, 230, 232{234, 414, 437 370
abstractions, 6{8 building le system, 26
activating a c actor, 125 building libraries, 74{76
actor manager, see AM actor busy loop, 237
actors, 6, 50, 51, 97{138, see c actors
activating, 125 c actors, 11, 49, 98{100, 106{109, 112, 118,
address space, 54, 59, 97, 144 195, see actors
attributes, 51, 54, 58, 59, 99{104 address space, 144
creating, 108{129, 148, 241{246 attributes, 109, 112, 120
default port, 97, 371{373, 383, 384 creating, 109
deleting, 129{132, 148 credentials, 110
identication, 58, 99 le context, 50, 98, 109
private data, 291{296 listing, 48, 107
privilege, 98, 101, 126, 127 loading, 120
starting, 101, 103 main thread, 49, 98, 110, 112
memory organization, 455
states, 100, 101, 127 spawning, 108, 110
stopping, 101 C INIT actor, 11, 16, 17, 39, 41{45, 54,
symbolic name, 51, 99 115, 130, 153, 205
type, 51, 97, 98 capabilities, 6, 58, 74, 86, 87, 99, 377, 378
address space, 6, 13, 97, 242 ChorusOS
ADMIN actor, 10, 11, 26 abstractions, 6{8
administration les, 28 administration les, 28
administration tools, 16 architecture, 8{12
akill command, 59, 108, 129{132, 222, 229, features, 9, 12
234 nucleus layer, 9
allocating memory, 158, 159, 161{166 OS layer, 10
AM actor, 11, 98, 106, 108, 111, 118 r3.2, 10
aps command, 48, 58 ChorusOS driver framework, 489{524
541
542 Index
ChorusOSMkMf command, 64, 71{73, 76, writing, 80
77, 445
chorusStat command, 17 empty actor, 108, 118, 119, 123, 126, 132,
communications, 355{409 242
conguration les, 28{30 environment, 33, 46, 90{92, 111, 115
conguration tools, 30{32 errno variable, 282, 526
congurator command, 26, 30{33, 344, 349, errors, 66, 525, 526
368{370 Ethernet, 4, 15, 34, 35, 369, 370
congure utility, 24, 25 event sets, 14, 267{276
conguring host system, 36, 37 ews command, 26, 30
console, 16, 38, 93, 94 exception handlers, 126, 196, 414, 421{436
consuming abortion, 231, 232 exceptions, 413, 421
copying data, 301, 302, 306, 442 execution actor, 195, 198, 231, 300, 309,
copying regions, 242 312, 316, 317, 420, 438, 440
core executive, 9 execution privilege, 127, 438
creating execution times, 237, 238
actors, 49, 108{129 extended actors, see c actors
threads, 197, 211{213, 215{221, 235, extended prole, 29
247
creating actors, 108 features, 6, 9, 12{17, 26, 31, 32
credentials of a c actor, 41 ACTOR EXTENDED MNGT, 12, 98, 108, 355
cross development environment, 5 ADMIN CHORUSSTAT, 17
cs command, 50{54, 58, 59, 103, 104, 128, ADMIN IFCONFIG, 17
129, 144, 229, 234, 261, 272, 317, ADMIN MOUNT, 17
322, 373, 390, 391, 437, 489 ADMIN NETSTAT, 17
current actor, 98, 99 ADMIN RARP, 17
current message, 380, 386, 398{400 ADMIN SHUTDOWN, 17
AF LOCAL, 16
debugger BPF, 16
kdb, 60 DATE, 13, 321, 323
XRAY, 15 DEBUG APPLI, 12, 15
default port, 97, 371{373, 383, 384 DEBUG SYSTEM, 15
delaying a thread, 73, 208 DEV MEM, 16
deleting DYNAMIC LIB, 12
actors, 59, 129 EVENT, 14, 251, 267
laps, 305 FIFOFS, 15
ports, 375 FLASH, 16
threads, 197, 225{229 IDE DISK, 16
demand paging, 13 IPC REMOTE COMM, 369
distributed ipc, 15, 355 IPC REMOTE, 15, 369, 390, 391
drivers, 10, 489{524 IPC, 14, 97, 355, 368
duplicating address space, 170, 242, 456 LAPBIND, 14, 299, 304
dynamic libraries, 12, 78{80 LAPSAFE, 14, 299
building, 78 LOCAL CONSOLE, 16
using, 79, 80 LOG, 15
dynamic programs, 80{82 MIPC, 14, 355, 356
running, 82 MSDOSFS, 15
Index 543
NFS CLIENT , 15 IP address, 36, 37, 42, 59, 369, 370
NFS SERVER , 15
ON DEMAND PAGING , 13, 139, 449 KERN actor, 9, 205
PERF , 14 kernel debugger (kdb), 60, 61
POSIX MQ , 15 killing c actors, 59, 131, 222, 233, 272
POSIX SHM , 15, 229 laps, 14, 195, 196, 198, 231, 299{319, 414,
POSIX SOCKETS , 16 441, 447
PPP , 16 creating, 303, 431
RAM DISK , 16 deleting, 304, 305
RESTART , 17 descriptors, 302
ROUND ROBIN , 13, 200, 349 handlers, 299, 305, 308{313, 315{319
RSH , 16 installing, 305, 306
RTC , 14 resolving name, 312
RTMUTEX , 14, 251, 259, 261 symbolic name, 304
SCSI DISK , 16 synchronous invocation, 312
SEM , 14, 251, 261 local caches, 140, 449, 451, 465{467
SHM REP , 12 local identiers, 6, 51, 87, 99, 100
SLIP , 16 local invocations, see laps
TIMER , 14, 321, 325 localization procedure, 371
UFS , 15 logical addresses, 141, 146, 148, 151, 181
USER MODE , 12
VIRTUAL ADDRESS SPACE , 13, 98, 139, mailboxes, 355
449 main function, 97, 111, 112, 117
VTIMER, 14, 237, 321, 333, 344 parameters, 111, 113, 122
le systems, 11, 15 main thread, 49, 110, 113, 117, 130, 204,
at memory, 13, 139, 140, 143 235
fork Unix, 170, 240{246 attributes, 110
functional mode, 376, 381{383, 389, 396, deleting, 228
397 priority, 32, 111
make command, 72, 75
handlers, 299, 300, 303, 305{308, 401{408, make environment, 68
413{448 mappers, 140, 449
hardware conguration, 4 mapping a segment, 463
hardware interrupts, 413 mapping shared objects, 191
header les, 64, 65, 68, 75 memory fault, 424, 425
home actor, 195, 198, 231, 232, 333, 336, memory management, 13, 139{159, 161{
416 172, 174{194, 449{488
host system, 4, 15, 19{21, 34, 93 parameters, 142, 452
Posix, 191
identifying actors, 51 statistics, 46
identifying threads, 198 memory mapping, 13
ifcong command, 17 memstat command, 46
imake command, 63, 71 message handlers, 401{408
imake environment, 69 message pool (mipc), 356
Imakele le, 63, 67, 71, 72, 75, 77, 445 message queues (mipc), 356
installing ChorusOS, 19{23 message space (mipc), 356
IOM actor, 11, 98 creating, 356, 357
544 Index
freeing, 356 conditions, 278
opening, 356 errors, 526
removing, 357 mutexes, 277
messages (ipc), 355, 368, 379{381 private data, 296
destination, 381, 383 synchronization, 276
handlers, 401, 414 threads, 246
priorities, 374 private data, 10, 281{297
receiving, 384, 385 errors, 526
replying, 386 keys, 284
sending, 382 per actor, 291{296
structure, 379 per thread, 285{290
messages (mipc), 356 Posix, 296, 297
operations, 358{362 producing binaries, 72, 73
priority, 360{363 protected memory, 13, 139, 140, 143, 146
micro-kernel, 9 pthreads, 246{250
migrating ports, 408{411 attributes, 246
Mix, 3 creating, 247
mount command, 17, 47, 48 killing, 248
mounting a le system, 17, 42, 47 synchronization, 276
multithreading, 195, 281 terminating, 248
mutexes, 256{261, 288, 289, 431 RARP, 35{37
mutual exclusion, 256, 431 rarp command, 17
naming RDBC actor, 12
actors, 99 real-time mutexes, 259{261
laps, 304 reboot command, 59, 60, 370
threads, 198 regions, 7, 54, 144{151, 461
netboot, 5 allocating, 158{166
NFS, 15, 42, 47 attributes, 145, 149{151, 165, 453{460
non-abortable calls, 208, 233, 322 copying, 170, 178, 242
descriptors, 148
page alignment, 144, 150, 159, 162, 163, freeing, 148, 166{169
167, 171, 184, 194 inheriting, 178{181
pages, 141, 144, 149 initializing, 149
PATH variable, 49, 56 sharing, 170{178
PD actor, 10, 282 remote communication, 369{371
perror function, 526 remote shell, 16
root le system, 26{28, 42
physical addresses, 141, 146, 151, 181 round-robin, see scheduling
port groups, 7, 355, 368, 376{379 RPC, 376, 393{396, 407
identifying, 376, 377 rsh command, 11, 16, 41, 57
operations, 376{379
ports, 7, 53, 54, 355, 368, 371{379 scheduling, 13, 53, 111, 197, 200{208, 219,
creating, 374, 375 237
deleting, 375, 376 fo, 13, 200, 201, 255, 275
identifying, 372 real-time, 202
migration, 408{411 round-robin, 13, 200{202, 206, 208, 219,
Posix, 11, 191, 246, 276 255, 273, 276, 349, 350
Index 545
secured mode, 43, 44, 56 terminating c actors, 130
segments, 140, 449{451, 461 TFTP, 35, 37
explicit access, 464 threads, 7, 53, 195, 196, 198{250
mapping, 463 aborting, 230, 232
semaphores, 252{256, 261{267 activating, 229
sharing memory, 170, 171 attributes, 53, 198, 204, 235
SHM REP actor, 12, 229 context, 195, 239
shutdown command, 17 creating, 197, 211{213, 215{221, 235
site identication, 83, 85, 369 delaying, 73, 208
sockets, 11, 16 deleting, 197, 225{229
spawning c actors, 110{118 execution actor, 195
standard input/output, 110 execution mode, see privilege
standard proles, 29 execution times, 237
starting home actor, 195, 198
actors, 101, 103 local identier, 195, 198, 235, 284
threads, 199 name, 198
startup les, 44 priority, 13, 197, 200, 201, 205
stopping private data, 285{290
actors, 102, 309 scheduling, 200{208, 211
threads, 199, 230 scheduling class, 200, 219
subsystems, 438, 441{446 stacks, 196
Sun Embedded Workshop, 4 starting, 199
supervisor actors, 49, 56, 59, 64, 68, 98, states, 197{199, 211, 231, 235
101, 104, 110, 112, 127, 147, 148, stopping, 199, 230
162, 165, 166, 196, 461 supervisor privilege, 195
supervisor address space, 68, 98, 127, 141, system stack, 196, 212, 235
143, 148, 162, 163, 166, 181{190, user privilege, 195
195, 235, 301, 306, 442 user stack, 196, 212, 222
supervisor privilege, 127, 196, 300 time management, 13, 321{353
supervisor threads, 148, 150, 159, 196 time of day, 323{325
synchronization, 14, 133{138, 251{280 time resolution, 322, 323
synchronizing c actors, 133{138 time slicing, 13, 201, 273
synchronous communication, see RPC timeout, 446{448
system actors, 26, 51, 101 timeouts, 333
ADMIN, 10, 11 timers, 321, 325, 326, 328{332
AM, 11 tip command, 38
C INIT, 11 trap handlers, 414, 438{446
IOM, 11 traps, 414, 438
KERN, 9 trusted actors, 98, 100, 110, 196
PD, 10 trusted users, 43, 44, 47
RDBC, 12 tunable parameters, 32, 41, 88{90
SHM REP, 12
system conguration, 51 umount command, 44, 48
system stacks, 147, 148, 196, 235, 312 unique identiers, 6, 51, 52, 74, 83{86
size, 147, 212 Unix, 3, 57, 111
Unix commands, 27
target system, 4, 19, 21, 27, 35, 36, 93 Unix process, 7, 195
546 Index
unmounting a le system, 48
user actors, 68, 98, 101, 110, 112, 146, 163{
165, 196, 458
user address space, 141, 143, 301, 306, 442
user stacks, 212, 213, 222, 442
using libraries, 76, 77
virtual addresses, see logical addresses
virtual memory, 13, 139, 140, 170, 449{488
virtual timeouts, 333{349
XML, 30
yielding processor, 245, 248, 294