SPC002007 Application Development Manual EN V1.0
User Manual:
Open the PDF directly: View PDF .
Page Count: 857
Download | ![]() |
Open PDF In Browser | View PDF |
Application Development Manual SpaceChain OS SPC002008 V1.00 Date: 2018/06/13 Product Manual 类别 内容 Type Contents Key Word Abstract SpaceChain OS Brief history Functions Brief history of the operating system Application Development Manual SpaceChain OS Revision History Version Date V1.00 2018/06/19 Reason Create file Manual OS. SpaceChain i Application Development Manaual SpaceChain OS Preface Brief introduction The book describes SylixOS program design interface, including: SylixOS system API function, POSIX standard API function and many functions provided by standard C, and is applicable to all programmers. As an advanced real-time embedded Operation System, SylixOS has been widely applied in aeronautics and astronautics, industrial automation, communications, new energy and other fields. Similar to many operating systems (such as VxWorks, Linux and so on), SylixOS provides massive service for program execution, such as: open file, read and write file, close file, dynamic loader, dynamic distribution of memory space, dynamic file creation, obtain system time and other services. Through the command under SylixOS Shell, one can conveniently view the system information. For example: view the running thread in the system via ts command, view the running process in the system via ps command, and view memory status via free command. How to use the command built in SylixOS Shell will be introduced in brief introduction to Shell in Chapter 3 of the book. SylixOS is an open source operating system. Therefore, one can conveniently obtain the source code (obtain SylixOS source code from www.sylixos.com), learn the knowledge in the book through SylixOS source code, and verify the knowledge in each section with the instance in the book step by step. This book will explain the programming method of the real-time system from the perspective of real-time system and precautions during programming. Overview of this book This book describes in detail the application programming method of SylixOS and use of application program interface thereof. The organization structure of this book is as follows: Chapter 1 describes the history of SylixOS and its application in various fields, and also describes POSIX standard of SylixOS; Chapter 2 describes the introduction and building of SylixOS development environment; Chapters 3 and 4 describe use of SylixOS Shell command and how to write the first SylixOS application; Chapter 5 provides an in-depth analysis of I/O system of SylixOS, and details the standard functions commonly used in I/O operations. These functions include unbuffered I/O functions, file and directory operation functions, and buffered I/O function and I/O multiplexed functions; Manual SpaceChain OS Application Development Manual SpaceChain OS Chapter 6 describes the multi-thread programming method of SylixOS and its thread scheduling principles; Chapter 7 describes the inter-thread communication mechanism of SylixOS and how to use the lock in the thread. Chapter 8 describes the multi-process programming method of SylixOS and its process principle; Chapter 9 describes the inter-process communication mechanism of SylixOS. Chapter 10 describes the signal system of SylixOS and how to correctly use the signal during programming; Chapter 11 describes how to use SylixOS time management functions. Chapter 12 describes the principle of fixed-length memory and variable-length memory of SylixOS and its virtual memory principle, and how to correctly use these memories; Chapter 13 describes the standard I/O device operation of SylixO; Chapter 14 describes the principle of the hot swapping system and how to use the API; Chapter 15 describes the network programming method of SylixOS and how to use its network tools; Chapter 16 describes the file system principle of SylixOS. Chapter 17 analyzes the log system of SylixOS in detail; Chapter 18 describes the multi-user management of SylixOS; Chapter 19 describes the dynamic loading principle of SylixOS and how to use its application program interface; Chapter 20 describes how to the power management functions of SylixOS; Chapter 21 describes the programming method for SylixOS graphical interface Qt and how to transplant the third-party library to SylixOS; Chapter 22 describes how the application is migrated from the Linux platform or the VxWorks platform to the SylixOS platform. Finally, the appendix lists the standard header files in SylixOS and wrong numbers and their meanings in SylixOS. Appendix C details the SylixOS Makefiile files. If the reader has experience in Linux or VxWorks system programming, it will be easy to understand the knowledge in the book. Of course, you can also easily learn the Manual OS. SpaceChain II Application Development Manual SpaceChain OS knowledge in the book without these experience, because this book contains a large number of instances, which are easy to understand (requiring C language program design basis). By learning this book, readers can quickly understand SylixOS, and be able to start developing their own SylixOS application. Acknowledgment This book can be successfully completed thanks to colleagues who have spent a lot of time and energy reviewing and writing. At the same time, we also want to thank enthusiastic netizens for their suggestions for revision of this book. Due to the limited level of writing staff, there will inevitably be some inadequacies in the book. Welcome readers to offer criticism and revision suggestions. Beijing Acoinfo Technology Co., Ltd. Manual OS. SpaceChain III Application Development Manual SpaceChain OS Contents Noun explanation and agreement .................................................................................. 11 Chapter I Brief introduction to SylixOS operating system .............................................. 13 1.1 Brief history of the operating system ................................................................. 13 1.2 Functions of the operating system .................................................................... 14 1.3 Classification of operating systems ................................................................... 15 1.4 Brief introduction to POSIX Standards .............................................................. 16 1.5 POSIX restrictions ............................................................................................. 17 1.6 SylixOS Overview .............................................................................................. 21 1.7 SylixOS application field .................................................................................... 23 1.8 Current application cases of SylixOS ................................................................ 24 Chapter 2 Integrated development environment ............................................................ 26 2.1 Introduction to ARM processor .......................................................................... 26 2.1.1 Brief introduction ..................................................................................... 26 2.1.2 Features .................................................................................................. 26 2.1.3 Operating mode ...................................................................................... 27 2.1.4 Register organization .............................................................................. 28 2.1.5 Instruction structure................................................................................. 30 2.2 Introduction to RealEvo-IDE .............................................................................. 30 2.2.1 Brief introduction ..................................................................................... 30 2.2.2 Functions ................................................................................................. 32 2.3 Introduction to GCC toolchain ........................................................................... 33 2.4 Installation of RealEvo-IDE ............................................................................... 33 2.4.1 Acquisition of SylixOS development kit................................................... 33 2.4.2 Installation of SylixOS development kit................................................... 33 Chapter 3 Brief introduction to Shell ............................................................................... 35 3.1 What's Shell ....................................................................................................... 35 3.2 Instructions for common Shell commands ........................................................ 35 3.2.1 System command ................................................................................... 36 3.2.2 File command .......................................................................................... 41 3.2.3 Network command .................................................................................. 44 3.2.4 Time command ........................................................................................ 47 3.2.5 Dynamic loading command..................................................................... 49 3.2.6 Other commands..................................................................................... 50 3.3 Environment variable ......................................................................................... 51 3.4 Root file system ................................................................................................. 52 3.5 Operation application ........................................................................................ 55 3.6 I/O redirection .................................................................................................... 55 Chapter 4 Compiling the first program............................................................................ 57 4.1 Hello world application ...................................................................................... 57 4.1.1 Create SylixOS.base project ................................................................... 57 Manual OS. SpaceChain 1 Application Development Manual SpaceChain OS 4.1.2 Create the Hello World Project .......................................................................... 60 4.1.3 Compiling the Hello world project ........................................................... 62 4.1.4 Deployment file ....................................................................................... 63 4.1.5 Run the Hello world application .............................................................. 64 4.1.6 Debug the Hello world application .......................................................... 65 4.1.7 Non-stop debugging mode...................................................................... 72 4.2 Hello Library ...................................................................................................... 73 4.2.1 Create Hello Library Project .................................................................... 74 4.2.2 Compile Hello Library Project ................................................................. 74 4.2.3 Deploy the library file............................................................................... 75 4.2.4 Modify Hello World application ................................................................ 75 4.2.5 Run the Hello world Application .............................................................. 77 4.2.6 Debug Hello world applications and dynamic library .............................. 77 Chapter 5 I/O System ..................................................................................................... 79 5.1 I/O System ......................................................................................................... 79 5.1.1 File type ................................................................................................... 79 5.1.2 File descriptor .......................................................................................... 81 5.1.3 I/O System structure ............................................................................... 82 5.2 Standard I/O access .......................................................................................... 85 5.2.1 File I/O ..................................................................................................... 86 5.2.2 Files and directories .............................................................................. 104 5.2.3 Standard I/O library ............................................................................... 120 5.3 Asynchronous I/O access ................................................................................ 132 5.3.1 POSIX asynchronous I/O ...................................................................... 133 5.4 Advanced I/O access ....................................................................................... 141 5.4.1 Decentralized aggregation operation .................................................... 141 5.4.2 Non-blocking I/O ................................................................................... 144 5.4.3 I/O multiplexing ..................................................................................... 144 5.4.4 File record lock ...................................................................................... 150 5.4.5 File memory mapping............................................................................ 154 Chapter 6 Thread management.................................................................................... 155 6.1 Thread ............................................................................................................. 155 6.2 Thread state machine ...................................................................................... 155 6.3 SylixOS thread................................................................................................. 157 6.3.1 Thread creation ..................................................................................... 157 6.3.2 Thread control ....................................................................................... 164 6.3.3 End of thread ......................................................................................... 167 6.3.4 Multi-thread security .............................................................................. 168 6.4 POSIX thread .................................................................................................. 173 6.4.1 Thread attribute ..................................................................................... 173 6.4.2 Thread creation ..................................................................................... 179 6.4.3 Thread exit ............................................................................................ 181 6.4.4 Thread cancel........................................................................................ 184 Manual OS. SpaceChain 2 Application Development Manual SpaceChain OS 6.5 POSIX thread key value ......................................................................................... 192 6.6 SylixOS thread scheduling .............................................................................. 195 6.6.1 Priority scheduling ................................................................................. 195 6.6.2 RR (Round-Robin) scheduling .............................................................. 197 6.7 POSIX thread scheduling ................................................................................ 199 6.8 SylixOS RMS scheduling ................................................................................ 203 6.9 SylixOS coroutine ............................................................................................ 208 Chapter 7 Inter-thread communication ......................................................................... 213 7.1 Shared resource .............................................................................................. 213 7.2 Inter-thread communication............................................................................. 214 7.3 SylixOS semaphore ......................................................................................... 215 7.3.1 Binary semaphore ................................................................................. 216 7.3.2 Counting semaphore ............................................................................. 225 7.3.3 Mutex semaphore ................................................................................. 232 7.3.4 Read-write semaphore .......................................................................... 238 7.4 POSIX semaphore........................................................................................... 241 7.4.1 POSIX anonymous semaphore ............................................................ 241 7.5 Priority inversion .............................................................................................. 246 7.5.1 What's priority inversion ........................................................................ 246 7.5.2 How to solve priority inversion .............................................................. 247 7.6 POSIX mutex semaphore................................................................................ 247 7.6.1 Mutex semaphore attribute block .......................................................... 248 7.6.2 Mutex semaphore ................................................................................. 251 7.7 Deadlock .......................................................................................................... 257 7.7.1 What's deadlock .................................................................................... 257 7.7.2 Generation conditions of deadlock ....................................................... 257 7.7.3 Deadlock prevention ............................................................................. 258 7.8 POSIX read-write lock ..................................................................................... 260 7.8.1 Read-write lock attribute block① ........................................................... 261 7.8.2 Read-write lock ..................................................................................... 262 7.9 SylixOS condition variable............................................................................... 266 7.9.1 Condition variable attribute block .......................................................... 268 7.9.2 Condition variable ................................................................................. 269 7.10 POSIX condition variable .............................................................................. 273 7.10.1 Condition variable attribute block ........................................................ 274 7.10.2 Condition variable ............................................................................... 275 7.11 SylixOS message queue ............................................................................... 279 7.11.1 Message queue ................................................................................... 280 7.12 SylixOS event set .......................................................................................... 289 7.12.1 Event set ............................................................................................. 290 7.13 POSIX thread barrier ..................................................................................... 296 7.13.1 Thread barrier attribute block .............................................................. 297 Manual OS. SpaceChain 3 Application Development Manual SpaceChain OS 7.13.2 Thread barrier................................................................................................. 298 7.14 POSIX spin lock............................................................................................. 300 7.14.1 Spin lock .............................................................................................. 302 7.15 SylixOS atomic number ................................................................................. 305 7.15.1 Atomic number .................................................................................... 306 7.16 One-time initialization .................................................................................... 310 7.16.1 pthread_once_t variable ..................................................................... 310 Chapter 8 Process management .................................................................................. 314 8.1 Real-time process............................................................................................ 314 8.2 Process state machine .................................................................................... 315 8.3 POSIX processAPI .......................................................................................... 315 8.3.1 Execute program ................................................................................... 316 8.3.2 Create process ...................................................................................... 319 8.3.3 Process scheduling ............................................................................... 328 8.3.4 Process relation .................................................................................... 333 8.3.5 Process control ..................................................................................... 338 8.3.6 Process environment ............................................................................ 347 8.4 SylixOS process API........................................................................................ 349 8.4.1 Create processes by using SylixOS API ............................................... 349 8.4.2 SylixOS process control API ................................................................. 351 Chapter 9 Inter-Process Communication ..................................................................... 356 9.1 Definition of IPC............................................................................................... 356 9.2 Anonymous Pipe.............................................................................................. 356 9.2.1 Operate Anonymous Pipe ..................................................................... 357 9.3 Named Pipe ..................................................................................................... 360 9.3.1 Operate Named Pipe ............................................................................ 360 9.4 POSIX Named Semaphore ............................................................................. 363 9.4.1 Named Semaphore ............................................................................... 364 9.5 POSIX Named Message Queue ..................................................................... 368 9.5.1 Attribute Block of Named Message Queue ........................................... 368 9.5.2 Named Message Queue ....................................................................... 369 9.6 POSIX Shared Memory ................................................................................... 376 9.7 XSI IPC ............................................................................................................ 377 9.7.1 XSI Identifiers and Keys ........................................................................ 377 9.7.2 XSI Permission Structure ...................................................................... 378 9.7.3 XSI IPC Semaphore .............................................................................. 380 9.7.4 XSI IPC Message Queue ...................................................................... 382 9.7.5 XSI IPC Shared Memory....................................................................... 385 Chapter 10 Signal System ............................................................................................ 389 10.1 Signal System................................................................................................ 389 10.1.1 Unreliable Signals and Reliable Signals ............................................. 392 10.2 Signal Installation .......................................................................................... 393 10.2.1 Function signal .................................................................................... 393 Manual OS. SpaceChain 4 Application Development Manual SpaceChain OS 10.2.2 Function sigaction .......................................................................................... 393 10.3 Signal Set ...................................................................................................... 398 10.4 Signal Transmission ...................................................................................... 403 10.4.1 非排队信号.................................................................. 错误!未定义书签。 10.4.1 Non-queued Signal ............................................................................. 404 10.4.2 Queued Signal .................................................................................... 405 10.4.3 Timer Signal ........................................................................................ 407 10.5 Signal Blocking .............................................................................................. 417 10.6 Process and Signal ....................................................................................... 420 10.7 Influence of Signal ......................................................................................... 421 10.7.1 System Call Interrupt① ........................................................................ 421 10.7.2 Reentrancy Effect of Function............................................................. 422 Chapter 11 Time Management ..................................................................................... 426 11.1 SylixOS Time Management ........................................................................... 426 11.1.1 System Time ........................................................................................ 426 11.1.2 RTC Time............................................................................................. 427 11.2 POSIX Time Management ............................................................................. 429 11.2.1 UTC Time and Local Time ................................................................... 429 11.2.2 Time Form Transformation .................................................................. 431 11.2.3 High-precision Time............................................................................. 435 11.2.4 Get Process or Thread Clock Source ................................................. 437 11.2.5 Time-related Extension Operations ..................................................... 437 Chapter 12 Memory Management ................................................................................ 440 12.1 Fixed Length Memory Management ............................................................. 440 12.1.1 Create Memory Partition ..................................................................... 440 12.1.2 Delete Memory Partition ..................................................................... 441 12.1.3 Get/return Memory Block .................................................................... 442 12.1.4 Get Current State of Memory Partition ............................................... 442 12.1.5 Get Memory Partition Name ............................................................... 443 12.2 Variable Length Memory Management ......................................................... 447 12.2.1 Create Memory Area ........................................................................... 447 12.2.2 Delete Memory Area ........................................................................... 447 12.2.3 Memory Area Increases Memory Space............................................. 448 12.2.4 Memory Allocation ............................................................................... 448 12.2.5 Allocate Memory of Address Aligned .................................................. 449 12.2.6 Dynamic Memory Adjustment ............................................................. 450 12.2.7 Free the Memory ................................................................................. 451 12.2.8 Get Current State of Memory Area ..................................................... 451 12.2.9 Get Memory Area Name ..................................................................... 452 12.3 POSIX Standard Memory Management ....................................................... 455 12.3.1 Memory Allocation ............................................................................... 455 12.3.2 Allocate Memory Specifying Alignment Value ..................................... 457 Manual OS. SpaceChain 5 Application Development Manual SpaceChain OS 12.3.3 Free the Memory ............................................................................................ 458 12.3.4 Memory Allocation Function with Security Detection .......................... 458 12.4 Virtual Memory Management ........................................................................ 459 12.4.1 Memory Division .................................................................................. 459 12.4.2 Process Page Management................................................................ 460 12.4.3 Virtual Memory Mapping ..................................................................... 460 12.4.4 Other Operations of Virtual Memory ................................................... 472 Chapter 13 Standard I/O Devices................................................................................. 474 13.1 /dev/null ......................................................................................................... 474 13.2 /dev/zero ........................................................................................................ 474 13.3 Terminal ......................................................................................................... 474 13.4 Virtual Terminal .............................................................................................. 477 13.5 Graphic Device .............................................................................................. 477 13.6 Input Device................................................................................................... 483 13.6.1 Mouse Device ..................................................................................... 483 13.6.2 Keyboard Device ................................................................................. 487 13.7 Memory Device.............................................................................................. 490 13.8 Random Device ............................................................................................. 492 13.9 Audio Device .................................................................................................. 493 13.9.1 Basics .................................................................................................. 493 13.9.2 Audio Programming............................................................................. 494 13.9.3 Mixer Programming ............................................................................. 497 13.10 Audio Device................................................................................................ 500 13.10.1 Device Description ............................................................................ 500 13.10.2 Description of Device Channels ........................................................ 501 13.10.3 Description of Image Format of the Device Channel ........................ 502 13.10.4 Setting of Device Channel ................................................................ 504 13.10.5 Setting of Device Buffer .................................................................... 505 13.10.6 Video Capture Control....................................................................... 507 13.10.7 Summary of Operation Commands for the Video Device ................ 508 13.10.8 Video Device Application Examples ................................................. 510 13.11 Real-Time Clock Device .............................................................................. 513 13.12 GPIO Devices .............................................................................................. 513 13.13 CAN Bus Device .......................................................................................... 517 13.14 Virtual Device Files ...................................................................................... 523 13.14.1 eventfd............................................................................................... 523 13.14.2 timerfd ............................................................................................... 526 13.14.3 hstimerfd............................................................................................ 531 13.14.4 signalfd .............................................................................................. 532 Chapter 14 Hot-plug System ........................................................................................ 537 14.1 Introduction of the Hot-plug System .............................................................. 537 14.2 Hot-plug Message ......................................................................................... 538 14.2.1 Format of Hot-Plug Messages ............................................................ 538 Manual OS. SpaceChain 6 Application Development Manual SpaceChain OS 14.2.2 Processing the Hot-Plug Messages............................................................... 538 Chapter 15 Network I/O ................................................................................................ 543 15.1 socket interface ............................................................................................. 543 15.1.1 Network endian ................................................................................... 544 15.1.2 Socket address ................................................................................... 546 15.1.3 Socket function.................................................................................... 551 15.1.4 Socket option ...................................................................................... 553 15.2 Brief introduction to TCP/IP ........................................................................... 558 15.2.1 Layering of TCP/IP .............................................................................. 558 15.2.2 IP address ........................................................................................... 561 15.2.3 Data encapsulation ............................................................................. 562 15.2.4 Data demultiplexing............................................................................. 563 15.2.5 Port number......................................................................................... 564 15.2.6 Link layer ............................................................................................. 564 15.2.7 IP Internet protocol .............................................................................. 568 15.2.8 ARP address desorption protocol ....................................................... 570 15.2.9 ICMP message control protocol .......................................................... 571 15.2.10 UDP user datagram protocol ............................................................ 575 15.2.11 TCP transmission control protocol .................................................... 575 15.3 Network communication instance ................................................................. 580 15.3.1 UDP instance ...................................................................................... 580 15.3.2 TCP instance ....................................................................................... 588 15.3.3 Raw socket (RAW) instance ............................................................... 597 15.4 Introduction to DNS ....................................................................................... 601 15.5 AF_UNIX domain protocol ............................................................................. 612 15.5.1 AF_UNIX instance ............................................................................... 613 15.6 AF_PACKET link layer communication ......................................................... 622 15.6.1 AF_PACKET instance ......................................................................... 624 15.6.2 AF_PACKET and mmap...................................................................... 627 15.7 Network event detection ................................................................................ 634 15.8 Standard network function library.................................................................. 638 15.8.1 ifconfig tool .......................................................................................... 638 15.8.2 TFTP.................................................................................................... 639 15.8.3 FTP ...................................................................................................... 641 15.8.4 Telnet ................................................................................................... 647 15.8.5 ping ...................................................................................................... 650 15.8.6 PPP ..................................................................................................... 652 15.8.7 Network address translation ............................................................... 656 15.8.8 SylixOS network routing ...................................................................... 659 15.8.9 netstat.................................................................................................. 676 15.8.10 npf ..................................................................................................... 681 15.9 Control interface of the standard network card ............................................. 686 15.10 Brief introduction to wireless communications and ad-hoc network ........... 694 Manual OS. SpaceChain 7 Application Development Manual SpaceChain OS Chapter 16 File system ................................................................................................. 700 16.1 Introduction of the file system ....................................................................... 700 16.2 TPSFS file system ......................................................................................... 702 16.3 FAT file system .............................................................................................. 704 16.3.1 FAT command ..................................................................................... 704 16.4 NFS file system ............................................................................................. 705 16.4.1 Basic operations of NFS ..................................................................... 705 16.5 ROM file system ............................................................................................ 708 16.6 RAM file system............................................................................................. 709 16.7 ROOT file system .......................................................................................... 710 16.8 PROC file system .......................................................................................... 712 16.8.1 /proc/pid process related information.................................................. 713 16.8.2 /proc/ksymbol kernel symbol table ...................................................... 715 16.8.3 /proc/posix POSIX subsystem information ......................................... 716 16.8.4 /proc/net network subsystem .............................................................. 716 16.8.5 /proc/power power management subsystem ...................................... 717 16.8.6 Subsystem of the /proc/fs file system ................................................. 719 16.8.7 /proc/version kernel version information ............................................. 720 16.8.8 /proc/kernel kernel information............................................................ 720 16.8.9 /proc/cpuinfo processor information.................................................... 722 16.8.10 /proc/bspmem memory mapping information ................................... 723 16.8.11 proc/self auxiliary information............................................................ 723 16.8.12 /proc/yaffs YAFFS partition information ............................................ 724 16.9 YAFFS file system ......................................................................................... 726 16.9.1 The difference between NAND Flash and NOR Flash ....................... 726 16.9.2 YAFFS proper nouns ........................................................................... 727 16.9.3 Memory Technology Device(MTD) ................................................. 728 16.9.4 YAFFS partition ................................................................................... 729 16.9.5 YAFFS command ................................................................................ 731 16.10 File system Shell command ........................................................................ 733 Chapter 17 Logging System ......................................................................................... 750 17.1 SylixOS logging system................................................................................. 750 17.2 POSIX logging system .................................................................................. 752 Chapter 18 Multi-user Management ............................................................................. 760 18.1 Introduction of POSIX User Management..................................................... 760 18.1.1 Users ................................................................................................... 760 18.1.2 User Groups ........................................................................................ 761 18.2 Management of POSIX Authority .................................................................. 762 18.2.1 File Authority and Expression Method ................................................ 763 18.2.2 File Authority Management Command chmod ................................... 764 18.3 User Management-related Files in the /etc Directory ................................... 765 18.3.1 /etc/passwd File .................................................................................. 765 18.3.2 /etc/shadow File .................................................................................. 767 Manual OS. SpaceChain 8 Application Development Manual SpaceChain OS 18.3.3 /etc/group File ................................................................................................ 769 18.4 POSIX User Operations ................................................................................ 770 18.4.1 User Password Operation ................................................................... 770 18.4.2 User Shadow Password Operation ..................................................... 772 18.4.3 User Group Operation ......................................................................... 773 18.4.4 User’s Additional Group Operation ..................................................... 774 18.5 Multi-user Management Database ................................................................ 776 18.5.1 User Operation .................................................................................... 776 18.5.2 Group Operation ................................................................................. 777 18.5.3 Password Operation............................................................................ 778 18.5.4 User Shell Commands ........................................................................ 778 Chapter 19 Dynamic Loading ....................................................................................... 782 19.1 Principle of Dynamic Link Library .................................................................. 782 19.1.1 Format of ELF File .............................................................................. 782 19.1.2 ELF Files in SylixOS ........................................................................... 782 19.1.3 SylixOS Dynamic Loader Features ..................................................... 783 19.2 Autoloading of Dynamic Library .................................................................... 783 19.2.1 Linking of Dynamic Library.................................................................. 783 19.2.2 Downloading of Dynamic Library ........................................................ 784 19.2.3 Loading of Kernel Module ................................................................... 785 19.3 POSIX Dynamic Link Library API .................................................................. 785 19.3.1 Common API of Dynamic Library ........................................................ 785 19.3.2 Other APIs ........................................................................................... 788 19.4 Dynamic Link Library Shell Command .......................................................... 790 19.4.1 Viewing Dynamic Link Libraries .......................................................... 790 19.4.2 Loading Kernel Modules ..................................................................... 791 19.4.3 Unloading Kernel Modules .................................................................. 791 Chapter 20 Power Management................................................................................... 794 20.1 SylixOS Power Management ........................................................................ 794 20.2 Power Management API................................................................................ 795 Chapter 21 Introduction to Standard Third-Party Software .......................................... 798 21.1 Qt Graphical Interface Software .................................................................... 798 21.1.1 Qt Porting in SylixOS .......................................................................... 798 21.1.2 Verification of Qt Graphical Interface Library ...................................... 800 21.1.3 QtCreator Installation and Configuration ............................................ 802 21.2 Zlib File Compression Library........................................................................ 808 21.2.1 Porting of Zlib Library .......................................................................... 808 21.2.2 Verification of Zlib Library.................................................................... 811 21.3 SQLite3 Database ......................................................................................... 811 21.3.1 Porting of SQLite3 in SylixOS ............................................................. 811 21.3.2 Verification of SQLite3 Library ............................................................ 811 21.4 OpenSSL Encryption Library ......................................................................... 812 21.4.1 Introduction to OpenSSL..................................................................... 812 Manual OS. SpaceChain 9 Application Development Manual SpaceChain OS 21.4.2 Porting of OpenSSL Library ........................................................................... 813 21.4.3 Verification of OpenSSL Library .......................................................... 815 21.5 GoAhead Web Server ................................................................................... 816 21.5.1 Porting of GoAhead............................................................................. 816 21.5.2 GoAhead Verification .......................................................................... 817 21.6 C-Language Interpreter ................................................................................. 817 21.6.1 Picoc Porting ....................................................................................... 818 21.6.2 Picoc Usage Verification ..................................................................... 818 Chapter 22 Platform Porting ......................................................................................... 819 22.1 From Linux to SylixOS ................................................................................... 819 22.2 From VxWorks to SylixOS ............................................................................. 820 22.2.1 Development of VxWorks Applications in RealEvo-IDE ..................... 820 Appendix A Standard Header File ................................................................................ 827 A.1 C Standard Header File .................................................................................. 827 A.2 POSIX Standard Header File .......................................................................... 827 Annex B SylixOS Error Number ................................................................................... 830 B.1 POSIX Error Number ...................................................................................... 830 B.2 IPC/Web Error Number ................................................................................... 830 B.3 SylixOS Kernel Error Number ......................................................................... 832 B.4 Thread Error Number ...................................................................................... 833 B.5 Message Queue Error Number ....................................................................... 835 B.6 TIMERError Number ....................................................................................... 835 B.7 Memory Operation Error Number ................................................................... 836 B.8 I/O System Error Number ............................................................................... 837 B.9 Shell Operation Error Number ........................................................................ 837 B.10 Other Error Numbers .................................................................................... 837 Annex C Description of SylixOS Makefile .................................................................... 839 C.1 Description of SylixOS Makefile ..................................................................... 839 C.1.1 Directory Structure of SylixOS.............................................................. 839 C.1.2 config.mk File........................................................................................ 841 C.1.3 Makefile File.......................................................................................... 843 Annex D Description of SylixOSOpen Source Community .......................................... 851 References .................................................................................................................... 852 Manual OS. SpaceChain 10 Application Development Manual SpaceChain OS Noun explanation and agreement This book strives to introduce the application development technology of the SylixOS real-time operation system with simplified language and space. The following computer vocabulary will be frequently used in the book. The following explanations and conventions are made for the computer terminology and its acronyms used. CPU: i.e., central processing unit (CPU) is the computing core and control core of a computer. It is collectively referred to as the three core components of the computer together with internal memory and input / output device. RISC: reduced instruction set computer, which adopts superscalar and superpipelined structures; there are only a few dozens of instructions, but the parallel processing capability is greatly enhanced. SMP: i.e., symmetric multi-processing, referring to the CPU which collects a group of same instruction set on a computer. Various CPUs shares the memory subsystem and bus structure. It is usually called as multi-core CPU system. AMP: i.e., Asynchronous Multiprocessing, referring to collection of a set of CPUs with different instruction sets and different functions on a computer. They are usually linked in a loosely coupled organization, and are responsible for processing different data respectively. Compiler: it is the program which translates the advanced computer language program (C/C++ and so on) to the machine language (binary code). Assembler: it is the language which translates the assembly language to machine language. Linker: it is a program which links one or more target files generated by the compiler or assembler with the dependent library to form an executable file. GNU: GNU Project was publicly launched by Richard Stallman on September 27, 1983, and it is aimed to provide a completely free operating system. Richard Stallman created the Free Software Foundation in 1985 to provide the technical, legal, and financial support for the GNU Project. Linux, GCC, EMAC and other software are from or enter GNU Project. GCC: it is the abbreviation of GNU Compiler Collection. GCC referred in particular to C compiler released by GNU. Due to rapid development of GCC, it Manual OS. SpaceChain 11 Application Development Manual SpaceChain OS has not been just a compiler, but a development tool chain which integrates the compiler, linker, debugger, object analysis and other functions. Multi-task: it refers to that the user can run multiple applications within the same time, and each application is called as a task. In the single CPU system structure, multiple tasks operate on a CPU alternatively. In the multi-CPU system structure, the tasks with the equal quantity can operate simultaneously. Scheduler: it is the core of the operating system. It is actually a memory-resident program. It constantly scans the thread queues, utilizes a specific algorithm to find out the thread which needs to run more than the thread currently occupying the CPU, ,and the use right of CPU is deprived from the previous thread and transferred to to threads that need to run. Embedded system: the embedded system refers to a dedicated computer system based on computer technology and with strict requirements for software and hardware tailoring, function, reliability, cost, size and power consumption in a narrow sense. An embedded system is a kind of dedicated computer system as a part of the device or equipment. The generalized embedded system refers to all computer systems except the server and PC. Preemptive system: it refers to the system which immediately abandons the current task, and turns to handle the more important event if any. Version management: it is the foundation of software configuration management, which manages and protects the developer's software resources. The main functions include: centralized file management; software version upgrade management; locking function; provide comparison of different versions of source programs. BUG tracking: it is the foundation of software defect management. The main functions include: recording and saving the problem-solving process; recording and saving the process and basis of a design decision. It can effectively record the process from software defect discovery to correction. BSP: abbreviation of board support packet. It is the collection of underlying programs running on the hardware platform of the operating system, generally including: startup program, driver, interrupt service program and other basic programs. TCM: the tightly coupled memory is an RAM with fixed size, which is closely coupled to the processor kernel, and provides the performance equivalent to Cache. Compared with Cache, the advantage is that the program code can accurately control the position of the function or code (stored in RAM). Manual OS. SpaceChain 12 Application Development Manual SpaceChain OS Cross compilation: that is to say, generate the executable code on a platform on another platform. For example, the executable program on ARM platform can be developed on x86 platform. Host machine: the computer used for development during cross compilation. Target machine: sometimes referred to as the target system or device, it is the target computer of cross compilation. The computer or device is used to run the cross-compiled executable program. Chapter I Brief introduction to SylixOS operating system 1.1 Brief history of the operating system The operating system (OS for short) is a computer program which manages and controls the computer hardware and software resources. It is the most basic system software which directly runs on the "bare machine". The operating system is the interface between the user and the computer, and also the interface between the computer hardware and other software. The functions of the operating system include managing hardware, software and data resources of the computer system, controlling program operation, improving the human-machine interface, and providing support for other application software. The operating system can maximizes functions of the computer system resources. There are many kinds of operating systems. From simple to complex, they can be divided into smart card operating system, real-time operating system, sensor node operating system, embedded operating systems, personal computer operating systems, multi-processor operating system, network operating system and mainframe operating system. There are mainly three types according to the application field: desktop operating system, server operating system and embedded operating system. In the middle of the 20th century, humans entered the age of information with the birth of computers. At that time, the first-generation computers did not have an operating system, because the way to build the early personal computers (same with building the mechanical computer) and performance were insufficient to execute such program. In 1947, due to invention of transistors and the micro-programming method invented by Maurice Vincent Wilkes, the computer was no longer a mechanical device but an electronic product. System management tools and programs which simplify the hardware operation process quickly emerged, which became the foundation of the operating Manual OS. SpaceChain 13 Application Development Manual SpaceChain OS system. By the mid-1950s, the commercial computer manufacturers had created the batch processing system which can serialize establishment, scheduling and execution of the work. At that time, the computer manufacturer writes different operating systems for each different model of computer, so the program written for one computer cannot be transplanted to other computers for execution, even if it is of the same model. In 1963, General Electric Company worked with Bell Labs and MIT to develop the Multics operating system in PL/I language. Its appearance was a source of inspiration for establishment of many operating systems in the 1970s. The UNIX system established by Dennis Ritchie and Ken Thompson from AT&T Bell Laboratories. The operating system was rewritten in C Language in 1969 in order to realize portability of the platform. The UNIX operating system written in C Language is of inter-temporal significance. It is the first modern operating system in the real sense. Later-born systems, such as Linux, BSD, Mactonish and Solaris, all came from UNIX system in the aspects of principle and application program interface. The impact of UNIX system on operating systems continues to this day. 1.2 Functions of the operating system The theoretical researchers of the operating system sometimes divide the operating system into four major parts: Device driver: the bottom part with direct control and monitoring of all types of hardware. Their responsibilities are to hide the hardware details and provide an abstract and generic interface to other parts. Kernel: the kernel part of the operating system, usually running at the highest privilege level, responsible for providing basic and structural functions. Interface library: it is a series of special program libraries. Their responsibilities are to wrap the basic services provided by the system into the programming interface (API) which can be used by the application, and it is the part closest to the application. Periphery: refers to all other parts of the operating system other than the above three types, and is usually used to provide specific advanced services. For example, in a microkernel structure, most system services and various daemons in UNIX/Linux are usually classified in this list. The main functions of the operating system are resource management, program control, human-machine interaction and so on. The resources of the computer system can be divided into two major categories of device resources and information resources. Device resources refer to hardware devices constituting the computer, such as central Manual OS. SpaceChain 14 Application Development Manual SpaceChain OS processors, main memory, magnetic disc memory, printer, tape memory, monitor, keyboard, mouse and other devices. Information resources refer to various data stored in the computer, such as files, program library, system software, application software and so on. The operating system is located between the underlying hardware and application software or users, and is the bridge between the two. The application or the user can operate the computer via various interfaces provided by the operating system. A standard operating system shall provide the following functions: Task management. Memory Management. File System. Networking. Security. User Interface. Device Drivers. 1.3 Classification of operating systems Due to differences in features and application fields, the operating systems can be divided into the following types: Batch Processing Operating System: the user submits the job to the system operator. The system operator composes the jobs of multiple users into a batch of jobs, and then enters it into the computer to form a continuous job flow with automatic transfer in the system. Then start the operating system, and the system will automatically executes each job in turn. The batch processing operating system is divided into the simple batch processing system and the multi-channel batch processing system. Time Sharing Operating System: multiple users can access the system through the terminal simultaneously. Since multiple users share the processor time, the technology is called Time Sharing. The user interactively submits commands to the system. In response to the request, the system accepts each user's commands and controls each user program to be executed alternately in a short time unit (this type of time unit is called as time slice). This technology makes each user think that they own the processor. The time-sharing system is characterized by multiplexing, interactivity, "exclusivity" and timeliness. Real Time Operating System: referring to an operating system which enables the computer to respond to the request of an external event in a timely manner, complete processing of the event within a defined “strict time”, and control all real-time devices and Manual OS. SpaceChain 15 Application Development Manual SpaceChain OS real-time tasks to work in concert. The goal of the real-time operating system is to: respond to external requests within a "strict time" range, which has the characteristics of high reliability, integrity, resource allocation, and real-time task scheduling. Resource allocation and real-time task scheduling are its main features. In addition, the real-time operating system shall have strong fault tolerance. The real-time operating system is divided into the hard real-time operating system and the soft real-time operating system. The hard real-time operating system can guarantee that all real-time events can be properly responded within a certain period. The soft real-time operating system can only do its utmost to strive for the real-time event to get response within a certain time. Distributed Software Systems: the operating system configured for distributed computing systems. A large number of computers are linked together via the network, so as to obtain extremely high calculation capability and extensive data sharing. This system is called as the distributed system. It differs from other operating systems in resource management, communication control and operating system structure. At the same time, the distributed operating system must support parallel processing. Therefore, the communication mechanism it provides is different from that provided by the network operating system. It requires a fast communication speed. The structure of the distributed operating system is also different from those of other operating systems. It is distributed on various computers in the system and can handle various demands of the users in parallel. It has strong fault tolerance. The Embedded Operating System is an operating system which runs on an embedded device. The embedded operating system is an operating system widely used. The embedded device generally adopts the dedicated embedded operating systems. They are usually the real-time operating system, such as SylixOS, VxWorks, and some are functionally-reduced versions of Linux kernel operating systems, such as Android, Tizen, MeeGo and so on. 1.4 Brief introduction to POSIX Standards Since the first modern operating system - UNIX was born in 1970, there have been a variety of modern operating systems, such as: Windows, Linux, BSD, Solaris, etc. In order to facilitate the transplantation of applications and middleware, most operating systems adopt UNIX-compatible API (except for Windows). The POSIX standard was established to ensure mutual compatibility of operating system API. POSIX is a collective term for a series of interconnected standards defined by the IEEE (Institute of Electrical and Electronics Engineers) to regulate the API interfaces provided by various UNIX operating systems. It is officially called as IEEE1003, and the international standard name is ISO/IEC9945. This standard originated from a project which began approximately in 1985. The name of POSIX is an easy-to-remember name proposed by Richard Stallman at the request of the IEEE. It is basically the abbreviation of Portable Operating System Interface, and X indicates its inheritance of Unix API. Manual OS. SpaceChain 16 Application Development Manual SpaceChain OS The POSIX standard defines a sub-protocol called as 1003.1b for real-time operating system. This protocol defines the basic behavior of the standard real-time operating system. SylixOS satisfies requirements of this protocol. The current POSIX is mainly divided into four parts: Base Definitions, System Interfaces, Shell and Utilities and Rationale. SylixOS is compatible with most specifications in these four parts. The current operating systems conforming to the POSIX standard protocol are: UNIX, BSD, Linux, iOS, Android, SylixOS, VxWorks, RTEMS, etc. Due to the support of POSIX by SylixOS, applications on other compatible POSIX systems can be easily transplanted to SylixOS operating system. Table A.2 lists the POSIX standard header files supported by SylixOS. 1.5 POSIX restrictions POSIX defines a number of constants which involve operating system implementation restrictions. Unfortunately, this is one of the most puzzling parts of POSIX. Although POSIX defines a lot of restrictions and constants, we only care about the parts related to the basic POSIX interface. These limits and constants are divided into the following 7 categories. Numeric restrictions: LONG_BIT, SSIZE_MAX and WORD_BIT; Minimum value: as shown in Table 1.1; Maximum value: _POSIX_CLOCKRES_MIN; Value which can be increased during operation: CHARCLASS_NAME_MAX, COLL_WEIGHTS_MAX, LINE_MAX, NGROUPS_MAX and RE_DUP_MAX; Invariant value; during operation; Other invariant values: NL_ARGMAX, NL_MSGMAX, NL_SETMAX and NL_TEXTMAX; Variable value of path name: FILESIZEBITS, LINK_MAX, MAX_CANON, MAX_INPUT, NAME_MAX, PATH_MAX, PIPE_BUF and SYMLINK_MAX. Among these restrictions and constants, some may be defined in, and the rest may be defined and may not be defined according to specific conditions . These minimum values are constant (they do not change with the system). They specify the most restrictive values of these features. A POSIX-compliant implementation shall provide such a large value at least. This is why they are called as the minimum values, even though their names all include MAX. In addition, in order to ensure portability, a strictly POSIX-compliant application shall not require a larger value. Manual OS. SpaceChain 17 Application Development Manual SpaceChain OS Manual OS. SpaceChain 18 Application Development Manual SpaceChain OS Table 1.1 Minimum value of POSIX in Name of the minimum value Note _POSIX_CHILD_MAX Number of child processes per actual user ID _POSIX_DELAYTIMER_MAX Maximum number of timer overruns _POSIX_HOST_NAME_MAX Length of the host name returned by the gethostname function _POSIX_LINK_MAX Number of links of the file _POSIX_LOGIN_NAME_MAX Length of the login name _POSIX _MAX_CANON Number of bytes in terminal specification input queue _POSIX _MAX_INPUT Available space of the terminal input queue _POSIX_NAME_MAX Number of bytes in the file name, excluding terminating null bytes _POSIX_NGROUPS_MAX Number of group IDs added by each process at the same time _POSIX_OPEN_MAX Number of open files per process _POSIX_PATH_MAX Number of bytes in the path name, including terminating null bytes _POSIX_PIPE_BUF Number of bytes which can be atomically written to a pipe Times of repetitions of basic regular expression allowed by the _POSIX_RE_DUP_MAX regexec and regcomp functions when the interval notation \{m,n\} is used _POSIX_RTSIG_MAX _POSIX_SEM_NSEMS_MAX _POSIX_SEM_VALUE_MAX _POSIX_SIGQUEUE_MAX _POSIX_SSIZE_MAX _POSIX_STREAM_MAX _POSIX_SYMLINK_MAX _POSIX_SYMLOOP_MAX _POSIX_TIMER_MAX _POSIX_TTY_NAME_MAX _POSIX_TZNAME_MAX Number of real-time signal numbers reserved for the application Number of semaphores which can be used by a process simultaneously Value which can be held by the semaphore Number of queued signals which can be sent and suspended by a progress Value which can exist in the ssize_t object Number of standard I/O streams which can be opened by a process simultaneously Number of bytes in the symbolic link Number of compliant links which can be traversed when parsing path names Number of timers per process Length of the terminal device name , including the terminating null bytes Time zone name bytes A specific value may not be defined in this header file, because the actual value of a given process may depend on the total amount of storage in the system. If not defined in the header file, they cannot be used as numeric boundaries during compilation. Therefore, POSIX provides 3 runtime functions for call: sysconf, pathconf, and fpathconf. The actual implementation value can be obtained at runtime with these 3 functions.. Manual OS. SpaceChain 19 Application Development Manual SpaceChain OS #include long sysconf(int name); long fpathconf(int fd, int name); long pathconf(const char *path, int name); Function sysconf prototype analysis: For success of the function, return the corresponding value. For failure, return -1 and set the error number; The parameter name is the request constant name, as shown in Table 1.2. Function fpathconf prototype analysis: For success of the function, return the corresponding value. For failure, return -1 and set the error number; The parameter fd is the open file descriptor; The parameter name is the request constant name, as shown in Table 1.3. Function pathconf prototype analysis: For success of the function, return the corresponding value. For failure, return -1 and set the error number; The parameter path is the file path name; The parameter name is the request constant name, as shown in Table 1.3. Table 1.2 Sysconf call (partial) name parameter Name parameter Return value _SC_ARG_MAX ARG_MAX _SC_CLK_TCK LW_TICK_HZ _SC_DELAYTIMER_MAX __ARCH_INT_MAX _SC_IOV_MAX __ARCH_LONG_MAX _SC_LINE_MAX LINE_MAX _SC_LOGIN_NAME_MAX LOGIN_NAME_MAX _SC_OPEN_MAX LW_CFG_MAX_FILES _SC_PAGESIZE PAGESIZE _SC_RTSIG_MAX RTSIG_MAX _SC_SEM_NSEMS_MAX SEM_NSEMS_MAX _SC_SEM_VALUE_MAX __ARCH_UINT_MAX _SC_SIGQUEUE_MAX SIGQUEUE_MAX _SC_TIMER_MAX LW_CFG_MAX_TIMERS _SC_TZNAME_MAX TZNAME_MAX Manual OS. SpaceChain 20 Application Development Manual SpaceChain OS Table 1.3 Pathconf and fpathconf calls (partial) name parameters name parameter Return value _PC_FILESIZEBITS FILESIZEBITS _PC_LINK_MAX Internal definition of the system _PC_MAX_CANON MAX_CANON _PC_MAX_INPUT MAX_INPUT _PC_NAME_MAX NAME_MAX _PC_PATH_MAX PATH_MAX _PC_PIPE_BUF PIPE_BUF _PC_SYMLINK_MAX Internal definition of the system The difference between the latter two functions is that: one takes the file descriptor as the parameter, and the other uses the path name as the parameter. If name is not a correct constant, these 3 functions return to -1, and set errno to EINVAL. Some name will return a variable value or -1, -1 represents an indeterminate value. At the moment, errno value will not be changed. 1.6 SylixOS Overview SylixOS is a large-scale embedded real-time operating system, which was born in 2006. At first, it was only a small multi-tasking scheduler. After years of development, SylixOS has become a stable and reliable embedded system software development platform with full functions and excellent performance. Among the real-time operating system similar to SylixOS, VxWorks (mainly used in aeronautics, astronautics, military and industrial automation) and RTEMS (originated from the missile and rocket control real-time system of the US Department of Defense) are well-known in the world. As a latecomer of the real-time operating system, SylixOS has borrowed many design ideas from many real-time operating systems, making SylixOS reach or exceed the level of many real-time operating systems in terms of functionality and specific performance, and making it one of the most best representatives in domestic real-time operating ① systems . SylixOS, as a preemptive multi-task hard real-time operating system, has the following features and characteristics: Compatible with specifications; IEEE1003 (ISO/IEC9945) Manual OS. operating system interface SpaceChain 21 Application Development Manual SpaceChain OS Compatible with POSIX 1003.1b (ISO/IEC 9945-1) real-time programming standards; Excellent real-time performance (task scheduling and switching, interrupt response algorithms are 0 (1) time complexity algorithm); Support unlimited multitasking; Preemptive scheduling supports 256 priorities; Support coroutines (called as fiber in windows); Support virtual process; Support priority inheritance to prevent priority inversion; Extremely stable kernel, many products based on SylixOS development require 7×24-hour continuous operation; The CPU occupancy rate of the kernel is low; Flexible system (Scalable); The core code is written in C Language, which has good portability; Support tightly-coupled homogeneous multi-processor (SMP), such as: ARM Cortex-A9 SMP Core; Unique hard real-time multi-core scheduling algorithm; Support standard I/O, multiple I/O multiplexing and asynchronous I/O interfaces; Support multiple emerging asynchronous event synchronization interfaces, such as signalfd, timerfd and eventfd; Support many standard file systems: TPSFS, FAT, YAFFS, RAMFS, NFS, ROMFS, etc; Support file record lock, and can support database; Support the uniform block device Cache model; Support memory management unit (MMU); Support third-party GUI graphic libraries, such as Qt, Microwindows and emWin; Support dynamic loading applications, dynamic link libraries and modules; Support extended system symbol interface; Support standard TCP/IPv4/IPv6 dual network protocol stack, and provide standard socket operation interface; Support AF_UNIX, AF_PACKET, AF_INET, AF_INET6 protocol domain; Manual OS. SpaceChain 22 Application Development Manual SpaceChain OS Integrated with network tools, such as: FTP, TFTP, NAT, PING, TELNET, NFS, etc. many Integrated with shell interface, support environment variables (basically compatible with Linux operation habits); Integrated with re-entry ISO/ANSI C library (supports over 80% of standard functions); Supports many standard device abstractions, such as TTY, BLOCK, DMA, ATA, GRAPH, RTC and PIPE. Meanwhile, support a variety of industrial equipment or bus models, such as: PCI, USB, CAN, I2C, SPI, SDIO, etc.; Provide high-speed timer device interface, and can provide timing service higher than the master clock frequency; Support hot swapping device; Support device power management; Kernel, drivers, and applications support GDB debugging; Provide kernel behavior tracker to facilitate application performance and failure analysis. 1.7 SylixOS application field SylixOS adopts the preemptive, multitasking and hard real-time approach to design the whole operating system. The core target of its technical implementation is real-time control, stability and reliability. Therefore, SylixOS is suitable for (but not limited to) the following fields where the requirements for real-timeness and stability are particularly prominent: Industrial real-time control: mainly including industrial robot system, site security monitoring and protection system, industrial field bus communication management system and so on; Aeronautics and astronautics: mainly including aircraft flight control systems, aerospace data acquisition and recording system, high-precision surveying and mapping system, aerospace communication system and so on; National defense security: mainly including encryption communication system, sensor terminal system, virtual instrument system, data acquisition and recording system, fire control system and so on; Financial terminal: mainly including POS charging system, terminal payment system, ATM and so on; Manual OS. SpaceChain 23 Application Development Manual SpaceChain OS High-reliability civil use: mainly including vehicle traveling data recorder system, central control system for vehicles and marine engines, production line testing system, medical instrument system, distributed unattended system and so on. 1.8 Current application cases of SylixOS Since 2006, many projects or products have been developed based on SylixOS, covering a wide range of fields, and the products have stable and reliable operation. The following describes some products based on SylixOS from industrial automation, military, communication, civil use and other fields. Most products require 7×24 hour uninterrupted operation, and many SylixOS system nodes have run over 50,000 hours without interruption. 1. Industrial automation Universal configuration development human-machine interface. Electrical fire alarm system. Toll-by-weight and overlimit detector. Special vehicle and marine engine state monitor. Power environment monitoring station. Access control system event server. Universal PLC system. 2. Communications A variety of industrial fieldbus protocol converters. Industrial high-reliability IP router. Coal mine wireless personnel positioning system. 3. New energy Small photovoltaic power generation real-time data manager. Large-scale photovoltaic power generation node manager. Super capacitor car balance charge and discharge controller. 4. Weapon system Conventional submarine battery monitoring system. Wheeled armored vehicle real-time monitoring system. Manual OS. SpaceChain 24 Application Development Manual SpaceChain OS Manual OS. SpaceChain 25 Application Development Manual SpaceChain OS Chapter 2 Integrated development environment This chapter introduces the functions and use of the integrated development environment of the SylixOS operating system. This book takes the most common ARM processor on the market as an example (similar to the development, debugging and deployment of x86, MIPS, PowerPC processor and ARM processor). 2.1 Introduction to ARM processor 2.1.1 Brief introduction ARM (Advanced RISC Machines) can be deemed as a company's name, the generic name for ARM core microprocessor, or the name of a technology. ARM was founded in Cambridge, UK in 1991, and mainly sells authorization of chip design technology. At present, the microprocessor adopting AMR technical intellectual property (IP) core (i.e., ARM processor mentioned normally) has been widely used in industrial control, consumer electronic product, communication system, network system, wireless system and various product markets. ARM-based microprocessor application occupies over 75% of market share of 32-bit RISC microprocessor, and ARM technology is gradually seeping into every aspect of daily life. ARM processors can be divided into different series according to different kernels used. Division of these series is based on ARM7, ARM9, ARM10 and ARM11 kernels (suffix numbers of 7,9,10 and 11 indicate different kernel designs). Digital ascending indicates improvement in performance and complexity. In each series, there are also multiple changes in memory management, Cache and TCM processor extension. 2.1.2 Features Small volume, low power consumption, low cost and high performance; Support Thumb (16-bit) /ARM (32-bit) dual instruction sets, and be compatible with 8-bit / 16-bit device; Extensive use of registers, realizing rapid instruction execution; Most date operations are completed in the register; The addressing mode is flexible and simple with high execution efficiency; The instruction length is fixed. Manual SylixOS. Application Development Manual SpaceChain OS 2.1.3 Operating mode ARM processor has 7 operating modes in total, as shown in Table 2.1. Manual OS. SpaceChain 27 Application Development Manual SpaceChain OS Table 2.1 Operating mode of ARM processor Operating mode Instructions User mode (User) For execution of normal program System mode (System) Operate the operating system tasks with privilege General interrupt mode (lrq) For normal interrupt processing Fast interrupt mode (Fiq) For fast interrupt processing Management mode (Supervisor) Protected mode used for the operating system Abort mode (Abort) Enter the mode when data or instruction prefetch is terminated Undefined mode (Undefined) Enter the mode when the undefined instruction is executed 1. Privileged mode Except for the user mode, other modes are privileged modes. ARM internal registers and some on-chip peripherals can only be accessed in the privileged mode on the hardware design. In addition, the privileged mode can be freely switched to the processor mode, and the user mode cannot be directly switched to other modes. 2. Exception mode Except for the system mode, other 5 modes in the privileged modes are also collectively referred as exception modes. In addition to entry through program switching in privilege, they can enter it from specific abnormality. For example, enter the interrupt exception mode when the hardware generates the interrupt signal, enter the abort exception mode when the data without authority is read, and enter the abort exception mode of undefined instruction when the undefined instruction is executed. Where, the management mode is also called as the superuser mode, and the specific mode providing software interrupt for the operating system. It is precisely because of software interrupt that the user program can switch to the management mode through system call. 2.1.4 Register organization There are 37×32-bit registers in ARM processor, including 31 general registers and 6 status registers. These registers cannot be accessed at the same time. Programmability and Accessibility of the register depends on working state and specific operation mode of the microprocessor. However, general registers R0 to R14 and the program counter and Manual OS. SpaceChain 28 Application Development Manual SpaceChain OS one or two status registers can be accessed at any time. 1. General register The general registers include R0 to R15, which can be divided into three categories: Unbanked register R0 to R7. Banked register R8 to R14. Program counter PC (R15). In all operation modes, unbanked registers are pointed at the same physical register, and not used for special purpose by the system. Therefore, damage to the register data may be caused when conversion of operation modes is performed for interrupt or exception handling. R8 to R12 registers respectively correspond to two different physical registers. Access registers of R8_fiq to R12_fiq in fiq mode, and access registers of R8_usr to R12_usr in other modes. R13 and R14 registers respectively correspond to 6 different physical registers, one of the physical registers is shared by the user mode and the system mode, and other 5 physical registers correspond to other 5 different operation modes. R13_ and R14_ modes are adopted to identify different physical registers (modes are USR, FIQ, IRQ, SVC, ABT and UND). R13 is often used as the stack pointer in ARM instruction, but it is only a idiomatic usage. The user can also use other registers as stack pointers. Each operation mode of the processor has its own independent physical register R13. Therefore, R13 in the corresponding mode shall be initialized in the initialization part. When program operation enters the exception mode, the register to be protected can be placed into the stack pointed by R13. However, when the program returns from the exception mode, it shall be recovered from the corresponding stack. R14 is also called as the subroutine link register or the link register LR. When the call instruction of BL subroutine is executed, R15 (program counter PC) backup can be obtained from R14, and R14 can be used to save the return address of the subroutine in each operation mode. The register R15 is called as the program counter (PC). Although R15 can be used as the general register, but it is not used in this way usually, because there are some special restrictions on use of R15, and execution results of the program are unknown if these restrictions are violated. Manual OS. SpaceChain 29 Application Development Manual SpaceChain OS 2. Status register Status registers include the current program status register and the saved program status register. CPSP can be accessed in any operation mode, which includes the condition flag bit, interrupt disable bit, pattern flag bit of current processor, and other related control and status bits. There is a special physical status register SPSR in each exception mode, SPSR is used to save the current value of CPSR in case of any exception, and CPSR can be recovered by SPSR when quit from exception. Since the user mode and the system mode do not belong to the exception mode, they do not have SPSR, and results are unknown when SPSR is accessed in the two modes. 2.1.5 Instruction structure ARM microprocessor supports two instruction sets in newer system structure: ARM instruction set and Thumb instruction set. Where, the length of ARM instruction set is 32 bits, the execution cycle is mostly single cycle, and the instructions are executed with conditions; The Thumb instruction can be seen as a subset of the ARM instruction set in the compressed form, it has 16-bit code density, while the execution efficiency is lower than that of ARM instruction. The Thumb instruction has the following features: The instruction execution condition will not be used frequently. The source register is same with the target register frequently. The number of registers used is relatively less. The value of the constant is relatively small. The Barrel Shifter in the kernel is not used frequently. 2.2 Introduction to RealEvo-IDE 2.2.1 Brief introduction RealEvo-IDE is the dedicated integrated development environment for the SylixOS operating system. It can enable development of SylixOS operating system application, BSPs, driver and shared library to be simple and efficient. RealEvo-IDE includes the following parts: SylixOS Base project: The project includes libsylixos (high-performance SylixOS kernel), libVxWorks (VxWorks compatible library), libcextern (C extension library), Manual OS. SpaceChain 30 Application Development Manual SpaceChain OS liblua, libluaplugin (Lua script support), libzmodem (zmodem protocol support) and libsqlite3 (SQLite database) and so on; Various project templates: BSP project template, application project template, shared library project template, kernel module project template, as shown in Figure 2.1; Development tools: automatic upload tools, RealEvo-Simulator, kernel behavior monitor and so on; Integrated development environment: It can help the use manage and build the project, compile corresponding code according to different hardware platforms, and can organize and manage communication, debugging and operation with SylixOS target system; Complier: having powerful code editing function; Compiler: including ARM, MIPS, PowerPC, x86, c6x, SPARC, Lite and other platform compilers. Manual OS. SpaceChain 31 Application Development Manual SpaceChain OS Figure 2.1 RealEvo-IDE project template Note: the experience version of RealEvo-IDE does not include the SylixOS Lite Project, and the function can be obtained by purchasing the Pro version of RealEvo-IDE. Figure 2.2 shows the communication and debugging relationship between RealEvo-IDE integrated development environment and SylixOS target system. Figure 2.2 RealEvo-IDE and target machine 2.2.2 Functions 1. Edit Code edition is one of the most fundamental and important work for software development, and an efficient code editor will yield twice the result with half the effort. The RealEvo-IDE editor has multiple color assortment schemes and functions of code static analysis and supplement. Therefore, code development is more efficient. 2. Compilation The RealEvo-IDE toolbar contains a one-key compilation button on the left, as shown in Figure 2.3. After the project you want to compile is selected, click this button for compilation. In addition, you can click the right mouse button on the project requiring compilation and select "Build Project" for compilation. Figure 2.3 compilation button Manual OS. SpaceChain 32 Application Development Manual SpaceChain OS 3. Debugging SylixOS has a debugging server with powerful function, which can realize online debugging of applications on devices with SylixOS running. RealEvo-IDE provides the supported debugging tool, which can conveniently debug SylixOS application. At present, RealEvo-IDE supports three debugging methods: automatic push debugging, manual debugging, and remote attachment debugging (Attach). 4. Push In order to more conveniently and rapidly deploy SylixOS application and driver to the SylixOS device, RealEvo-IDE provides the function of one-key push to conveniently deploy the program complied on the SylixOS target system (see Section 4.1.4). 2.3 Introduction to GCC toolchain The GCC toolchain is a group of compilation kit (GCC), plus some binary files (such as the linking tool LD, object file packaging tool AR and so on) and some standard compilation kits consisting of C library. The GCC toolchain with RealEvo-IDE is the compilation tool formed by adding SylixOS-related elements and more efficient library files based on standard GCC and satisfying SylixOS requirements. 2.4 Installation of RealEvo-IDE 2.4.1 Acquisition of SylixOS development kit SylixOS development kit is divided into experience and commercial versions, and you can obtain a experience version of the development kit in the following ways: visit SylixOS official website (www.sylixos.com or www.acoinfo.com), and you can apply for a complete set of SylixOS integrated development environment. 2.4.2 Installation of SylixOS development kit After a set of SylixOS development kit is obtained successfully, one can carefully read the document in CD to obtain more information about the SylixOS integrated development kit. The following process is to install RealEvo-IDE through the SylixOS development kit CD: Open SylinOS IDE, double click to open the InstallWizard.exe file, and you will see Manual OS. SpaceChain 33 Application Development Manual SpaceChain OS installation toolset of the SylixOS Development Kit, precautions, and SylixOS website information. It can be seen from Figure 2.4 that SylixOS IDE includes installation of “RealEvo-IDE”, “QtCreator” and “RealEvo-QtSylixOS”. Figure 2.4 Integrated development environment toolset It is installed according to the sequence in Figure 2.4, RealEvo-IDE in SylixOS integrated development environment can be used only after registration. For the specific registration process and the place to be noticed during registration, view the file of RealEvo Software Registration Procedure in CD. After above process, installation of SylixOS integrated development environment is completed. Manual OS. SpaceChain 34 Application Development Manual SpaceChain OS Chapter 3 Brief introduction to Shell 3.1 What's Shell Shell is the "shell" program of the operating system, it provides the user with a user interface based on command-line type, and can be called as the command parser. The system developer usually use the interface to operate the computer. Almost all operating systems include Shell programs, for example: the more common shell in Linux is the Bash program, and the shell program in Windows is cmd.exe. SylixOS is no exception, and also includes its own Shell program: ttinyShell. The ttinyShell program is the simplest and most convenient interface for the system developer to operate the SylixOS operating system. It has the functions similar to those of the the Linux system, what's different is that ttinyShell runs in the kernel space, and it is ① not an application . The ttinyShell can run application, and many common commands solidified in SylinxOS kernel are built in. The operation interface of ttinyShell program is shown in Figure 3.1. Figure 3.1 ttinyShell operation interface 3.2 Instructions for common Shell commands Some common ttinyShell built-in commands will be briefly introduced in the section, which are divided into the system command, file command, network command, time command, dynamic load command and other commands, and detailed instructions can be Manual SylixOS. Application Development Manual SpaceChain OS viewed on the Sylix OS device with the help [keyword] command. The kernel version is different from clipping configuration. Therefore, ttinyShell built-in commands will be different on SylixOS systems with different versions and configurations. 3.2.1 System command For ttinyShell built-in system commands, see Table 3.1. Table 3.1 Common built-in system commands of SylixOS Command name Brief description help Display list of all built-in commands of ttinyShell free Display current memory information of the system echo Echo parameters entered by the user ts View thread information in the system tp Check information of the thread blocked in the system ss View status conditions of all threads and interrupt system stacks in the system ps View information of all system processes touch Create a regular file ints View information of the system interrupt vector table mems View memory utilization of kernel and system memory heap of the operating system zones View partition status conditions of physical pages of the operating system env View the global environment variable table of the operating system varsave varload Save the environment variable table of current operating system, and the default save path is /etc/profile Extract the load environment variable table from the file with specified parameters, and from /etc/profile at default vardel Delete a appointed system environment variable cpuus View cpu utilization rate top View cpu utilization rate kill Send the signal to the appointed thread or process, SIGKILL signal at default drvlics Display table information of all device drivers installed in the system devs Display all devices mounted in the system buss Display all bus information mounted in the system tty Display tty file corresponding to current Shell terminal clear Clear the current screen. aborts Display statistical information of exception handling of current operating system sprio Set priority of the appointed thread renice Set priority of the appointed process hostname Display or set the host name of current SylixOS mirror image login Switch the user and log in again Manual OS. SpaceChain 36 Application Development Manual SpaceChain OS who View identity of the current login user shutdown Shut down or restart the system monitor Start, shut down, or set the kernel tracker pcis Print related information of PCI bus and equipment of system lsusb Print related information of USB bus and USB device of the system (dependent on USB library) which Check the file location appointed by the parameter exit Quit current Shell terminal The following are several common and more important commands. Command ts The information of current running thread of SylixOS system can be viewed with “ts” command. [Command format] ts [pid] [Common option] None [Instructions for parameters] pid :process ID The following are detailed meanings of output information of the ts command: # ts① thread show >> NAME TID PID PRI STAT ERRNO DELAY PAGEFAILS FPU CPU ---------------- ------- ----- --- ---- ------- ---------- --------- --- --t_idle 4010000 0 255 RDY 0 0 0 0 …… thread : 16 Various meanings of output are as follows: ① "#" represents the administrator user in SylixOS, and here is no user name or other information compared with the reality ([root@sylixos_station:/]#). In order to avoid differences among different users, "#” is used to represent operation under ttinyShell in the book, and the path information is ignored. Manual SpaceChain OS. 37 Application Development Manual SpaceChain OS NAME: it is the thread name, for example, t_idle represents the IDLE thread of SylixOS; TID: it is the thread ID (handle), represented with hexadecimal system, such as 4010001; PID: it is ID of the process belonging to the thread, represented with decimal system, which is same with the representation method in UNIX system (0 represents the kernel thread of the operating system); PRI: it is the priority of the thread, represented with decimal system (the smaller the value, and the higher the priority.), such as 255; STAT: it is the current state of the thread, and RDY represents the ready state (for the thread state, see 6.2 Thread State Machine); ERRNO: it is the running error number; DELAY: it is the thread delay; PAGEFAILS: it is missing page interruption counting; FPU: it represents whether the hardware floating-point unit (fpu) is used; CPU: it represents on which CPU the thread is running (there may be other values on the multi-core system); thread: it represents the total number of current running thread. 2. Command tp The blockage information of current running thread of SylixOS system can be viewed with the command. [Command format] tp [pid] [Common option] None [Instructions for parameters] pid :process ID The following are detailed meanings of output information of the tp command: # tp thread pending show >> NAME TID PID STAT DELAY Manual OS. PEND EVENT OWNER SpaceChain 38 Application Development Manual SpaceChain OS ---------------- ------- ----- ---- ---------- ----------------------------t_except 4010002 0 SEM 0 10010003:job_sync …… pending thread : 14 Various meanings of output are as follows: NAME: it is the name of the thread TID: it is ID of the thread; PID: it is ID of the process; STAT: it is the current state of the thread; DELAY: it is the thread delay; PEND EVENT: it represents what kind of event the thread is currently blocking, such as semaphore, message queue and so on; OWNER: it represents the thread ID occupying the blocking object when the thread is blocked (the domain will include the thread ID occupying the lock with occupy lock in case of any deadlock); pending thread: it represents that 14 threads among the running threads are blocked on an event. 3. Command ps The information of the running process of SylixOS system can be viewed with the ps command. [Command format] ps [Common option] None [Instructions for parameters] None The following are detailed meanings of output information of the ps command: # ps NAME FATHER STAT PID GRP MEMORY UID GID USER ---------------- ---------------- ---- ----- ----- ---------- ----- ----- -----kernel R 0 Manual OS. 0 0KB 0 0 root SpaceChain 39 Application Development Manual SpaceChain OS app R 2 2 196KB 0 0 root total vprocess: 2 Various meanings of output are as follows: NAME: process name (program name); FATHER: it represents the father process, orphan represents an orphan process, i.e., there is no father process; STAT: it represents the process state, as shown in Table 3.2; PID: it is ID of the process; GRP: it is ID of the process group; MEMORY: it is the total memory consumed by the process (unit: byte); UID: it is the user ID of the process; GID: it is the user group ID of the process; USER: it is the user name of the process, such as root. Table 3.2 Process state State label Note I Initial state of the process, the process has not started to run yet; R Running state of the process, the process is running; T Stop state of the process, the process stops running for a certain reason; Z Zombie state of the process, the process has exited, waiting for the resource to be recycled. 4. Command ints The interrupt vector information of SylinOS system can be displayed with the ints command. [Command format] ints [cpuid start] [cpuid end] [Common option] Manual OS. SpaceChain 40 Application Development Manual SpaceChain OS None [Instructions for parameters] cpuid start :CPU ID at the beginning cpuid end :CPU ID in the end The following are detailed meanings of output information of the ints command: # ints interrupt vector show >> IRQ NAME ENTRY CLEAR PARAM ENABLE RND PREEMPT CPU 0 ---- -------------- -------- -------- -------- ------ --- ------- ------------7 dm9000_isr 20013978 0 2c62fbe8 true 4068 …… interrupt nesting show >> CPU MAX NESTING IPI ----- ----------- ------------0 1 0 interrupt vector base : 0x2c7a96a8 Various meanings of output are as follows: IRQ: it is the interrupt number; NAME: it is the registered interrupt name; ENTRY: it is the address of the interrupt service function, represented with hexadecimal system, such as 20013978; CLEAR: it is the address of the interrupt cleaning function; PARAM: it is the parameter address of the interrupt service function; ENABLE: it represents whether the interrupt is enabled; RND: it represents whether it can be used as the system random number seed; PREEMPT: it represents whether occupation is allowed; CPU 0 (0 represents the CPU number): it represents the number of interrupts generated on CPU0. For example, dm9000_isr generates 4068 interrupts on CPU0. 3.2.2 File command Manual OS. SpaceChain 41 Application Development Manual SpaceChain OS Built-in file commands of ttinyShell are shown in Table 3.3. Manual OS. SpaceChain 42 Application Development Manual SpaceChain OS Table 3.3 Common built-in file commands of the system Command name ls ll files fdentrys sync Brief description List files in the appointed directory, current directory at default List the detailed information of the file in the appointed directory, current directory at default List the information of the file opened in the system kernel (excluding the file opened by the process) Lists all file information on which the operating system is working (including files opened by the process) Write all system cache files, devices, and disk information in the corresponding physical device logfileadd Add the appointed kernel file descriptor to the kernel log print function logfileclear Clear the appointed kernel file descriptor from the list of the kernel log print files logfiles Display the list of kernel log print files loglevel Display or set the print level of current kernel log cd Switch the current directory pwd View current working directory df View the file system information of the appointed directory tmpname Obtain a temporary file name which can be created mkdir Create a directory mkfifo Create a naming pipeline. Notice: it can only be created under the device of the root file system rmdir Delete a directory rm Delete a file mv Move or rename a file cat View contents of a file cp Copy a file cmp Compare contents of two files dsize Calculate all file information contained in an appointed directory chmod Set the permission bit of of the file or directory mkfs Format the appointed disk shfile Execute the appointed Shell script mount Mount a volume umount Uninstall a volume showmount View all volumes mounted in the system ln Create the symbolic link file dosfslabel View the volume label of the fat file system fatugid Set the user and group id of the fat file system mmaps Display the system mmap information fdisk Disk partition Manual OS. SpaceChain 43 Application Development Manual SpaceChain OS 1. Command fdisk The disk partition can be displayed or the partition table of the disk device can be created with the fdisk command [Command format] fdisk [-f] [block I/O device] [Common option] -f:Specified disk device [Instructions for parameters] block I/O device:block device,such as /dev/blk/sdcard0 The following is how to use the fdisk command: Display udisk0 partition table: # fdisk /dev/blk/udisk0 Create the partition table: # fdisk –f /dev/blk/udisk0 4 partitions can be created with fdisk (number of partitions: 1 to 4), the size percentage of each partition shall be indicated (such as 40%), one can select whether the appointed partition is the active partition (including: active and inactive). The file system types currently supported include: 1:FAT, 2: TPSFS (SylixOS power-failure security file system), and 3: LINUX. 3.2.3 Network command Built-in network commands of ttinyShell are shown in Table 3.4. Manual OS. SpaceChain 44 Application Development Manual SpaceChain OS Table 3.4 Common built-in network commands of the system Command name Brief description route Add, delete, modify or view the system routing table netstat View the network state ifconfig Configure the network interface information ifup Enable a network interface ifdown Disable a network interface arp Add, delete, or view the ARP table ping ping command ping6 IPv6 ping command tftpdpath View or set the local path of tftp server tftp Receive and send a file with the tftp command ftpds Display ftp server information ftpdpath View or set the initialization path of ftP server nat Start, shut down, or set service of NAT virtual network address nats View the service state of current NAT virtual address npfs View the state of the network packet filter npfruleadd Add a rule of the network packet filter npfruledel Delete a rule of the network packet filter npfattach Enable the network packets filter on the appointed network interface npfdetach Disable the network packets filter on the appointed network interface flowctl ioctl flow control 1. Command ifup and ifdown The ifup command enables the appointed network interface, and can open and close dhcp lease at the same time. The ifdown command can disable the appointed network interface. [Command format] ifup [netifname] [{-dhcp | -nodhcp}] ifdown [netifname] [Common option] -dhcp :Open DHCP lease -nodhcp :close DHCP lease [Instructions for parameters] netifname :network interface name(eg:en1) Manual OS. SpaceChain 45 Application Development Manual SpaceChain OS The following shows how to use ifup and ifdown commands: Enable network interface en1; ifup en1 Enable network interface en1 and open dhcp lease; ifup en1 –dhcp Enable network interface en1 and stop dhcp mode; ifup en1 –nodhcp Stop network interface en1. ifdown en1 2. Command flwoctl The flowctl command enables the flow control function, which can perform flow control for the network interface, IPv4 and IPv6. [Command format] flowctl [cn] [type] ips ipe [proto] ps pe dev [ifname] [dl][ul] bufs [Common option] cn : add : add del : delete chg : change type : ip :traffic control for IP address if :traffic control for network interface proto : tcp :TCP protocol udp :UDP protocol all :default protocol [Instructions for parameters] When type is ip: ips :IP address at the beginning ipe :IP address in the end ps :port number at the beginning pe :port number in the end when type is if: Do not need to enter ips, ipe, proto, ps, and pe Manual OS. SpaceChain 46 Application Development Manual SpaceChain OS ifname :network interface name(eg:en1) bufs :buffer size dl :download speed ul :upload speed The following shows how to use the flowctl command: Add the flow control information of the network interface; flowctl if dev en1 50 100 64 Add IPv4 flow control information; flowctl add ip 192.168.1.1 192.168.1.10 tcp 20 80 dev en1 50 100 64 Delete IPv4 flow control information; flowctl del ip 192.168.1.1 192.168.1.10 tcp 20 80 dev en1 Modify IPv4 flow control information. flowctl chg ip 192.168.1.1 192.168.1.10 tcp 20 80 dev en1 100 200 For flow control information, the uplink and downlink speed can only be modified, and modification in ip and the port number is invalid. For detailed information of other network commands, see Chapter I15 Network I/O. 3.2.4 Time command Built-in time commands of ttinyShell are shown in Table 3.5. Manual OS. SpaceChain 47 Application Development Manual SpaceChain OS Table 3.5 Common built-in time commands of SyinxOS Command name Brief description date Display or set the system time times Display the current time of the system hwclock Display or synchronize the operating system and hardware RTC clock 1. Command date The system time can be displayed or set with the date command [Command format] date [-s {time | date}] [Common option] -s: set time [Instructions for parameters] time:hour、minute、second format date:year、month、day format The following shows how to use the date command: Display the system time; date Set 24h time format of the system; date –s 18:15:09 Set system date. data –s 20150918 2. Command hwclock hwclock command can display or synchronize hardware RTC clock. [Command format] hwclock [{--show | --hctosys | --systohc}] [Common option] --show : show RTC time Manual OS. SpaceChain 48 Application Development Manual SpaceChain OS --hctosys :Synchronize RTC time to system time --systohc :Synchronize system time to RTC time [Instructions for parameters] None The following shows how to use the hwclock command: Display hardware RTC time; hwclock –show Synchronize hardware RTC time to system time; hwclock --hctosys Synchronize system time to hardware RTC time. hwclock –-systohc 3.2.5 Dynamic loading command The ttinyShell built-in dynamic loading command is shown in Table 3.6. Table 3.6 SylixOS common built-in dynamic loading commands Command name Brief description debug Debug a process dlconfig Configure working parameter of the dynamic linker modulereg Register a kernel module moduleunreg Uninstall a kernel module modulestat View a kernel module or dynamic link library file information lsmod See all kernel module information loaded by the system modules View all kernel module and process dynamic link library information loaded by the system 1. Command debug The debug command is used to debug the SylixOS application. [Command format] debug [connect options] [program] [argments...] [Common option] None Manual OS. SpaceChain 49 Application Development Manual SpaceChain OS [Instructions for parameters] connect options :Connection options program :program name argments... :Program parameter list The following shows how to use the debug command: Through network debugging, the SylixOS device starts the debugger. localhost:1234 represents the debugging port number of the SylixOS device: 1234, ./app is the application program to be debugged in the current directory; debug localhost:1234 ./app Through serial debugging, the SylixOS device starts the debugger. The default serial port baud rate is 115200 bps, the number of data bits is 8, the stop bit is 1 bit, and there is no parity check. debug /dev/ttyS1 ./app 3.2.6 Other commands The ttinyShell built-in other commands are shown in Table 3.7. Table 3.7 SylixOS common built-in other commands Command name shstack Brief description Display or set the Shell task stack size, and setting is only valid for the shell started later leakchkstart Start system memory leak tracker leakchkstop Turn off system memory leak tracker leakchk Memory leak check xmodems Send a file thorough the xmodem protocol xmodemr Receive a file thorough the xmodem protocol untar Unpack or unzip a tar or tar.gz file package gzip Compress or decompress a file vi Start the vi editor 1. Commands leakchkstart, leakchkstop, and leakchk The above commands can detect system memory leak. [Command format] leakchkstart [max save node number] [pid] leakchkstop Manual OS. SpaceChain 50 Application Development Manual SpaceChain OS leakchk [Common option] None [Instructions for parameters] max save node number :Maximum storage nodes pid :Process ID The following shows how to use the above commands: Start the memory leak tracker, the second parameter 2048 is the maximum number of nodes tracked, and the third parameter 0 is detection of kernel memory. When the parameter is less than 0, it represents detection of all memory (kernel and user process). If the parameter is larger than 0, it represents detection of memory of the specified process, leakchkstart 2048 0 Detect memory leaks, leakchk Stop the memory leak tracker. leakchkstop Note: the number of nodes in the second parameter is the number of memory blocks in the fixed-length memory, and these memories are used for temporary cache. For detailed description of fixed-length memory, see Chapter 12 Memory Management. 3.3 Environment variable Environment variable is an object with specific name in the operating system. It contains the information which one or more applications will use. It is generally used to specify some parameters of the operating system or the application execution environment, such as the temporary directory location, application search location, etc. The configuration information of environment variables is usually saved in the /etc/profile file. A complete environment variable definition table is saved in Shell environment. When the system is started, BSP will automatically import definition of environment variables in this file into the Shell environment. Users can use the env command to view all environment variables of the system. When an application is started, this environment variable will be imported into the application process (meanwhile, creating several environment variables which indicate application parameters, such as HOME, etc.). Manual OS. SpaceChain 51 Application Development Manual SpaceChain OS The environment variable is equivalent to some parameters set for the system or the user application, and the specific role is related to the specific environment variable. The common environment variables of SylixOS are shown in Table 3.8. Table 3.8 Description of SylixOS environment variables Name of the environment variable Brief description XINPUT_PRIO xinput subsystem priority LANG Language selection LD_LIBRARY_PATH Dynamic loader search path PATH Application search path NFS_CLIENT_PROTO NFS client protocol NFS_CLIENT_AUTH NFS login authentication mode SYSLOGD_HOST syslogd remote address SO_MEM_PAGES Number of page initially occupied by the application memory heap TSLIB_TSDEVICE Touch screen device MOUSE xinput subsystem detection mouse device set KEYBOARD xinput subsystem detection keyboard device set TZ System time zone TMPDIR Temporary folder The user can also quote the value of the environment variable in the command line, and the reference format is ${VAR_NAME}. When this parameter is encountered, ttinyShell will automatically use the contents of the environment variable to substitute ${VAR_NAME}. For example: ttinyShell executes the echo ${PATH} command, and the system will echo the contents of PATH environment variable. Users can add their own environment variables in the following format: VAR=VALUE Use command varsave to save the Shell environment variable to the default configuration file /etc/profile. An environment variable can have multiple different values. Different values are separated with a colon, as shown below: PATH /usr/bin:/bin:/usr/local/bin:/home/user 3.4 Root file system The first file system automatically mounted after SylixOS is started is called as the root file system. Unlike Linux system, SylixOS root file system is a virtual file system. After the power failure, modification of the file system will not be saved, so SylixOS can work on a machine without non-volatile memory (usually a hard disk or other magnetic disc Manual OS. SpaceChain 52 Application Development Manual SpaceChain OS memories). On this file system, SylixOS will automatically create the dev, media and mnt directories. Other standard directories require BSP to do mounts or symbolic links at the initialization phase. The standard directory structure used by SylixOS is shown in Table 3.9. Manual OS. SpaceChain 53 Application Development Manual SpaceChain OS Table 3.9 Instructions for directory of SylixOS root file system Directory name Brief description qt Dynamic link library of Qt graphics system and other Qt resource directories tmp Temporary directory var Store various changed file directories, such as logs, buffer files etc. root Home directory of root user home Home directory of other users apps Application directory sbin System program directory bin Common Shell program directory usr User program library and environment directory lib System program library and environment directory etc Directory of system or other application configuration files boot Directory of operating system boot image media Directory of automatic mount of file system device (such as USB, SD card and so on) proc Directory of system kernel information file mnt Directory of dynamic file system mount dev System device file directory Manual OS. SpaceChain 54 Application Development Manual SpaceChain OS 3.5 Operation application ttinyShell can not only execute the built-in command, but also execute user application. The method of executing the application is the same with that of executing the built-in command. When the user types the command name and parameters in the Shell interface and clicks Enter, ttinyShell will firstly detect whether the command is a user application. If so, execute the user application first. If not, check whether the command is a built-in command. If not, ttinyShell will print the error message. The ttinyShell command detection sequence is as follows: (1) Detect whether the command is a file path. If the file exists, execute the specified application. (2) Detect the path specified by the PATH environment variable. if the file exists, execute the specified application. (3) Detect whether the command is a built-in command. If so, execute the built-in command. There are two ways for ttinyShell to execute the built-in command or application: synchronous and asynchronous. Synchronous mode: when ttinyShell executes the built-in command, the command code is in the context of the ttinyShell thread at default. When the application is executed, ttinyShell will create a process, and load the application code in the process. At the same time, ttinyShell blocks itself, and waits for the process to end to resume execution; Asynchronous: when the user enters & symbol behind the command keyed in, ttinyShell will execute the command asynchronously. When ttinyShell executes the built-in command, it will create a kernel thread to execute the command code. When the application is executed, ttinyShell will create a process and load application code in this process. Unlike the synchronization method, ttinyShell does not wait for the command to complete, but is immediately ready to receive the user's next command. 3.6 I/O redirection Each application has three standard file descriptors (for detailed introduction, see Chapter 5 I/O System): 0, 1 and 2. 0 represents standard input, i.e., files read by scanf, getc and other functions; 1 represents standard output, i.e, files written by printf, putc and other functions; 2 represents standard error, i.e., files written by perror and other functions. Manual OS. SpaceChain 55 Application Development Manual SpaceChain OS At default, ttinyShell will use current terminal device as the standard file, and the process created by ttinyShell will inherit settings of ttinyShell standard file. Of course, the user can also set the standard file for the command. When there is the I/O redirection parameter behind command string, ttinyShell will analyze redirection expression, and set the new standard file descriptor. The setting method is as follows: Redirect standard output to the file_path file; echo “aaa“ 1>file_path If it is required to add a certain file, one can use 1>>file_path. The method of defining a standard error file is similar to that of a standard output file, such as ① 2>file_path . It is required to locate standard input to a certain file, and parameter 0 "Debug Configurations" to open the debugger interface; Select "SylixOS Remote Application", click "New launch configuration" button to create a new debugger object, and fill in the debugger object name in the "Name" edit box; Manual OS. SpaceChain 65 Application Development Manual SpaceChain OS Figure 4.9 Debug configuration Note: one can quickly start debugging by right-click on the successfully compiled application to select "Debug As" → "SylixOS Remote Application". Click Figure 4.9 Debug button to enter the debugging interface as shown in Figure 4.10. Here are some RealEvo-IDE debugging commands: Start debugging (full speed operation) "Run" → "Resume (F8)"; Step into the function "Run" → "Step Into (F5)"; Step over "Run" → "Step Over (F6)"; Quit from the function "Run" → "Step Return (F7)"; Stop debugging "Run" → "Terminate (Ctrl + F2)"; Set the breakpoint "Run" → "Toggle Breakpoint (Ctrl + Shift + B)". Manual OS. SpaceChain 66 Application Development Manual SpaceChain OS Figure 4.10 Debug operation interface 2. Manually start debugging To manually start debugging, it is required to manually start GDB server in SylixOS Shell, and the specific method is as follows: Use the debug command to start the application (such as helloworld) to be debugged, as shown in Figure 4.11 (for use of the debug command, see 3.2.5); Manual OS. SpaceChain 67 Application Development Manual SpaceChain OS Figure 4.11 Manually start debug Select the project to be debugged (such asa "helloworld" project), and select the menu "Run" -> "Debug Configurations" to open the debugger interface; Select "SylixOS Remote Application", create a new debugger object, select the "Select other..." button, enable "Use configuration specific settings" in the pop-up box, select "SylixOS Manual Remote App Debugging Launcher" in the drop-down list, click "OK" to return to the "Debug Configurations" dialog box, as shown in Figure 4.12 (the arrow indicates the operation sequence); Manual OS. SpaceChain 68 Application Development Manual SpaceChain OS Figure 4.12 Manual debugging of configuration Open “Debugger”→“Connection” tab, select “TCP” in the “Type” drop-down box, enter the IP address of SylixOS device in the “Host name or IP address” edit box, and enter the port number in the “Port number” edit box (the port number must be consistent with that when the debug command is started. Click "Apply" to apply modification, and click Debug to start debugging, as shown in Figure 4.13 Manual OS. SpaceChain 69 Application Development Manual SpaceChain OS Figure 4.13 Set debug configuration 3. Attach debugging When the program is running (the program may have been running for a long time), a serious error (the program did not stop because of this error) appears. Because the program cannot stop at the moment, we need a special method to find the problem when the program is running. The Attach debugging method is a method which can debug the run the program. The debugging method exactly satisfies the above conditions. SylixOS supports the Attach debugging method. The following introduces how to use the Attach method to debug the SylixOS application: Modify the helloworld project code, as shown in program list 4.1; Start the application manually, as shown in Figure 4.14, and use the ps command to view the process ID; Program List 4.1 helloworld project code #include #include int main (int argc, char *argv[]) Manual OS. SpaceChain 70 Application Development Manual SpaceChain OS { while (1) { printf("Hello SylixOS!\n"); sleep(1); } return (0); } Note: the program code is added in the program code, and the purpose is to let the program run continuously. sleep is a sleep function, which will make the program run with time delay by a second, so that other threads of SylixOS have the opportunity to be executed. Figure 4.14 Helloworld program Select the "helloworld" project, and select the menu "Run" → "Debug Configurations" to open the debugger interface. Select "SylixOS Remote Application", create a new debugger object, the default name, select the "Select other..." button, enable "Use configuration specific setting" in the pop-up box, select "SylixOS Attach Remote App Debugging Lancher" in the drop-down list, click "OK" to return to the "Debug Configurations" dialog box, as shown in Figure 4.15 (the arrow indicates the operation sequence); Manual OS. SpaceChain 71 Application Development Manual SpaceChain OS Figure 4.15 Attach debugging Set “Target process ID”. This option is filled in with the process ID previously viewed. Finally, click “Apply” to complete setting. 4.1.7 Non-stop debugging mode During multi-thread debugging, it is often necessary to debug a specific thread. Non-stop mode allows the debugger to stop only the thread set with breakpoint when it encounters a breakpoint, and the other threads continue to run. In SylixOS system, the three debugging modes described in Section 4.1.6 all support Non-stop mode. The setting method is as follows: select "Run" → "Debug Configurations" dialog box, open "Debugger" → "Main" property page, enable "Non-stop mode" to open Non-stop mode, as shown in Figure 4.16. Manual OS. SpaceChain 72 Application Development Manual SpaceChain OS Figure 4.16 Non-stop mode 4.2 Hello Library In essence, the library is a binary form of executable code which can be loaded into the memory for execution for the operating system. SylixOS's libraries are divided into static libraries and shared libraries (also called dynamic libraries or dynamic link libraries). The difference is that the library code is loaded at different times: The static library is linked to the object code during program compilation (the file extension name is usually .a). When the program is running, it is no longer necessary to dynamically load the library. If multiple applications requires the the same static library, each application code part is linked to this static library.Therefore, the application code has large size; The dynamic library will not be linked to the object code (the file extension name is usually .so) when the program is compiled, but is only loaded when the program runs. Therefore, the dynamic library file shall be stored in the target system when the program is running. When multiple applications use the same dynamic library, the dynamic library is shared by several application processes in the memory. Therefore, the application code has small size. Manual OS. SpaceChain 73 Application Development Manual SpaceChain OS 4.2.1 Create Hello Library Project The method for creating a library project is similar to that for creating an application project. The difference is that you need to select "SylixOS Shared Lib" on the project template, as shown in Figure 4.17. Click "Finish" to complete creation of the library project. Figure 4.17 Library project settings 4.2.2 Compile Hello Library Project The compilation method is the same with the method to compile helloworld project. Manual OS. SpaceChain 74 Application Development Manual SpaceChain OS After compilation, the file list shown in Figure 4.18 will be generated after compilation. Figure 4.18 List of library files 4.2.3 Deploy the library file The compiled result file is used to deploy the library file to the target system according to the method described in Subsection 4.1.4. SylixOS shared library file is usually stored in the directory specified under /lib of the SylixOS system or the environment variable LD_LIBRARY_PATH. The corresponding adjustment can be made according to the specific situation. 4.2.4 Modify Hello World application This section describes how SylixOS application uses the dynamic library and makes the following changes based on the helloworld project: Figure 4.19 Link library settings Manual OS. SpaceChain 75 Application Development Manual SpaceChain OS Note: Modify the helloworld source file, as shown in Program List 4.2; Program List 4.2 helloworld code #include #include extern void lib_func_test(void); int main (int argc, char *argv[]) { while (1) { lib_func_test(); sleep(1); } return (0); } Manual OS. SpaceChain 76 Application Development Manual SpaceChain OS 4.2.5 Run the Hello world Application Recompile the code modified in the previous section, and then upload the result file generated to the SylixOS device and run it. Observe the running running, as shown in Figure 4.20. Figure 4.20 Running results 4.2.6 Debug Hello world applications and dynamic library In the debugging process, sometimes it is required to jump to the library function for further analysis of the program. At the moment, one must use RealEvo-IDE dynamic library debugging to complete it. In order to achieve dynamic library debugging, RealEvo-IDE shall be subject to following settings. Select "helloworld" project, open "Debug Configurations" dialog box, open "Debugger" tab, select "Add library paths from project setting" at "Library Paths Setting", click "Apply" button to complete settings, as shown in Figure 4.21. Manual OS. SpaceChain 77 Application Development Manual SpaceChain OS Figure 4.21 Settings of dynamic library debugging Manual OS. SpaceChain 78 Application Development Manual SpaceChain OS Chapter 5 I/O System 5.1 I/O System I/O system is also called as the input / output system. SylixOS is compatible with the POSIX standard input / output system. I/O concept of SylixOS inherits I/O concept of UNIX operating system, which considers everything as files. Same with UNIX operating system, files in SylixOS are also divided into different types. 5.1.1 File type The regular files and directory files are the most common in SylixOS system, but there are other special file types, including the following: Regular file, it is the most common file type, and such files contain certain forms of data. Such data, regardless of plain text or binary, makes no difference to SylixOS. It shall be noted that the kernel must understand its format for a binary executable file. The binary executable file of SylixOS follows a standardized format, which allows SylixOS to determine the program code and data loading location (for details, see Chapter 19 Dynamic Loading); Directory files, which contain the names of other files and pointers pointed at the information related with these files; Block device files. The I/O interface standards provided by such files conform to SylixOS's definition of block devices. Character device file, which is a standard unbuffered device file. The most common device file in the system is the character device file; FIFO file. This type of file is used for inter-process communication, and it sometimes is also called as named pipe; Socket files, which can be used for inter-process network communication (for detailed introduction, see Chapter 15 Network I/O); Symbolic link, this type of file points to another file. The information of file type is contained in the st_mode member of the stat structure. It can be judged through the macros shown in Table 5.1, and the parameters of these macros are the type values of the member st_mode. Table 5.1 Macro name Manual File type File type SylixOS. Application Development Manual SpaceChain OS S_ISDIR(mode) Directory file S_ISCHR(mode) Character device file S_ISBLK(mode) Block device file S_ISREG(mode) Regular file S_ISLNK(mode) Symbolic link file S_ISFIFO(mode) Pipe or a named pipe S_ISSOCK(mode) Socket file Here is an example of the print file type: Program List 5.1 Print file type #include #include int main (int argc, char *argv[]) { struct stat mystat; int ret; if (argc < 2) { fprintf(stderr, "argc error.\n"); return (-1); } ret = stat(argv[1], &mystat); if (ret < 0) { perror("stat"); return (-1); } if (S_ISDIR(mystat.st_mode)) { fprintf(stdout, "file: %s is dir file.\n", argv[1]); } if (S_ISCHR(mystat.st_mode)) { fprintf(stdout, "file: %s is char file.\n", argv[1]); } if (S_ISBLK(mystat.st_mode)) { fprintf(stdout, "file: %s is block file.\n", argv[1]); } Manual OS. SpaceChain 80 Application Development Manual SpaceChain OS if (S_ISREG(mystat.st_mode)) { fprintf(stdout, "file: %s is general file.\n", argv[1]); } if (S_ISLNK(mystat.st_mode)) { fprintf(stdout, "file: %s is link file.\n", argv[1]); } if (S_ISSOCK(mystat.st_mode)) { fprintf(stdout, "file: %s is socket file.\n", argv[1]); } return (0); } Run the program under the SylixOS Shell: # ./type_test /dev/socket file: /dev/socket is socket file. # ./type_test /dev/null file: /dev/null is char file. Program List 5.1 uses the stat function. The detailed information of this function will be introduced in Subsection 5.2.2. It can be seen from the program results that the file type can be judged by the macros shown in Table 5.1. The program also displays how to use macros in Table 5.1. 5.1.2 File descriptor For the kernel, all open files are referenced through the file descriptor. The file descriptor is a non-negative integer. When an existing file is opened or a new file is created, the kernel returns a file descriptor to the process. When reading or writing a file, use the file descriptor returned by the open function or the creat function to identify the file. This file descriptor can be transferred to the read function or the write function as a parameter. SylixOS file descriptor is compatible with POSIX definition. It is an integer number (_POSIX_OPEN_MAX) starting from 0 up to a maximum value. Each open file has one or more (dup) file descriptors for matching. SylixOS, like the majority of operating systems, always uses a minimal and unused file descriptor as a newly allocated file descriptor when opening a file. According to the habit, 0 file descriptor represents standard input, 1 file descriptor represents standard output, and 2 file descriptor represents standard error. It shall be noted here that each process of SylixOS has its own file descriptor table, and each Manual OS. SpaceChain 81 Application Development Manual SpaceChain OS process does not conflict with each other. If a child process has a parent process, all file descriptors of the parent process will be inherited; if the child process is an orphan process, only 3 standard file descriptors of the system will be inherited. All threads within a process share the process file descriptor. There is a global file descriptor table in the kernel. This file descriptor table does not contain 0, 1 and 2 standard files. These three file descriptors are remapping flags in the kernel, that is to say, SylixOS allows each kernel task in the kernel has its own standard file. In POSIX.1-compliant applications, 0, 1, and 2 have been standardized, but they shall be replaced with symbolic constants STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO, so as to improve readability. In SylixOS, one can use these constants by including header file. 5.1.3 I/O System structure The I/O system structure of SylixOS is divided into ORIG drive structure and NEW_1 drive structure for historical reasons. The NEW_1 drive structure is added with file access permission, file record lock and other functions based on ORIG drive structure. Figure 5.1 shows the diagram of ORIG drive structure of SylixOS: Figure 5.1 ORIG drive structure Each file descriptor in SylixOS corresponds to a file structure. Different file descriptors may correspond to the same file structure. When all file descriptors corresponding to the same file structure are closed, the operating system will release the corresponding file structure, and call the corresponding driver. Different file structures can point to the same logical device. For example, a FAT file system device can be opened with many file structures. Different logical devices can also correspond to the same driver. For example, serial port 0 and serial port 1 with the same physical structure can correspond to a group of drivers for their services. The hardware device for specific service of each group of Manual OS. SpaceChain 82 Application Development Manual SpaceChain OS drivers is determined by the bottom BSP. Figure 5.2 shows the diagram of SylixOS NEW_1 driver structure: Figure 5.2 NEW_1 driver structure The NEW_1 driver structure is added with file nodes based on ORIG. Therefore, file access permission, file user information, file record locks (described in detail in Section 5.4.4) are introduced. Figure 5.3 NEW_1 kernel data structure Manual OS. SpaceChain 83 Application Development Manual SpaceChain OS It is found from Figure 5.2 that SylixOS supports sharing open files between different processes. It can be seen from Figure 5.3 NEW_1 kernel data structure that SylixOS kernel uses three data structures (file descriptor item, file structure and file node) to represent the open file, and their relationship determines possible influence of a process on another process in the aspect of file sharing. Each process maintains a file descriptor table of its own. Each file descriptor occupies one item, and those associated with each file descriptor are: Pointer to the file structure; File reference count; File descriptor flag (FD_CLOEXEC). The kernel maintains a file structure table for all open files, and entries of each file structure table includes (partial): Device head pointer (this pointer points to the device node); File name; File node pointer; File attribute flags (read, write, etc. See Table 5.2 for more information); File current pointer (indicating file offset). Each open file has a file node, and the file node includes (partial): Device descriptor; Inode (there is only one inode in the same file); File permissions information (readable, writable and executable); File user information; Current file size; File record lock pointer. ① Figure 5.3 shows the relationship among the three data structures corresponding to a process. The process opens two different files, one opened from file descriptor 3 and the other opened from file descriptor 4. If two independent processes open the same file respectively, the relation is shown in Figure 5.4. Manual OS. SpaceChain 84 Application Development Manual SpaceChain OS Figure 5.4 Two independent processes open the same file respectively We assume that the first process opens the file on file descriptor 3, while another process opens the same file on file descriptor 4. Each process which opens the file gets its own file structure, but there is only one file node for a given file. Each process gets its own file structure because it allows each process to have its own current read / write pointer to the file (file operation offset). The file descriptor flag and the file attribute flag are different in terms of action scope. The former only applies to one file descriptor in a process, while the latter applies to all file descriptors in any process which points to the given file structure. In Section 5.2.1, we will describe how to call the fcntl function to obtain and modify the file descriptor flag and the file attribute flag. 5.2 Standard I/O access Standard I/O is also called as synchronous I/O, i.e., initiating transmission and controlling I/O are both user-active actions. The file or device must operate with user's intervention. The majority of applications currently use this type of I/O operation. SylixOS supports the majority of synchronous I/O operations specified by POSIX. We will describe in detail operation of files and directories in SylixOS as follows. Manual OS. SpaceChain 85 Application Development Manual SpaceChain OS 5.2.1 File I/O 1. Function open #include int open(const char *cpcName, int iFlag, ...); Prototype analysis of Function open: For success of the function, return the file descriptor. For failure, return -1 and set the error number; Parameter cpcName is the name of the file to be opened ; Parameter iFlag is the open file flag; Parameter... is a variable parameter. ① Call the open function to open or create a file. The last parameter of the open function is written as ..., ISO C uses this method to represent that the number of remaining parameters and its type are variable. For open function, this parameter will only be used when the new file is created. Parameter iFlag contains multiple options, as shown in Table 5.2. This parameter is usually composed by adding "or" between multiple options. Table 5.2 iFlag options Option name Note O_RDONLY Open the file in a read-only manner O_WRONLY Open the file in a write-only manner O_RDWR Open the file in a readable or writable manner O_CREAT O_TRUNC O_APPEND O_EXCL O_NONBLOCK O_SYNC If the file does not exist, the file is created, and the third parameter of open function specifies the file permission mode If the file exists and it is opened successfully in write-only or read-write mode, its length is truncated to 0 Append the read-write pointer to the end of the file If O_CREAT is specified and the file exists, an error occurs. If the file does not exist, create it Open the file in a non-blocking manner Enable each write to wait for physical I/O operation to complete, including file attribute updates caused by the write operation. Make each write wait for physical I/O operation to complete. However, if the write O_DSYNC operation does not affect reading the data just written, you must not wait for the file attributes to be updated O_NOCTTY O_NOFOLLOW If cpcName quotes the terminal device, the device will not be assigned as control terminal for this process If cpcName quotes a symbolic link, an error occurs Manual OS. SpaceChain 86 Application Development Manual SpaceChain OS O_CLOEXEC Set FD_CLOEXEC flag as the file descriptor flag O_LARGEFILE Open big file flag The file descriptor returned by the open function must be the smallest and unused descriptor value in the system. This is used by some applications to open new files on standard input, standard output or standard error. For example, an application can first close standard output (file descriptor 1) and then open another file. Before executing the open operation, it can be understood that the file will be opened on file descriptor 1. When explaining dup2, you will learn that there is a better way to open a file on a given file descriptor. I/O system of SylixOS supports up to 2TB file. However, limited by certain file system design, the FAT file system, for example, can only support 4GB file size at maximum. In an application, in order to explicitly specify to open a large file, the O_LARGEFILE flag shall be specified when the open function is called. SylixOS also provides the following functions to open large files. #include int open64(const char *cpcName, int iFlag, ...); Function open64 prototype analysis: For success of the function, return the file descriptor. For failure, return -1 and set the error number; Parameter cpcName is the name of the file to be opened; Parameter iFlag is the open file flag; Parameter... is a variable parameter. 2. Function creat #include int creat(const char *cpcName, int iMode); Function creat prototype analysis: For success of the function, return the file descriptor. For failure, return -1 and set the error number; Parameter cpcName is the name of the file to be created; Parameter iMode is the mode to create file. A file can be created by calling creat function. The function is equivalent to the following function call: open(cpcName, O_WRONLY | O_CREAT | O_TRUNC, iMode); Manual OS. SpaceChain 87 Application Development Manual SpaceChain OS The file access permission mode (iMode) will be described in detail in Section 5.2.2. One drawback of creat function is that it opens the created file in a write-only manner. If a file is created with creat function and then it is required to read the file, one must firstly call creat function to create the file, then call close to close the file, and then open the file in a read manner. However, this way can be achieved directly by calling open function: open(cpcName, O_RDWR | O_CREAT | O_TRUNC, iMode); 3. Function close #include int close(int iFd); Prototype analysis of function close: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor. Calling of close function will reduce the reference count of file descriptors and the total reference count of files by one. When the reference count of file descriptors is zero, the file descriptor will be deleted (one will see the point at introduction to dup function). When the total reference count is reduced to zero, close the file, and release all record locks added by the current process on the file (Section 5.4.4 Introduction to Record Lock). When a process is terminated, the kernel will automatically close all the files it opens. Many programs take advantage of the function not to explicitly call close function to close the open file. 4. Function read #include ssize_t read(int void size_t iFd, *pvBuffer, stMaxBytes); Prototype analysis of Function read: For success of the function, return the number of bytes read. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Output parameter pvBuffer is the receive buffer zone; Parameter stMaxBytes is the size of the receive buffer zone. The data can be read from the open file by calling read function, and the number of bytes actually read is less than the number of bytes to be read in many cases: Manual OS. SpaceChain 88 Application Development Manual SpaceChain OS When the ordinary file is read, the file end has been reached before the requested number of bytes is read; For reading from a terminal device, a line is read at most at a time; For reading from the network, the buffer mechanism in the network may cause that the return value is less than the number of bytes to be read; When it is interrupted by the signal, and some data has been read. Usually, we need to judge the quantity and correctness of the read data through the read return value. 5. Function write #include ssize_t write(int const void size_t iFd, *pvBuffer, stNBytes); Prototype analysis of Function write: For success of the function, return the number of bytes written. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter pvBuffer is the address of the data buffer zone to be written to the file; Parameter stNBytes is the number of bytes written to the file. Call Function write to write data to the open file, the return value is usually the same with Parameter stNBytes value. Otherwise, it means an error. A common cause for write error is that the disk is full or exceeds the file length limit of a process. For ordinary files, the write operation is start at the current offset of the file. If the O_APPEND flag is specified when the file is opened, the file offset is set at the file end before each operation. After a successful write, the file offset increases the number of bytes actually written at the file end. 6. Function lseek Each open file has a current file offset associated with it, which is usually an integer, which measures the number of bytes calculated from the file beginning. In general, both read and write operations are started from the current file offset, and the offset increases the number of bytes read and written. At default of SylixOS, when a file is opened, the current file offset is always set as 0 unless the O_APPEND flag is specified. #include Manual OS. SpaceChain 89 Application Development Manual SpaceChain OS off_t lseek(int iFd, off_t oftOffset, int iWhence); Prototype analysis of Function lseek: For success of the function, return the new file offset. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter oftOffset is the offset; Parameter iWhence is the location datum. Calling the lseek function can explicitly set an offset for an open file. It shall be noted that the lseek call just adjusts the file offset records associated with the file descriptor in the kernel, but does not cause access to any physical device. The meaning of Parameter oftOffset varies depending on Parameter iWhence, as shown in Table 5.3: Table 5.3 iWhence value SEEK_SET SEEK_CUR SEEK_END iWhence value correlation Instructions for oftOffset Set the file offset with oftOffset bytes from the file beginning. Set the file offset as the current value plus oftOffset bytes, and oftOffset can be negative Set the file offset to file length plus oftOffset bytes, and oftOffset can be negative The meaning of iWhence parameter is shown in Figure 5.5. Figure 5.5 Meaning of iWhence parameter Here are some instances of Function lseek call, and the note describes where to move the current file pointer. lseek(fd, 0, SEEK_SET); /* at the beginning of the file */ lseek(fd, 0, SEEK_END); /* at the end of the file */ Manual OS. SpaceChain 90 Application Development Manual SpaceChain OS lseek(fd, -1, SEEK_END); /* The first byte of the file is counted down */ lseek(fd, -20, SEEK_CUR); of the file /* 20 bytes before the current location */ lseek(fd, 100, SEEK_END); /* Extend 100 bytes at the end of the file */ If the file offset of the program has already spanned the file end, perform I/O operation, read function call will return 0, indicating the file end. However, write function can write data at any position behind the file end. The space from the file end to the newly written data is called as file hole. From the perspective of programming, there is a byte in the file hole, and reading the hole will return a buffer zone filled with 0. Existence of hole means that the nominal size of a file may be larger than the total disk storage it occupies (sometimes much larger). Of course, the specific processing method is related to implementation of the file system. The following instance shows use of lseek function. This program creates a new file, generates a file hole by calling lseek function, and then writes some data at the file end, so that the program can read the file hole part, which are non-printable characters. Program print "\0" represents non-printable character. Program List 5.2 Use instance of Function lseek #include #include #include #include #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) int main (int argc, char *argv[]) { int fd, i; off_t ret; ssize_t size; char char *buf1 = "sylixos"; buf2[16]; fd = open("./file", O_RDWR | O_CREAT | O_TRUNC, FILE_MODE); if (fd < 0) { fprintf(stderr, "Create file failed.\n"); return -1; } Manual OS. SpaceChain 91 Application Development Manual SpaceChain OS write(fd, buf1, 7); ret = lseek(fd, 1000, SEEK_SET); if (ret < 0) { fprintf(stderr, "lseek failed.\n"); close(fd); return -1; } write(fd, buf1, 7); lseek(fd, -7, SEEK_END); size = read(fd, buf2, 7); if (size < 0) { fprintf(stderr, "read error.\n"); close(fd); return -1; } for (i = 0; i < size; i++) { if (!isprint(buf2[i])) { fprintf(stdout, "\\0"); } else { fprintf(stdout, "%c", buf2[i]); } } fprintf(stdout, "\n"); lseek(fd, -14, SEEK_END); size = read(fd, buf2, 7); if (size < 0) { fprintf(stderr, "read error.\n"); close(fd); return -1; } for (i = 0; i < size; i++) { if (!isprint(buf2[i])) { fprintf(stdout, "\\0"); } else { fprintf(stdout, "%c", buf2[i]); } Manual OS. SpaceChain 92 Application Development Manual SpaceChain OS } fprintf(stdout, "\n"); close(fd); return 0; } Run this program under the SylixOS Shell, and the program results show that the written data and contents of the file hole part are correctly read. # ./lseek_test sylixos \0\0\0\0\0\0\0 7. Functions pread and pwrite Section 5.1.3 introduces that multiple processes in SylixOS can read the same file. Each process has its own file structure, which also has its own current file offset. However, in non-NEW_1 file systems (without unique file node), unexpected results may be generated when multiple processes write the same file. To illustrate how to avoid this situation, one needs to understand concept of atomic operation. Considering the following code, a file is opened in the process to append data to it. …… ret = lseek(fd, 0, SEEK_END); if (ret < 0) { fprintf(stderr, "Lseek error.\n"); } ret = write(fd, buf, 10); if (ret != 10) { fprintf(stderr, "Write data error.\n") } …… This code has no problem in the case of single process, and the fact also proves this. However, if there are multiple processes, problems will be caused by using this method to append data to the file. If there are two independent processes 1 and 2 to simultaneously append the write operation to a file, each process does not use the O_APPEND flag when opening the file. At this time, the relation of each data structure is shown in Figure 5.4. Each process is has its own file structure and file current offset, but a file node is shared. If process 1 calls lseek function to set the current file offset to the file end, then process 2 runs, calls lseek function, and also sets the current file offset to the file end. Then process 2 calls write function to push the file offset of process 2 backward by 10 bytes. At this time, the file becomes longer, and the kernel also increases the file length in the file node by 10 bytes. Manual OS. SpaceChain 93 Application Development Manual SpaceChain OS After that, the kernel switches process 1 for running, and calls the write function. At this point, process 1 starts to write from its own current offset, thus overwriting the data that process 2 just wrote. As can be seen from the above process, the problem lies in the "position to the file end, and then write the file", this process is completed with two functions. Therefore, it causes a non-atomic operation, because the process may be switched between two functions. Therefore, we can conclude that if the process is completed in a function (forming an atomic operation), the problem can be solved. SylixOS provides an atomic operation method for this operation. When the file is opened, the O_APPEND flag is set, so that the kernel will set the current offset to the file end for each write operation, and it is not required to call lseek function before each write. SylixOS provides an atomic function to locate and perform I/O operations: pread, ① pwrite . #include ssize_t pread(int void iFd, *pvBuffer, size_t stMaxBytes, off_t oftPos); ssize_t pwrite(int iFd, const void *pvBuffer, size_t stNBytes, off_t oftPos); Prototype analysis of Function pread: For success of the function, return the number of bytes read. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Output parameter pvBuffer is the receive buffer zone; Parameter stMaxBytes is the size of the buffer zone; Parameter oftPos appoints the read position. Prototype analysis of Function pwrite: For success of the function, return the number of bytes written. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter pvBuffer is the data buffer zone; Parameter stNBytes is the number of bytes written; Manual OS. SpaceChain 94 Application Development Manual SpaceChain OS appoints the writing position. Parameter oftPos Calling pread function is equivalent to calling lseek function before calling read function, but pred function has the following important differences from this sequence: When calling the pread function, its positioning and read operations cannot be interrupted (atomic operation process); Do not update the current file offset. Calling pwrite function is equivalent to calling write function after calling lseek function first, but there are also similar differences from the above. In general, atomic operation refers to one operation consisting of multiple steps. If the operation is performed atomically, either all steps are performed or no step is performed, it is not possible to perform only a subset of all steps. To be able to read and write larger files (usually larger than 4GB), SylixOS provides the following group of functions. #include ssize_t pread64(INT iFd, PVOID pvBuffer, size_t stMaxBytes, off64_t oftPos); ssize_t pwrite64(INT iFd, CPVOID pvBuffer, size_t stNBytes, off_t64 oftPos); Prototype analysis of Function pread64: For success of the function, return the number of bytes read. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Output parameter pvBuffer is the receive buffer zone; Parameter stMaxBytes is the size of the buffer zone; Parameter oftPos appoints the read position. Prototype analysis of Function pwrite6: For success of the function, return the number of bytes written. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Manual OS. SpaceChain 95 Application Development Manual SpaceChain OS data buffer zone; Parameter pvBuffer is the Parameter stNBytes is the number of bytes written; Parameter oftPos appoints the writing position. The following example shows how to use pwrite function and pread function. This program creates a new file, writes data to the specified offset of the file by calling pwrite function, and then calls read function to verify the current offset of the file. Calling pread function also verifies that the file hole is generated. Program List 5.3 Use of pwrite and pread functions #include #include #include #include #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) int main (int argc, char *argv[]) { int fd, i; ssize_t size; char *buf1 = "sylixos"; char buf2[16]; fd = open("./file", O_RDWR | O_CREAT | O_TRUNC, FILE_MODE); if (fd < 0) { fprintf(stderr, "Create file failed.\n"); return (-1); } size = pwrite(fd, buf1, 7, 100); if (size != 7) { fprintf(stderr, "pwrite error.\n"); close(fd); return -1; } size = read(fd, buf2, 7); if (size < 0) { fprintf(stderr, "read error.\n"); close(fd); Manual OS. SpaceChain 96 Application Development Manual SpaceChain OS return (-1); } for (i = 0; i < size; i++) { if (!isprint(buf2[i])) { fprintf(stdout, "\\0"); } else { fprintf(stdout, "%c", buf2[i]); } } fprintf(stdout, "\n"); size = pread(fd, buf2, 7, 100); if (size < 0) { fprintf(stderr, "pread error.\n"); close(fd); return (-1); } for (i = 0; i < size; i++) { if (!isprint(buf2[i])) { fprintf(stdout, "\\0"); } else { fprintf(stdout, "%c", buf2[i]); } } fprintf(stdout, "\n"); close(fd); return (0); } Run the program under the SylixOS Shell: # ./pwrite_test \0\0\0\0\0\0\0 sylixos The print results show that the file hole is generated after pwrite function was called, and the current file offset has not changed. This also confirms that the aforementioned pwrite function is equivalent to calling lseek function before function write, but the difference is that pwrite is an atomic operation. 8. Functions dup and dup2 Manual OS. SpaceChain 97 Application Development Manual SpaceChain OS #include int dup(int iFd); int dup2(int iFd1, int iFd2); Prototype analysis of Function dup: For success of the function, return the file descriptor. For failure, return -1 and set the error number; Parameter iFd is the original file descriptor. Prototype analysis of Function dup2: For success of the function, return iFd2 file descriptor. For failure, return -1 and set the error number; Parameter iFd1 is the file descriptor 1; Parameter iFd2 is the file descriptor 2; Calling dup function and dup2 function can copy an existing file descriptor. The new file descriptor returned by the dup function must be the smallest value in the currently available file descriptor. For dup2 function, one can use Parameter iFd2 to specify the value of the new file descriptor. If iFd2 has been already opened, it shall be closed firstly, and FD_CLOEXEC file descriptor flag of iFd2 will be cleared, so that iFd2 is open when the process calls exec function (see Chapter 8 Process Management). Note that the SylixOS kernel does not currently support the situation that iFd1 is equal to iFd2. The file descriptor returned by dup function and Parameter iFd share the same file structure item (file table entry). Similarly, the file descriptors iFd1 and iFd2 of dup2 function also share the same file structure item, as shown in Figure 5.6. Figure 5.6 Kernel data structure behind dup(3) In Figure 5.6, the process calls: fd = dup(3); Assume that file descriptor 3 has already been occupied (which is very likely). At the Manual OS. SpaceChain 98 Application Development Manual SpaceChain OS moment, we call dup function to use file descriptor 4, because both file descriptors point to the same file structure (file table entry). Therefore, they share the same file attribute flag (read, write, append, etc.) and the same file current pointer (file offset). Each file has its own set of file descriptor flags, and the new file descriptor flag (FD_CLOEXEC) is always cleared by dup function. Another way to copy the descriptor is to use fcntl function. The following subsection introduces the function, in fact, calling dup(fd); Equivalent to fcntl(fd, F_DUPFD, 0); While, calling dup2(fd, fd2); Equivalent to fcntl(fd, F_DUP2FD①, fd2); Or close(fd2); fcntl(fd, F_DUPFD, fd2); As mentioned above, each process of SylixOS has its own file descriptor table, and there is also a global file descriptor table in the kernel. Then if a file is opened in the process, the kernel cannot see the file descriptor, but there are some file descriptors to be opened by the kernel operating process (for example: write data to the file specified by the application in the log system). SylixOS provides the following functions to realize copying of the process file descriptor to the kernel space. #include int dup2kernel(int fd); Prototype analysis of Function dup2kernel: For success of the function, return the kernel file descriptor. For failure, return -1 and set the error number; Parameter fd is the process file descriptor. The following program shows how to use the dup function. The program creates a new file, calls dup function to copy a new file descriptor, and then manipulates the created file on the new file descriptor. ① POSIX.1-2008 addition. Manual OS. SpaceChain 99 Application Development Manual SpaceChain OS Program List 5.4 Use of dup function #include #include int main (int argc, char *argv[]) { int fd, newfd; char buf[64] = {0}; fd = open("./file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stderr, "open file: %s failed.\n", "./file"); return -1; } newfd = dup(fd); if (newfd < 0) { fprintf(stderr, "dup error.\n"); close(fd); return -1; } write(newfd, "sylixos", 7); lseek(newfd, 0, SEEK_SET); read(fd, buf, 7); fprintf(stdout, "buf: %s\n", buf); return 0; } Running this program in SylixOS Shell, and the operation results show that lseek operation newfd is equivalent to operation fd. # ./dup_test buf: sylixos 9. Functions sync, fsync and fdatasync SylixOS system has a disk cache in the kernel and most disk I/O is conducted through the buffer zone. When we write data to the file, the kernel usually copies the data into the buffer zone buffer, then queues it, and then writes it to the disk (done by the thread “t_diskcache”) when appropriate. This is called as delayed write. In general, when the kernel needs to re-use the buffer zone to store other disk block data, it will write all delayed write data blocks to the disk. In order to guarantee Manual OS. SpaceChain 100 Application Development Manual SpaceChain OS consistency between the actual file system on the disk and contents in the buffer zone, SylixOS provides three functions of sync, fsync and fdatasync. #include void sync(void); int fsync(int iFd); int fdatasync(int iFd); Prototype analysis of Function fsync: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor. Prototype analysis of Function fdatasync: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor. The sync function arranges all modified disk caches in the system to the write queue, and then waits to complete the actual write disk operation before return. The fsync function only works for a file specified by the file descriptor iFd. The fdatasync function is similar to fsync function, but it only affects the data portion of the file. In addition to the data, fsync also synchronously updates file attributes. 10. Function fcntl #include int fcntl(int iFd, int iCmd, ...); Prototype analysis of Function fcntl: For success of the function, return different values according to different Parameter iCmd. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter iCmd is the command; Parameter ... is the command parameter. Calling fcntl function can change attributes of the opened file. In the instance of this section, the 3rd parameter is always an integer. However, when the record lock is described, the 3rd parameter is a pointer to a structure. SylixOS fcntl function supports the following 4 functions: Copy an existing file descriptor (iCmd = F_DUPFD, F_DUPFD_CLOEXEC, Manual OS. SpaceChain 101 Application Development Manual SpaceChain OS F_DUP2FD, F_DUP2FD_CLOEXEC); Get / set the file descriptor flag (iCmd = F_GETFD, F_SETFD); Get / set the file attribute flag (iCmd = F_GETFL, F_SETFL); Get / set the file record lock (iCmd = F_GETLK, F_SETLK, F_SETLKW). Table 5.4 describes the functions of the first 3 commands, and the record lock function will be described in detail in Subsection 5.4.4 File Record Lock. Table 5.4 Function description of fcntl Command Note F_DUPFD Copy the file descriptor, equivalent to dup and dup2 functions F_DUPFD_CLOEXEC Copy the file descriptor, and set the file descriptor flag F_DUP2FD Copy the file descriptor, equivalent to dup2 function F_DUP2FD_CLOEXEC Copy the file descriptor, and set the file descriptor flag F_GETFD Get the file descriptor flag (FD_CLOEXEC), and return it as the return value F_SETFD Set the file descriptor flag (FD_CLOEXEC) F_GETFL Get the file attribute flag, and return it as the return value F_SETFL Set the file attribute flag The following program shows how to get the file attribute flag by calling fcntl function. The user enters different file open attribute flags to verify that the attribute flags got by fcntl function are also different. Program List 5.5 fcntl modify file attribute flag #include #include #include int main (int argc, char *argv[]) { int fd; int flags, inflags = 0; if (argc < 3) { fprintf(stderr, "Don't find parse files.\n"); return (-1); } if (!strcmp(argv[2], "O_RDONLY")) { Manual OS. SpaceChain 102 Application Development Manual SpaceChain OS inflags = O_RDONLY; } if (!strcmp(argv[2], "O_WRONLY")) { inflags = O_WRONLY; } if (!strcmp(argv[2], "O_RDWR")) { inflags = O_RDWR; } fd = open(argv[1], inflags); if (fd < 0) { fprintf(stderr, "open file: %s failed.\n", argv[1]); return (-1); } flags = fcntl(fd, F_GETFL, 0); switch (flags & O_ACCMODE) { case O_RDONLY: fprintf(stdout, "file: %s read only!\n", argv[1]); break; case O_WRONLY: fprintf(stdout, "file: %s write only!\n", argv[1]); break; case O_RDWR: fprintf(stdout, "file: %s read write.\n", argv[1]); break; default: fprintf(stdout, "file: %s flags: %x\n", argv[1], flags); break; } close(fd); return (0); } Run the program under SylixOS Shell. Judging from the program running results, if the open file attribute flags are different, and the file attribute flags obtained by fcntl Manual OS. SpaceChain 103 Application Development Manual SpaceChain OS function will also be changed. # touch test.file # ./fcntl_test test.file O_RDONLY file: test.file read only! # ./fcntl_test test.file O_RDWR file: test.file read write. 11. Function ioctl #include int ioctl(int iFd, int iFunction,...); Play the role of Function ioctl For success of the function, return 0. For failure, return -1; Parameter iFd is the file descriptor; Parameter iFunction is the function; Parameter... is the function parameter. For I/O operation, ioctl function can be seen as a “treasure chest”. Those which cannot be done with I/O function can be done with ioctl function. A lot of ioctl operations are used in the terminal I/O. Each device driver can define its own set of ioctl commands. The system provides the generic ioctl command for different types of devices. 5.2.2 Files and directories In the previous section, we discussed the basic operations of the file: open file, read file, write file and so on. In this section, we introduce other features of the file and how to modify these features. Finally, we will introduce the symbolic links in SylixOS. 1. Functions stat, lstat and fstat #include int stat(const char *pcName, struct stat *pstat); int lstat(const char int fstat(int *pcName, struct stat *pstat); iFd, struct stat *pstat); Prototype analysis of Function stat: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the file name; Output parameter pstat returns the file status information. Manual OS. SpaceChain 104 Application Development Manual SpaceChain OS Prototype analysis of Function lstat: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the file name; Output parameter pstat returns the file status information. Prototype analysis of Function fstat: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Output parameter pstat returns the file status information. Calling stat function will return the status information of the pcName file through Parameter pstat, calling the fstat function will get information about the file which has been opened on the descriptor iFd. lstat function is similar to stat function. However, when the incoming file name is the symbolic link name, lstat function will get information about the symbolic link, but not information of the actual file which the symbolic link refers to (the subsections below will detail the symbolic link). Parameter pstat is a status buffer zone to be provided by the user. The pointer points to the stat structure type buffer zone, and the structure is as shown below: struct stat { dev_t st_dev; /* device */ ino_t st_ino; /* inode */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device type (if inode device) */ off_t st_size; /* total size, in bytes */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last create */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of blocks allocated */ …… }; In the stat structure, there are basically the basic data types of the system. The ll Manual OS. SpaceChain 105 Application Development Manual SpaceChain OS command is mostly used in stat function of SylixOS, and this command can get the following file information: # ll -rw-r--r-- root root Tue Jul 07 10:22:28 2015 -rwxr-xr-x root root Tue Jul 07 10:18:51 2015 0 B, test.file 233KB, app Next, we will focus on the file mode (st_mode information). We introduced the file types in Section 5.1.1. Table 5.5 shows the corresponding bit information of these types in st_mode. Table 5.5 File type bit st_mode bit Note S_IFIFO FIFO file S_IFCHR Character file S_IFDIR Directory file S_IFBLK Block device file S_IFREG Ordinary file S_IFLNK Symbolic link file S_IFSOCK Socket file All of these file types have access permission, and each file has 9 access permission bits, just like the first column output by ll command. These access permission bits can be divided into 3 categories, as shown in Table 5.6. Table 5.6 Access permission bit st_mode bit Note S_IRUSR User read S_IWUSR User write S_IXUSR User execution S_IRGRP Group read S_IWGRP Group write S_IXGRP Group execution Manual OS. SpaceChain 106 Application Development Manual SpaceChain OS S_IROTH Other read S_IWOTH Other write S_IXOTH Other implementation In each group shown in Table 5.6, the term "user" refers to the file owner, "group" refers to the group of the owner, and "other" refers to other users not belonging to this group. The 9 permission bits can be modified with the chmod command. It shall be noted that during permission modification with the chmod command in Linux and other systems, user can be represented by u, group can be represented by g, and others can be represented by o. SylixOS is directly represented with the number. For example: 755 represents -rwxr -xr-x. When we open a file of any type with a name, we must have execution permission for each directory contained in that name, including the current working directory it may imply. For example, in order to open the file /apps/app/test.c, it is required to have execution permission bits for directory /apps, /apps/app. The read permission bit for a file determines whether we can open an existing file for read operation. This is related to the O_RDONLY and O_RDWR flags of open function. Of course, the write situation is similar. 2. Function access #include int access(const char *pcName, int iMode); Prototype analysis of Function access For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the file name; Parameter iMode is the access mode. access function performs test according to the file access permission of the file owner. The test mode of access function is shown in Table 5.7. Table 5.7 iMode bit of access function iMode bit R_OK Note File readable Manual OS. SpaceChain 107 Application Development Manual SpaceChain OS W_OK File writable X_OK File executable F_OK File exist The following program shows how to use access function. The program reads the file provided by the user from the Shell interface, and then determines whether the file is writable and can be successfully opened. Manual OS. SpaceChain 108 Application Development Manual SpaceChain OS Program List 5.6 Use method of access function #include #include int main (int argc, char *argv[]) { int fd; if (argc != 2) { fprintf(stderr, "%s [filename].\n", argv[0]); return -1; } if (access(argv[1], W_OK) < 0) { fprintf(stdout, "%s can't write.\n", argv[1]); } else { fprintf(stdout, "%s can write.\n", argv[1]); } if ((fd = open(argv[1], O_WRONLY)) < 0) { fprintf(stdout, "open file failed.\n"); } else { fprintf(stdout, "open file success.\n"); close(fd); } return 0; } Run the program under the SylixOS Shell: # ll -rw-r--r-- root root Tue Jul 07 13:49:01 2015 0 B, b.c -r-------- root root Tue Jul 07 13:48:33 2015 0 B, a.c # ./access_test b.c b.c can write. open file success. # ./access_test a.c a.c can't write. open file failed. It can be seen from the above instance that open function cannot open this file normally by setting the file access permission. Manual OS. SpaceChain 109 Application Development Manual SpaceChain OS 3. Function umask #include mode_t umask(mode_t modeMask); Prototype analysis of Function umask: This function returns the previous mask word; Parameter modeMask is the new mask word. The umask function creates a mask word for the current process setup file, and returns the previous value. It is a function without error value returned. Among them, Parameter modeMask consists of a number of bitwise "or" from the constants listed in Table 5.6. When the process creates a new file or a new directory, it will surely use the file mode to create mask words (during introduction of open function and creat function, both functions have a mode parameter, which specifies the access permissions of the new file). For the bit as 1 the file mask word, the corresponding access permission bits in the file must be closed. It shall be noted that the owner's read permission will not be masked when the Sylix OS kernel creates a new file (this guarantees that the file owner can read files normally). The following program shows how to use umask function. The program creates two files. When the first one is created, the umask value is 0. That is to say, no permission bit will be masked. The file will be created according to the default permission mode of the kernel. When the second is created, the umask value disables group and other read and write permissions. Program List 5.7 Use of umask function #include #include #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) int main (int argc, char *argv[]) { umask(0); if (creat("./a.c", FILE_MODE) < 0) { fprintf(stderr, "create file failed.\n"); return -1; } umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); Manual OS. SpaceChain 110 Application Development Manual SpaceChain OS if (creat("./b.c", FILE_MODE) < 0) { fprintf(stderr, "create file failed.\n"); return -1; } return 0; } Running this program under the SylixOS Shell, and it can be found from the running results that the file permission bit is affected by the process mask word. # ./umask_test # ll -rw------- root root Tue Jul 07 15:10:25 2015 0 B, b.c -rw-rw-rw- root root Tue Jul 07 15:10:25 2015 0 B, a.c 4. Functions fchmod and chmod #include int fchmod(int iFd, int iMode); int chmod(const char *pcName, int iMode); Prototype analysis of Function fchmod: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter iMode is the mode to be set. Prototype analysis of Function chmod: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the file name; arameter iMode is the mode to be set. Calling chmod function and fchmod function can change the access permissions of existing files. chmod function operates on the specified file, and fchmod function operates on the opened file. The following program shows use of chmod function. The program implements two operations. One operation is to remove a certain permission based on the original access permission of the file, and the other operation is to set certain access permission bits. Manual OS. SpaceChain 111 Application Development Manual SpaceChain OS Program List 5.8 Use of Function chmod #include #include int main (int argc, char *argv[]) { struct stat newstat; if (stat("./a.c", &newstat) < 0) { fprintf(stderr, "stat error.\n"); return -1; } if (chmod("./a.c", (newstat.st_mode & ~S_IRGRP)) < 0) { fprintf(stderr, "drop mode error.\n"); return -1; } if (chmod("./b.c", S_IRUSR | S_IWGRP) < 0) { fprintf(stderr, "set mode error.\n"); } return 0; } Run the program under SylixOS Shell, and it can be seen from program running results that chmod function correctly sets corresponding access permission bits. # ll -rw-rw-rw- root root Tue Jul 07 15:10:25 2015 0 B, b.c -rw-rw-rw- root root Tue Jul 07 15:10:25 2015 0 B, a.c -r---w---- root root Tue Jul 07 15:10:25 2015 0 B, b.c -rw--w-rw- root root Tue Jul 07 15:10:25 2015 0 B, a.c # ./chmod_test # ll 5. Function unlink and remove #include int unlink(const char *pcName); Prototype analysis of Function unlink: For success of the function, return 0. For failure, return -1 and set the error number; Manual OS. SpaceChain 112 Application Development Manual SpaceChain OS name of the file to be deleted. Parameter pcName is the One can delete a file via calling the unlink function. File deletion shall satisfy a certain condition. When the reference count of the file reaches 0, the file can be deleted. When the process opens the file, it cannot be deleted. When a file is deleted, the kernel first checks the number of processes opening the file. If the number reaches 0, the kernel rechecks the reference count. If it is also 0, then the file is deleted. If Parameter pcName is the name of a symbolic link, the symbolic link will be deleted, instead of deleting the file referenced by the symbolic link. You can also call the remove function in ANSI C to delete a file. #include int remove(const char *file); Prototype analysis of Function remove: For success of the function, return 0. For failure, return -1 and set the error number; Parameter file is the name of the file to be deleted. 6. Function rename #include int rename(const char *pcOldName, const char *pcNewName); Prototype analysis of Function rename: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcOldName is the old file name; Parameter pcNewName is the file name modified. The file or directory can be renamed with the rename function. The following several situations shall be indicated according to different Parameter pcOldName: If pcOldName points to a non-directory file, there are two following situations: If pcNewName already exists, pcNewName cannot be a directory file; If pcNewName already exists and is not a directory file, delete it firstly, and rename pcOldName. If pcOldName points to a directory file, there are two following situations: If pcNewName already exists, pcNewName must be an empty directory; If pcNewName already exists, delete it firstly, and rename pcOldName. Manual OS. SpaceChain 113 Application Development Manual SpaceChain OS If pcOldName and pcNewName are the same file, the function returns silently without any modification. Note: In SylixOS, if pcOldName is a symbolic link name, calling the rename function will modify the name of the real file to which the symbolic link points, and special attention shall be paid for the point. 7. Function opendir and closedir #include DIR *opendir(const char *pathname); int closedir(DIR *dir); Prototype analysis of Function opendir: For success of the function, return the directory pointer. For failure, return NULL and set the error number; Parameter pathname is the directory name. Prototype analysis of Function closedir: For success of the function, return 0. For failure, return -1 and set the error number; Parameter dir is the directory pointer. Calling the opendir function will open the directory to which the pathname points and returns the directory pointer of DIR type, which points to the starting position of the directory. It might also be noted that the opendir function will open the directory in a read-only manner. It means that the open directory must exist. Otherwise, it will return NULL and set the errno as ENOENT. If the pathname is not a valid directory file, NULL is returned and the errno is set as ENOTDIR. Calling the closedir function will close the directory to which dir points (dir is returned via the opendir function). 8. Function readdir and readdir_r #include struct dirent *readdir(DIR *dir); int readdir_r(DIR *pdir, struct dirent *pdirentEntry, struct dirent **ppdirentResult); Prototype analysis of Function readdir: For success of the function, return the directory information pointer. For failure, Parameter dir is an open directory pointer. Prototype analysis of Function readdir_r: Manual OS. SpaceChain 114 Application Development Manual SpaceChain OS For success function, return 0. For failure, return -1 and set the error number; of the Parameter pdir is an open directory pointer. Output parameter pdirentEntry returns directory information; Output parameter ppdirentResult points to the pdirentEntry address or NULL. Calling the readdir function will return the directory information of the specified directory, and the readdir function is not reentrant. The readdir_r function is reentrant implementation of the readdir function, and pdirentEntry points to the user buffer for storing directory information. If the end of the directory is read, *ppdirentResult is equal to NULL. The read directory information is stored in the dirent structure, as shown below: struct dirent { char d_name[NAME_MAX + 1]; /* file name unsigned char d_type; /* file type(maybe DT_UNKNOWN) */ char d_shortname[13]; /* fat short file name (maybe doesn’t exist) */ */ …… }; The d_name member saves the name of the file in the directory. and the d_type indicates the type of the file as shown in Table 5.1. The following macros can realize mutual conversion of the file type and the file type mode bit (shown in Table 5.5). #include unsigned char IFTODT①(mode_t mode); mode_t DTTOIF(unsigned char dtype); IFTODT macro converts the type mode to the file type, and DTTOIF macro converts the file type to the type mode. The following program shows how to use operation directory function. The program opens a appointed directory (such as "/") and reads the directory information, and then displays the name and type of the directory file. Program List 5.9 Display directory information #include #include #include ① Here is a kind of representation form of macro function, which hides details realized in the macro. Manual SpaceChain OS. 115 Application Development Manual SpaceChain OS #define DIR_PATH "/" char *file_type (char type, char *name, int len) { if (!name) { return (NULL); } if (S_ISDIR(DTTOIF(type))) { strlcpy(name, "directory", len); return (name); } if (S_ISREG(DTTOIF(type))) { strlcpy(name, "regular", len); return (name); } if (S_ISSOCK(DTTOIF(type))) { strlcpy(name, "socket", len); return (name); } if (S_ISLNK(DTTOIF(type))) { strlcpy(name, "link", len); return (name); } return (NULL); } int main (int argc, char *argv[]) { DIR *dir; struct dirent dirinfo; struct dirent *tempdir; int ret; char name[64]; dir = opendir(DIR_PATH); if (dir == NULL) { return (-1); } Manual OS. SpaceChain 116 Application Development Manual SpaceChain OS while (((ret = readdir_r(dir, &dirinfo, &tempdir)) == 0) && tempdir) { fprintf(stdout, "file: %s type is: %s file\n", dirinfo.d_name, file_type(dirinfo.d_type, name, sizeof(name))); } closedir(dir); return (0); } Run the program under the SylixOS Shell: # ./readdir_test file: tmp type is: directory file file: var type is: link file file: root type is: link file file: home type is: link file …… 9. Function mkdir and rmdir #include int mkdir(const char *dirname, mode_t mode); int rmdir(const char *pathname); Prototype analysis of Function mkdir: For success of the function, return 0. For failure, return -1 and set the error number; Parameter dirname is the name of the created directory; Parameter mode is the creation mode. Prototype analysis of Function rmdir: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pathname is the directory name. Calling the mkdir function can create an empty directory, the access authority of the directory appointed directory is appointed by the mode, and the mode will modify the mask work according to the file mode of the progress. Calling Function rmdir can delete an empty directory, and the underlying can be realized by calling the unlink function. 10. Function chdir, fchdir and getcwd Manual OS. SpaceChain 117 Application Development Manual SpaceChain OS Each process has a current working directory, which is the starting point for searching all relative path names (the path not starting with the oblique line is called the relative path). When the user logins SylixOS, the current working directory is usually the sixth field of the user's login entry in the password file "/etc/passwd" - the user's home directory. #include int chdir(const char *pcName); int fchdir(int iFd); char *getcwd(char *pcBuffer, size_t stByteSize); Prototype analysis of Function chdir: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the new default directory. Prototype analysis of Function fchdir: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor. Prototype analysis of Function getcwd: For success of the function, return the first address in the buffer zone of the default directory. For failure, return NULL; Output parameter pcBuffer is the default directory buffer; Parameter stMaxBytes is the size of the buffer zone. The process calls the chdir function or the fchdir function to change the current working directory, the chdir function appoints the current working directory with parameter pcName, and the fchdir function appoints the current working directory via the file descriptor iFd. The current working directory is a property of the process. Therefore, it is worth noting that modification of the working directory of the process will not influence the working directory of other processes. Calling the getcwd function can get the current default working path, and the function must have a large enough buffer zone to store the name of the absolute path returned plus a terminating null byte. Otherwise, it returns error. The getcwd function is very useful when an application is required to return to the starting point of its work in the file system. Before change of the working directory, one call the getcwd function to save the current working directory firstly. After processing, one can Manual OS. SpaceChain 118 Application Development Manual SpaceChain OS take the working directory previously saved as the parameter and transfer it to the chdir function, which returns to the starting point of the file system. The fchdir function provides a more convenient method. Before change in different locations of the file system, call the open function to open the current working directory firstly, and save the file descriptor returned. When it is hoped to return the original working directory, it is required to take the saved file descriptor as the parameter and transfer it to the fchdir function. 11. Symbolic link The symbolic link is the indirect pointer to a file, and any user can create the symbolic link to the directory. The symbolic link is generally used to direct a file or the whole directory str When the file is opened with the open function, if the parameter of the file name transferred to the open function is a symbolic link, open will open the appointed file with the symbolic link. However, if the file does not exist, the open function will return error indicating that the file does not exist, and the point shall be noticed. #include int symlink(const char * pcActualPath, const char *pcSymPath); ssize_t readlink(const char *pcSymPath, char *pcBuffer, size_t iSize); Prototype analysis of Function symlink: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcActualPath is the target file of the actual link; Parameter pcSymPath is the symbolic link file newly created. Prototype analysis of Function readlink: For success of the function, return the length of symbolic link contents read. For failure, return -1 and set the error number; Parameter pcSymPath is the symbolic link name to be read; Output parameter pcBuffer is the content buffer zone; Parameter iSize is the length of the buffer zone. SylixOS can call symlink function to create the symbolic links, symlink function will create a symbolic link pcSymPath pointing to pcActualPath, and pcActualPath and pcSymPath cannot be in the same file system The open function mentioned above can only open the file to which the symbolic link points. Therefore, there needs to be a way to open the symbolic link itself, and read its contents. The readlink provides such function. Note: SylixOS does not support the hard link at present. Manual OS. SpaceChain 119 Application Development Manual SpaceChain OS 12. File truncation Sometimes we need to intercept some data at the end of the file to shorten the file, and it is a special case to truncate the length of a file to 0. One can do this with the O_TRUNC flag when you open a file. #include int ftruncate(int iFd, off_t oftLength); int truncate(const char *pcName, off_t oftLength); For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter oftLength is the file length. Prototype analysis of Function truncate: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pcName is the file name; Parameter oftLength is the file length. Calling the truncate function and ftruncate function can reduce or extend the file length. If the previous file length is longer than the oftLength, the additional data will be lost. If the previous file length is smaller than the appointed length, the file length will be expanded, i.e., the file hole is generated. The ftruncate function operates the file has been opened by the user, and the file descriptor is transferred. 5.2.3 Standard I/O library 1. Standard input, standard output and standard error All I/O functions introduced above surround the file descriptor. When a file is opened, a file descriptor is returned, and the file descriptor is used for subsequent I/O operation. For standard I/O libraries, their operation is performed around the stream. When a file is opened with the standard I/O library, the file is correspondingly associated with the file, and then a file pointer of the FILE type is returned. Three streams are predefined for a process, and the three streams are automatically used by the process, including standard input, standard output and standard error. The files quoted by these streams are the same with those by the file descriptors of STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO. The predefined file pointers of three stream are: stdin, stdout and stderr, which are used by our print function previously. Manual OS. SpaceChain 120 Application Development Manual SpaceChain OS 2. Buffer area The standard I/O library provides buffering to call the read function and the write function as little as possible, and automatically perform buffer management for each I/O stream, so as to avoid the trouble from consideration of the point by the application. The standard I/O library provides 3 types of buffer zones. Full buffer. In this case, actual I/O operations can be performed after filling the standard I/O buffer zone. Full buffer is usually performed for the file stayed on the disk via the standard I/O library. Line buffer. Actual I/O operations, i.e., standard input and standard output, are performed when the line break is encountered at input and output, and line buffer is used usually. No buffer, the standard I/O library does not perform buffer storage for the character. Standard errors are usually unbuffered, so that the error information can be indicated as far as possible. Calling the following functions can modify types of the buffer zone. #include void setbuf(FILE *fp, char *buf); int setvbuf(FILE *fp, char *buf, int mode, size_t size); Prototype analysis of Function setbuf: Parameter fp is the file pointer. Parameter buf is the buffer zone. Prototype analysis of Function setvbuf: For success of the function, return 0. For failure, return non-0 value and set the error number; Parameter fp is the file pointer; Parameter buf is the buffer zone; Parameter mode is the buffer type, as shown in Table 5.8; Table 5.8 Standard I/O buffer type Type of buffer zone Note _IOFBF Full buffer _IOLBF Line buffer _IONBF No buffer Manual OS. SpaceChain 121 Application Development Manual SpaceChain OS buffer size. Parameter size is the The above function requires that the appointed stream has been opened, and called before any operation. One can use the setbuf function to open or close the buffer zone. In order to set a buffer zone, the parameter buf points to the first address of the buffer zone, and BUFSIZ defines the size of the buffer zone (defined in ). If one wants to close the buffer zone, it is only required to point the buf parameter to NULL. Calling the setvbuf function can appoint the buffer type, as shown in Table 5.8. Generally, standard I/O will automatically release the buffer zone when the stream is closed. Of course, calling the fflush function can flush a stream at any time. #include int fflush(FILE *fp); Prototype analysis of Function fflush: ① For success of the function, return 0. For failure, return EOF ; Parameter fp is the file pointer. The fflush function transmits all unwritten data on the appointed stream to the kernel, and SylixOS does not currently support the case where fp is NULL. 3. Open the stream #include FILE *fopen(const char *file, const char *mode); FILE *freopen(const char *file, const char *mode, FILE *fp); FILE *fdopen(int fd, const char *mode); Prototype analysis of Function fopen: For success of the function, return the file pointer. For failure, return NULL and set the error number; Parameter file is the name of file to be opened; Parameter mode is the open mode, as shown in Table 5.9. Prototype analysis of Function freopen: For success of the function, return the file pointer. For failure, return NULL and set the error number; Parameter file is the name of file to be opened; Parameter mode is the open mode, as shown in Table 5.9; Parameter fp is the file pointer. Manual OS. SpaceChain 122 Application Development Manual SpaceChain OS Prototype analysis of Function fdopen: For success of the function, return the file pointer. For failure, return NULL and set the error number; Parameter fd is the open file descriptor; Parameter mode is the open mode. Above 3 functions can open a standard I/O stream, and the fopen function opens the file appointed by file. The freopen function opens an appointed file on an appointed stream. If the stream has been opened, it is closed first. If the stream has been orientated, the orientation will be cleared. The fdopen function takes an existing file descriptor, and combines a standard I/O stream with the descriptor. Table 5.9 mode parameter of opening standard I/O stream Operation type r or rb w or wb Symbol of the open function Note Open it in a read-only manner Truncate the file to 0 byte length, or create it in a write manner O_RDONLY O_WRONLY|O_CREAT|O_TRUNC Add, and open it in a write manner at a or ab the end of the file, or create it in a O_WRONLY|O_CREAT|O_APPEND write manner r+ or r+b or rb+ w+ or w+b or wb+ a+ or a+b or ab+ Open it in read and write manners Truncate the file to 0 byte length, or open it in read and write manners Open or create it from the file end in read and write manners O_RDWR O_RDWR|O_CREAT|O_TRUNC O_RDWR|O_CREAT|O_APPEND Use character b as a part of the mode. Therefore, the standard I/O system can differentiate the text file and the binary file. However, these two files are not differentiated in SylixOS, so that Character b is invalid in SylixOS. For the fdopen function, the meaning of the mode parameter is slightly different. The file descriptor has been opened. Therefore, the file opened via the fdopen function for writing are not truncated. In addition, the append write manner of standard I/O cannot be used to create the file. After opening a file with append write, the data will be written at the end of the file each time. If multiple processes open the same file via the append write manner of standard I/O, the data from each process will be correctly written in the file. Calling the fclose function close an open stream. #include Manual OS. SpaceChain 123 Application Development Manual SpaceChain OS int fclose(FILE *fp); Prototype analysis of Function fclose: For success of the function, return 0. For failure, return EOF and set the error number; Parameter fp is the file pointer. Before the file is closed, the output data in the buffer zone is flushed first, and the input data in the buffer is discarded. If the standard I/O library has automatically distributed the buffer zone, it is released. When a process is terminated normally, all standard I/O streams with unwritten buffer data are flushed, and all open standard I/O streams are closed. 4. Read and write streams Once the stream is opened, one can read and write different types of I/O, and read a character at a time when calling the following functions. #include int getc(FILE *fp); int fgetc(FILE *fp); int getchar(void); Prototype analysis of Function getc: For success of the function, return the next character. For failure, return EOF; Parameter fp is the file pointer. Prototype analysis of Function fgetc: For success of the function, return the next character. For failure, return EOF; Parameter fp is the file pointer. Prototype analysis of Function getchar: For success of the function, return the next character. For failure, return EOF. The getchar function is equal to getc (stdin). The difference between the getc function and the getc function is that the getc function can be implemented as the macro, and the fgetc function cannot be implemented as the macro. Above 3 input functions correspond to the following 3 output functions. #include int putc(int c, FILE *fp); int fputc(int c, FILE *fp); int putchar(int c); Manual OS. SpaceChain 124 Application Development Manual SpaceChain OS Prototype analysis of Function putc: For success of the function, return the entered character. For failure, return EOF and set the error number; Parameter c is the character to be entered; Parameter fp is the file pointer. Prototype analysis of Function fputc: For success of the function, return the entered character. For failure, return EOF and set the error number; Parameter c is the character to be entered; Parameter fp is the file pointer. Prototype analysis of Function putchar: For success of the function, return the entered character. For failure, return EOF and set the error number; Parameter c is the character to be entered. Putchar(c) is equal to putc(c, stdout), and the putc function and the fputc function can output a character to the appointed stream. The difference is that the putc function can be implemented as the macro, and the fputc function cannot be implemented as the macro. The following functions can read a line of characters from the appointed stream (line terminators are represented with "\n"). #include char *fgets(char *buf, int n, FILE *fp); char *gets(char *buf); Prototype analysis of Function fgets: For success of the function, return the first address of bus. For failure, return NULL; Parameter buf is the character buffer zone; Parameter n is the length of the buffer zone; Parameter fp is the file pointer. Prototype analysis of Function gets: For success of the function, return the first address of buf. For failure, return NULL; Parameter buf is the character buffer zone, Manual OS. SpaceChain 125 Application Development Manual SpaceChain OS Two functions appoint the address of the buffer zone to which the lines read will be sent. The gets function is read from standard input, and the fgets function is read from the appointed stream. The gets function must appoint the length of the buffer zone, and the function is always read until the next break line character, not exceeding n-1 characters. The read character is sent to the buffer zone ending with a null character. If the number of characters in the final line include the line is more than n-1, the fgets function returns only an incomplete line. The buffer always ends with the null byte, and the next call to the fgets function will continue read the line. The gets function is not recommended, because the caller does not appoint the ① length of the buffer, which may cause buffer area overflow . The following function can output one line of characters to the appointed stream. #include int fputs(const char *str, FILE *fp); int puts(const char *str); Prototype analysis of Function fputs: For success of the function, return the non-negative value. For failure, return EOF and set the error number; Parameter str is character string to be written; Parameter fp is the file pointer. Prototype analysis of Function puts: For success of the function, return the non-negative value. For failure, return EOF and set the error number; Parameter str is character string to be written. The fputs function writes a null-terminated string to the appointed stream, and the stop character null at the end is not written. The puts function writes a null-terminated string to the standard output, and the stop character null is not written. Unlike the fputs function, the puts function will write a break line character to the standard output afterwards. The puts function is not recommended generally. The following program shows how to use the standard I/O read and write functions. The program writes a string to the open file, then call the rewind function (usage of the function will be introduced later) to move the file's current pointer to beginning of the file, and call the fgets function to read the character string in the file for printing. Program List 5.10 Use of standard I/O function read and write #include Manual OS. SpaceChain 126 Application Development Manual SpaceChain OS int main (int argc, char *argv[]) { FILE int *fp; ret; char char *str = "This is test sylixos string functions example."; buf[64] = {0}; fp = fopen("file", "r+"); if (fp == NULL) { fprintf(stderr, "fopen error.\n"); return (-1); } ret = fputs(str, fp); if (ret < 0) { fprintf(stderr, "fputs write error.\n"); fclose(fp); return (-1); } rewind(fp); beginning of the file /* currently offset to the */ if (fgets(buf, sizeof(buf), fp) == NULL) { fprintf(stderr, "fgets read fp error.\n"); fclose(fp); return (-1); } fprintf(stdout, "buf: %s\n", buf); fclose(fp); return (0); } Run the program under the SylixOS Shell: # ./iorw_test buf: This is test sylixos string functions example. 5. Positioning stream #include long ftell(FILE *fp); int fseek(FILE *fp, long offset, int whence); Manual OS. SpaceChain 127 Application Development Manual SpaceChain OS void rewind(FILE *fp); Prototype analysis of Function ftell: For success of the function, return the current file offset. For failure, return -1 and set the error number; Parameter fp is the file pointer. Prototype analysis of Function fseek: For success of the function, return 0. For failure, return -1 and set the error number; Parameter fp is the file pointer; Parameter offset is the set offset; Parameter whence, as shown in Table 5.3. Prototype analysis of Function rewind: Parameter fp is the file pointer. For a binary file, its file location indicator is measured in byte from the file start location. When ftell function is used for the binary file, the return value is this byte location. In order to use fseek to locate a binary file, one must appoint a byte offset and whence. ISO C does not require an implementation to support SEEK_END for binary files. The reason is that some systems require that the length of the binary file is the integral multiple of a certain magic number. However, SEEK_END is supported in SylixOS. For the text file, the current location of their files may not be measured by the simple byte offset. This is also mainly in non-UNIX systems, and the text files may be stored in different formats. In order to locate a text file, whence must be SEEK_SET, and offset can only have two values: 0 (rewind to the file start location), or the value returned by ftell function of the file. A stream can also be set to the file start location by using rewind function. The fgetpos function and the fsetpos function are introduced by ISO C standard. #include int fgetpos(FILE *fp, fpos_t *pos); int fsetpos(FILE *fp, const fpos_t *pos); Prototype analysis of Function fgetpos: For success of the function, return 0. For failure, return non-0 value and set the error number; Parameter fp is the file pointer. The output parameter pos is the file offset location. Manual OS. SpaceChain 128 Application Development Manual SpaceChain OS Prototype analysis of Function fsetpos: For success of the function, return 0. For failure, return -1 and set the error number; Parameter fp is the file pointer. Parameter pos is the file offset. The fgetspos function stores the current value of the file position indicator in the object pointed to by pos. You can use this value to relocate the stream to that position when the fsetpos function is called later. 6. I/O formatting #include int printf(const char *format, ...); int fprintf(FILE *fp, const char *format, ...); int fdprintf(int fd, const char *format, ...); int sprintf(char *buf, const char *format, ...); int snprintf(char *buf, size_t n, const char *format, ...); Prototype analysis of Function printf: For success of the function, return the number of characters output. For failure, return the negative value; Parameter format is the format character string; Parameter... is a variable parameter. Prototype analysis of Function fprintf: For success of the function, return the number of characters output. For failure, return the negative value; Parameter fp is the file pointer; Parameter format is the format character string; Parameter... is a variable parameter. Prototype analysis of Function fdprintf: For success of the function, return the number of characters output. For failure, return the negative value; Parameter fd is the file descriptor; Parameter format is the format character string; Parameter... is a variable parameter. Manual OS. SpaceChain 129 Application Development Manual SpaceChain OS Prototype analysis of Function sprintf: For success of the function, return the number of characters output. For failure, return the negative value; Parameter buf is the pointer of the character buffer zone; Parameter format is the format character string; Parameter... is a variable parameter. Prototype analysis of Function snprintf: For success of the function, return the number of characters output. For failure, return the negative value; Parameter buf is the pointer of the character buffer zone; Parameter n is the length of the buffer zone; Parameter format is the format character string; Parameter... is a variable parameter. The printf function prints characters to the standard output stream in the format specified by format. The fprintf function prints characters to the stream specified by fp in the format specified by format. Parameter fd of the fdprintf function is the descriptor of the opened file. This function prints the format character to the file specified by fd. The sprintf function and The snprintf function print the format characters to the buffer zone specified by buf. The difference between two functions is that snprintf function specifies the length of the buffer zone, so as to guarantee memory security. It is not suggested to use sprintf function. #include int scanf(const char *format, ...); int fscanf(FILE *fp, const char *format, ...); int sscanf(const char *buf, const char *format, ...); Prototype analysis of Function scanf: For success of the function, return the number of matching characters. Otherwise, return EOF; Parameter format is the format character string; Parameter... is a variable parameter. Prototype analysis of Function fscanf: For success of the function, return the number of matching characters. Otherwise, return EOF; Manual OS. SpaceChain 130 Application Development Manual SpaceChain OS pointer; Parameter fp is the file Parameter format is the format character string; Parameter... is a variable parameter. Prototype analysis of Function sscanf: For success of the function, return the number of matching characters. Otherwise, return EOF; Parameter buf is the pointer of the buffer zone; Parameter format is the format character string; Parameter... is a variable parameter. The scanf function scans the standard input and stores the value in the format format to the appropriate memory. The memory address will be given in the variable parameter. Functions of the fscanf function are similar to those of the sscanf function. The difference is that the fscanf function scans characters from the stream specified by fp, while the sscanf function scans characters from the buffer zone specified by buf. The following program shows how to use the formatting function. The program uses the fdopen function to get the file pointer from the opened file descriptor. Program List 5.11 Use of the formatting function #include #include int main (int argc, char *argv[]) { FILE char *fp; buf[64] = {0}; char *content = "Format print function test."; char *temp; int fd; fd = open("file", O_RDWR | O_CREAT, 0644); if (fd < 0) { fprintf(stderr, "open failed test done.\n"); return (-1); } fp = fdopen(fd, "r+"); if (fp == NULL) { Manual OS. SpaceChain 131 Application Development Manual SpaceChain OS fprintf(stderr, "fdopen failed test done.\n"); close(fd); return (-1); } fprintf(fp, "%s", content); rewind(fp); temp = fgets(buf, sizeof(buf), fp); if (temp == NULL) { fprintf(stderr, "fgets read error or file end.\n"); fclose(fp); return (-1); } fprintf(stdout, "buf: %s\n", buf); fclose(fp); return (0); } Run the program under the SylixOS Shell: # ./format_test buf: Format print function test. 5.3 Asynchronous I/O access The signaling mechanism (see Chapter 10 Signaling System) provides a way to asynchronously notify that certain events have occurred. However, such asynchronous I/O is limited. They cannot be used on all file types, and only a signal can be used. If asynchronous I/O is performed on more than one file descriptors, then the process does not know which file descriptor the signal corresponds to when receiving the signal. SylixOS supports the standard asynchronous I/O interfaces defined by the POSIX 1003.1b real-time extension protocol, namely aio_read function, aio_write function, aio_fsync function, aio_cancel function, aio_error function, aio_return function, aio_suspend function and lio_listio function. This group of APIs is used to operate asynchronous I/O. Asynchronous I/O is a concept proposed as per synchronous I/O. It does not require the thread to wait for I/O results, but only requests transmission. Then the system automatically completes the I/O transmission. At the end or when an error occurs, the corresponding I/O signal will be generated. The user program only needs to set the corresponding signal sink function to handle an asynchronous I/O event. Manual OS. SpaceChain 132 Application Development Manual SpaceChain OS 5.3.1 POSIX asynchronous I/O The POSIX asynchronous I/O interface provides a set of consistent method for asynchronous I/O for different types of files. These interfaces come from the real-time draft standard, and these asynchronous I/O interfaces adopt AIO control block to describe I/O operation. The aiocb structure defines AIO control block, and SylixOS implements this structure as follows: struct aiocb { int aio_fildes; /* file descriptor */ off_t aio_offset; /* file offset */ /* Location of buffer. */ volatile void *aio_buf; size_t aio_nbytes; /* Length of transfer. */ int aio_reqprio; /* Request priority offset. */ struct sigevent aio_sigevent; /* Signal number and value. */ int aio_lio_opcode; /* Operation to be performed. */ …… }; The following describes the structure members in detail: The aio_fildes is the file descriptor to be operated, i.e., the file descriptor returned by the open function; The aio_offset is the file offset at the start of read and write; The aio_buf is a pointer of the data buffer zone. For read, the data is read from the buffer zone. For write, the data is written to the buffer zone; The aio_nbytes is the number of bytes read and written; The aio_reqprio is the priority of asynchronous I/O request. This priority determines the read and write sequence, which also means that the higher the priority is, the sooner it is read or written. The aio_sigevent is the signal to be sent. When a read or write is completed, the the signal specified by the application will be sent. The aio_lio_opcode is the type of asynchronous I/O operation, as shown in Table 5.10; Table 5.10 Type of asynchronous I/O operation Operation type Note LIO_NOP No transmission request LIO_READ Request a read operation Manual OS. SpaceChain 133 Application Development Manual SpaceChain OS LIO_WRITE Request a write operation LIO_SYNC Request asynchronous I/O synchronization Before asynchronous I/O operation, we need to initialize the AIO control block firstly, and then request asynchronous write operation by calling the aio_read function, and request an asynchronous read operation by calling the aio_write function. #include int aio_read(struct aiocb *paiocb); int aio_write(struct aiocb *paiocb); Prototype analysis of Function aio_read: For success of the function, return 0. For failure, return -1 and set the error number; Parameter paiocb is AIO control block. Prototype analysis of Function aio_write: For success of the function, return 0. For failure, return -1 and set the error number; Parameter paiocb is AIO control block. After the aio_read function and the aio_write function have been called successfully, the asynchronous I/O request has already been put into the waiting queue by the operating system. These return values have nothing to do with the results of actual I/O operation. If it is required to view the return status of the function, you can call aio_error function. To force all waiting asynchronous operations to write to memory without waiting, one can create an AIO control block, and call the aio_fsync function. #include int aio_fsync(int op, struct aiocb *paiocb); Prototype analysis of Function aio_fsync: For success of the function, return 0. For failure, return -1 and set the error number; Parameter op is the operation option; Parameter paiocb is AIO control block. The aio_fildes member in the structure struct aiocb is the file to be synchronized. If the op parameter is set to O_SYNC, the aio_fsync call is similar to fsync. If the op parameter is set as O_DSYNC, the aio_fsync call is similar to fdatasync (Currently Manual OS. SpaceChain 134 Application Development Manual SpaceChain OS SylixOS does not make a specific distinction between the two situations). Same with the aio_read function and the aio_write function, aio_error can be called by viewing the aio_fsync function. #include int aio_error(const struct aiocb *paiocb); Prototype analysis of Function aio_error: For success of the function, return 0. For failure, return -1 and set the error number; Parameter paiocb is AIO control block. The return value shall be one of the following 4 situations: 0: asynchronous operation completed successfully, call aio_return function to get the return code; -1: failure to call aio_error; EINPROGRESS: waiting for asynchronous operation; Other cases: any other return value is the error code returned by the associated asynchronous operation failure. If asynchronous operation is successful, one can call the aio_return function to get the return value of asynchronous operation. #include ssize_t aio_return(struct aiocb *paiocb); Prototype analysis of Function aio_return: For success of the function, return the return value after asynchronous operation completion. For failure, return -1 and set the error number; Parameter paiocb is AIO control block. When performing I/O operations, one can use asynchronous I/O if there are other transactions to be processed without being blocked by I/O operation. However, if all transactions are completed but asynchronous operation is not completed, one can call the aio_suspend function to block the process until operation is completed. #include int aio_suspend(const struct aiocb * const list[], int nent, const struct timespec *timeout); Prototype analysis of Function aio_suspend: Manual OS. SpaceChain 135 Application Development Manual SpaceChain OS For success function, return 0. For failure, return -1 and set the error number; Parameter list is the array of AIO control blocks; Parameter nent is the number of array elements; Parameter timeout is the set timeout period. of the The aio_suspend may return one of the following 3 situations: In case of block timeout, then the aio_suspend function will return -1; If any I/O operation is completed, the aio_suspend function will return 0; If all asynchronous I/O operations have been completed when the aio_suspend function is called, the aio_suspend function will return when not blocked. Parameter list will automatically ignore the null pointer, and the non-null element is the initialized AIO control block. When there are still waiting asynchronous I/O operations not to be completed, one can call the aio_cancel function to cancel them. #include int aio_cancel(int fildes, struct aiocb *paiocb); Prototype analysis of Function aio_cancel: This function return value is shown in Table 5.11; Table 5.11 Type of aio_cancel return value Type of return value Note AIO_CANCELED All requested operations have been canceled AIO_NOTCANCELED At least one requested operation is not canceled AIO_ALLDONE Completed before the request is canceled -1 Failure to call the aio_cancel function Parameter fildes is the file descriptor to be operated; Parameter paiocb is AIO control block. If paiocb is NULL, the system will try to cancel all unfinished asynchronous I/O operations on this file. In other cases, the system will attempt to cancel the single asynchronous I/O operation described by the AIO control block specified by paiocb. If the asynchronous I/O operation is successfully canceled, the corresponding AIO Manual OS. SpaceChain 136 Application Development Manual SpaceChain OS control block will call the aio_error function to return error ECANCE LED. If the operation cannot be canceled, the corresponding AIO control block will not be modified due to call of the aio_cancel function. The following program shows how to use the above function. The program constructs an AIO control block for read operation, and then calls aio_read to request read operation. After the operation is completed, the signal_handler function will be notified via the signal (see the signal section) for further processing. Program List 5.12 Use of asynchronous I/O function ① #include #include #include #include #include #include #define BUFSIZE (64) void signal_handler (union sigval val) { struct aiocb ssize_t *paio = (struct aiocb *)val.sival_ptr; ret; ret = aio_return(paio); if (ret < 0) { fprintf(stderr, "aio_return error.\n"); return; } fprintf(stdout, "len: %ld\ncontent: %s\n", ret, (char *)(paio->aio_buf)); } int main (int argc, char *argv[]) { int fd; int ret; static struct aiocb aio; const struct aiocb *const list[] = {&aio}; fd = open("file", O_RDONLY); if (fd < 0) { fprintf(stderr, "open file failed.\n"); Manual OS. SpaceChain 137 Application Development Manual SpaceChain OS return (-1); } memset(&aio, 0, sizeof(struct aiocb)); /* * 设置 AIO 控制块 */ aio.aio_fildes = fd; aio.aio_buf = malloc(BUFSIZE + 1); aio.aio_nbytes = BUFSIZE; aio.aio_offset = 0; aio.aio_reqprio = 1; aio.aio_lio_opcode = LIO_READ; aio.aio_sigevent.sigev_notify = SIGEV_THREAD | SIGEV_SIGNAL; aio.aio_sigevent.sigev_value.sival_ptr = (void *)&aio; aio.aio_sigevent.sigev_signo = SIGUSR1; aio.aio_sigevent.sigev_notify_function = signal_handler; ret = aio_read(&aio); if (ret < 0) { perror("aio_read"); close(fd); return (-1); } aio_suspend(list, 1, NULL); close(fd); return (0); } Run the program under the SylixOS Shell: # ./aio_read_test len: 64 content: This is a sylixos test file Calling the lio_listio function can submit a series of I/O requests described by a list of AIO control blocks. #include int lio_listio(int mode, struct aiocb * const list[], int nent, struct sigevent *sig); Prototype analysis of Function lio_listio: Manual OS. SpaceChain 138 Application Development Manual SpaceChain OS For success function, return 0. For failure, return -1 and set the error number; of the Parameter mode is the transfer mode (LIO_WAIT, LIO_NOWAIT); Parameter list is the array of request AIO control blocks; Parameter nent is the array of AIO control blocks; Parameter sig is the signal method generated after all I/O operations are completed. Parameter mode determines whether I/O is really asynchronous. If this parameter is set as LIO_WAIT, the lio_listio function will return after all I/O operations specified by the list are completed. In this case, Parameter sig will be ignored. If the mode parameter is set as LIO_NOWAIT, the lio_listio function will return immediately after the I/O request is enqueued. The process will be notified asynchronously as specified by Parameter sig after all I/O operations are completed. If notice is not required, one can set Parameter sig as NULL. It might also be noted that each asynchronous I/O operation can be set with its own notification mode. However, the notification mode specified by Parameter sig is an additional notification mode, and it will be notified only after all operations are completed. In each AIO control block, the aio_lio_opcode field specifies whether the operation is a read operation (LIO_READ), a write operation (LIO_WRITE), or a null operation (LIO_NOP) which will be ignored. The following program shows how to use the lio_listio function. It can be seen from the code that a single lio_listio function call initiates multiple transmissions. From this point, it can be concluded that performance of the lio_listio function is worth studying. Program List 5.13 Use of lio_listio function #include #include #include #include #include #include #define BUFSIZE (64) void signal_handler (union sigval val) { fprintf(stdout, "async operator complete.\n"); } int main (int argc, char *argv[]) Manual OS. SpaceChain 139 Application Development Manual SpaceChain OS { int fd; int ret; struct aiocb aio[2]; struct aiocb *const list[] = {&aio[0], &aio[1]}; static struct sigevent notify; fd = open("file", O_RDONLY); if (fd < 0) { fprintf(stderr, "open file failed.\n"); return (-1); } memset(&aio[0], 0, sizeof(struct aiocb)); memset(&aio[1], 0, sizeof(struct aiocb)); /* * Set up the first AIO control block */ aio[0].aio_fildes = fd; aio[0].aio_buf = malloc(BUFSIZE + 1); aio[0].aio_nbytes = BUFSIZE; aio[0].aio_offset = 0; aio[0].aio_reqprio = 1; aio[0].aio_lio_opcode = LIO_READ; /* * Set up second AIO control block */ aio[1].aio_fildes = fd; aio[1].aio_buf = malloc(BUFSIZE + 1); aio[1].aio_nbytes = BUFSIZE; aio[1].aio_offset = BUFSIZE; aio[1].aio_reqprio = 2; aio[1].aio_lio_opcode = LIO_READ; notify.sigev_signo = SIGUSR1; notify.sigev_notify_function = signal_handler; notify.sigev_notify = SIGEV_THREAD | SIGEV_SIGNAL; ret = lio_listio(LIO_NOWAIT, list, 2, ¬ify); if (ret < 0) { perror("lio_listio"); close(fd); return (-1); Manual OS. SpaceChain 140 Application Development Manual SpaceChain OS } sleep(60); close(fd); return (0); } Run the program under the SylixOS Shell: # ./lio_listio_test signal handler. During implementation of asynchronous I/O of SylixOS, I/O operation will be performed via an additional thread (proxy thread). In order to be able to set or get the stack information of the proxy thread, SylixOS adds the following set of functions. This set of functions are not defined in the standard. It might also be noted that that the set stack information is only valid for future start threads (this usually occurs in future I/O requests). #include int aio_setstacksize(size_t newsize); size_t aio_getstacksize(void); Prototype analysis of Function aio_setstacksize: For success of the function, return 0. For failure, return -1 and set the error number; Parameter newsize is the new stack size. Prototype analysis of Function aio_getstacksize: This function always returns the size of the current thread stack. Call the aio_setstacksize function to set the stack size for future start threads (proxy thread), and call the aio_getstacksize function to get the size of the current thread (proxy thread) stack. 5.4 Advanced I/O access 5.4.1 Decentralized aggregation operation #include ssize_t readv(int ssize_t writev(int iFd, struct iovec *piovec, int iIovcnt); iFd, const struct iovec *piovec,int iIovcnt); Prototype analysis of Function readv: Manual OS. SpaceChain 141 Application Development Manual SpaceChain OS For success of the function, return the number of bytes read. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; The output parameter popovec is a pointer of the decentralized buffer zone array; Parameter iIovcnt is the number of the buffer zones. Prototype analysis of Function writev: For success of the function, return the number of bytes written. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter popovec is an pointer of the aggregation buffer zone (data to be sent) array; Parameter iIovcnt is the number of the buffer zones. The readv function and the writev function are used to read and write multiple non-contiguous buffer zones in a single function call. Sometimes, these two functions are called scatter read and gather write. The second parameter of these two functions is a pointer to the iovec structure array: struct iovec { PVOID iov_base; /* base address size_t iov_len; /* length */ */ }; The member iov_base in the structure points to the first address of a buffer zone, and the member iov_len is the length of the buffer zone. The relation between the parameters of these two functions and the iovec structure is shown in Figure 5.7. (缓冲区=relief area) Figure 5.7 Relation between readv and writev and iovec structures The order to read readv function from the buffer zone is piovec[0], piovec[1] until piovec [iIovcnt-1], and the total number of bytes read is successfully returned. The write order of the writev function is the same with that of readv, and the total number of bytes Manual OS. SpaceChain 142 Application Development Manual SpaceChain OS written is successfully returned. The following program shows how to use the readv function and the writev function. The program defines two iovec elements, two buffer zones with different lengths, calls the readv function to read data from the file a.test, and then calls the writev function to write data to the file b.test. Program List 5.14 Use of readv and writev #include #include int main (int argc, char *argv[]) { int fd; char buf1[15], buf2[34]; struct iovec iov[2]; ssize_t ret; iov[0].iov_base = buf1; iov[0].iov_len = sizeof(buf1); iov[1].iov_base = buf2; iov[1].iov_len = sizeof(buf2); fd = open("a.test", O_RDONLY); if (fd < 0) { fprintf(stderr, "open file: %s failed.", "a.test"); return (-1); } ret = readv(fd, iov, 2); if (ret < 0) { fprintf(stderr, "readv error.\n"); close(fd); return (-1); } fprintf(stdout, "readv read bytes %ld\n", ret); close(fd); fd = open("b.test", O_WRONLY); if (fd < 0) { fprintf(stderr, "open file: %s failed.\n", "b.test"); return (-1); } Manual OS. SpaceChain 143 Application Development Manual SpaceChain OS ret = writev(fd, iov, 2); if (ret < 0) { fprintf(stderr, "writev error.\n"); close(fd); return (-1); } fprintf(stdout, "writev write bytes %ld\n", ret); close(fd); return (0); } Run the program under the SylixOS Shell. It can be seen from the program operation results that the readv function will read two buffer zones in turn, while the writev function will write the data in the two buffer zones to the specified file. # cat a.test This is a sylixos test readv and writev example. # ./rwv_test readv read bytes 49 writev write bytes 49 # cat b.test This is a sylixos test readv and writev example. 5.4.2 Non-blocking I/O ① Some "low-speed" system functions may block the process forever, such as certain inter-process communication functions, certain ioctl operations and so on. Non-blocking I/O makes open, read and write operations unblocked forever (requiring support by the device driver). If these operations cannot be completed, the call immediately returns with an error, indicating that the operation will be blocked if it continues. There are two ways to get a non-blocking I/O: Call the open function to get a file descriptor, and one can specify the O_NONBLOCK flag; If the file is already open, one can call ioctl to specify the FIONBIO command, or call fcntl to specify the F_SETFL option. 5.4.3 I/O multiplexing The I/O multiplexing technique is to construct a list of file descriptors which we are Manual OS. SpaceChain 144 Application Development Manual SpaceChain OS interested in, and then call a function. The function will not return until one of these descriptors is ready for I/O. 4 functions of select, pselect, poll and ppoll can implement I/O multiplexing functions. When these functions return, the process or thread is notified which file descriptors have been ready for I/O operation. The details of the select function, pselect function, poll function, and ppoll function are described as below. 1. select function group #include int select(int iWidth, fd_set *pfdsetRead, fd_set *pfdsetWrite, fd_set *pfdsetExcept, struct timeval *ptmvalTO); int pselect(int iWidth, fd_set *pfdsetRead, fd_set *pfdsetWrite, fd_set *pfdsetExcept, const struct timespec *ptmspecTO, const sigset_t *sigsetMask); Prototype analysis of Function select: For success of the function, return the quantity of descriptor waited. For failure, return -1 and set the error number; Parameter iWidth is the maximum descriptor in the list of file descriptors plus 1; Parameter pfdsetRead is the set of read descriptors; Parameter pfdsetWrite is the set of write descriptors; Parameter pfdsetExcept is the set of exception descriptors; Parameter ptmvalTo is the waiting timeout. Prototype analysis of Function pselect: For success of the function, return the quantity of descriptor waited. For failure, return -1 and set the error number; Parameter iWidth is the maximum descriptor in the list of file descriptors plus 1; Parameter pfdsetRead is the set of read descriptors; Parameter pfdsetWrite is the set of write descriptors; Parameter pfdsetExcept is the set of exception descriptors; Parameter ptmspecTo is the waiting timeout; Manual OS. SpaceChain 145 Application Development Manual SpaceChain OS the signal blocked at waiting. Parameter sigsetMask is Judging from parameters of the select function and the parameters transferred to the kernel, the following points can be seen: Tell the kernel which file descriptors we care about; Conditions which we care about for each file descriptor (read, write and exception); How long you are willing to wait (you can wait forever, you may not wait, you can wait for the specified time); When select returns, we can know how many file descriptors are ready and which file descriptors are ready. Read, write and other operations can be performed with these ready file descriptors. Parameter ptmvalTo can be divided into 3 kinds of situations: When ptmvalTo == NULL, it represents waiting forever, When ptmvalTo->tv_sec == 0 && ptmvalTo->tv_usec == 0, it represents noy wait, When ptmvalTo->tv_sec != 0 || ptmvalTo->tv_usec != 0, it represents waiting for the satisfy number of seconds and microseconds. Parameters pfdsetRead, pfdsetWrite and pfdsetExcept are pointers to the file descriptor set. Each file descriptor set is stored in a variable of the fd_set data type. For this type, different systems may have different implementations. Here we can consider it as a very large byte array, and each file descriptor occupies a bit. In SylixOS, the following group of macros is provided for variables of fd_set type: Table 5.12 Variable operation macro of fd_set type Macro name Note FD_SET(n, p) Set the file descriptor n in the file descriptor set p FD_CLR(n, p) Clear the file descriptor n from file descriptor set p FD_ISSET(n, p) Determine whether the file descriptor n belongs to the file descriptor set p FD_ZERO(p) Clear the file descriptor set p After declaring a file descriptor set of fd_set type, firstly use FD_ZERO to clear the file descriptor set, and then use FD_SET to put the file descriptor we care about into the set. When select returns successfully, use FD_ISSET to determine it is the file descriptor we Manual OS. SpaceChain 146 Application Development Manual SpaceChain OS care about. The select function has 3 possible return values: The return value -1 indicates an error. For example, if a signal is captured when none of the specified file descriptors is ready, return -1; Return value 0 represents no file descriptor is ready, because no file descriptor is ready within the specified time, i.e., call timeout; The return value is an integer larger than zero. This value is the sum of all ready file descriptors in the 3 file descriptor sets. The select function returns the sum of the ready file descriptors, and here "ready" has the following meanings: For a file descriptor in the read set, read operation will not block; For a file descriptor in the write set, write operation will not block; For a file descriptor in the exception set, there is a pending exception condition. It might also be noted that if the file end is encountered on a file descriptor, the select function will consider the file descriptor as readable, and then call the read function to return 0. In the above definition, we see that except for the last two parameters and select function, other parameters of the pselect function are the same. Let's introduce these two different parameters. The type of select function timeout value is struct timeval, while timeout value type of the pselect function is struct timespec (see Chapter 11 Time Management). The timespec structure expresses the timeout value in seconds and nanoseconds, that is to say, the ① pselect function provides more accurate timeout value than the select function . The pselect function can use signal mask words. If sigmask is NULL, the operation conditions of the pselect function and the select function are the same. Otherwise, sigmask points to a signal mask word, which is installed through atomic operation when the pselect function is called. On return, the previous signal mask word is recovered. The following program shows how to the select function. The program waits for the standard input (STDIN_FILENO) descriptor to be readable. If select returns within the timeout period, the standard input is read, and the read character is printed. Program List 5.15 Use of select function #include #include int main (int argc, char *argv[]) Manual OS. SpaceChain 147 Application Development Manual SpaceChain OS { fd_set fdset; int ret; struct timeval timeout; char ch; timeout.tv_sec = 10; timeout.tv_usec = 0; for (;;) { FD_ZERO(&fdset); FD_SET(STDIN_FILENO, &fdset); ret = select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout); if (ret <= 0) { break; } else if (FD_ISSET(STDIN_FILENO, &fdset)){ read(STDIN_FILENO, &ch, 1); if (ch == '\n') { continue; } fprintf(stdout, "input char: %c\n", ch); if (ch == 'q') { break; } } } return (0); } Run the program under the SylixOS Shell, and enter the characters to see the corresponding print. # ./select_test h input char: h 2. poll function group Functions of the poll function are similar to those of the select function, but the function interface are different. #include Manual OS. SpaceChain 148 Application Development Manual SpaceChain OS int poll(struct pollfd fds[], nfds_t nfds, int timeout); int ppoll(struct pollfd fds[], nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask); Prototype analysis of Function poll: For success of the function, return the quantity of descriptor waited. For failure, return -1 and set the error number; Parameter fds is the array of poll file descriptors; Parameter nfds is the number of elements in fds array; Parameter timeout is the timeout value. Prototype analysis of Function ppoll: For success of the function, return the quantity of descriptor waited. For failure, return -1 and set the error number; Parameter nfds is the number of elements in fds array; Parameter timeout_ts is the timeout value; Parameter sigmask is a signal blocked while waiting. Unlike the select function, the poll function does not construct a file descriptor set for each condition. Instead, it constructs an array of pollfd structure. Each array element specifies a file descriptor number and the conditions for which we are interested in the file descriptor. struct pollfd { int fd; /* file descriptor being polled */ short int events; /* the input event flags */ short int revents; /* the output event flags */ }; When the poll function is called, events in each fds element shall be set as one or more values in Table 5.13. These values tell the kernel which events of the each file descriptor we care about. At return, the revents member is set by the kernel to indicate which events occur in each file descriptor. 3 lines in Table 5.13 represents the readable, writable and exception from top to bottom. Manual OS. SpaceChain 149 Application Development Manual SpaceChain OS Table 5.13 pollfd event value Macro name POLLIN Note Can read data beyond high priority without blocking (Equivalent to POLRRDNORM | POLLRDBAND) POLLRDNORM Can read ordinary data without blocking POLLRDBAND Can read priority data without blocking POLLPRI Can read data with high priority without blocking POLLOUT Can write the ordinary data without blocking POLLWRNORM Same with POLLOUT POLLWRBAND Can write priority data without blocking POLLERR There has been error POLLHUP Hanged off The timeout wait parameters of the poll function are similar to those of the select function which are also divided into three cases. We shall notice that the poll function and the select function will not affect the blocking condition because a file descriptor is blocked. The behaviors of the ppoll function are similar to those of poll, except that the ppoll function can specify the signal mask word. 5.4.4 File record lock When a process is reading or modifying a portion of a file, the file record lock can be used to prevent other processes from modifying the same area of the same file. It can be used to lock an area of a file or the entire file. SylixOS supports multiple file record lock API. Previously, we said that SylixOS supports multiple device driver models. However, only NEW_1 device driver supports file record lock function currently. Such driver file nodes are similar to vnode of the UNIX system. Firstly, we introduce the fcntl lock with flexible functions. For the function prototype, refer to the fcntl part of Section 5.2.1. When introducing the fcntl function earlier, we mentioned that the fcntl function contains the function to operate the file record lock. This function contains 3 commands: F_GETLK, F_SETLK and F_SETLKW. The 3rd parameter of fcntl record lock is a flock structure pointer. Manual OS. SpaceChain 150 Application Development Manual SpaceChain OS struct flock { short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */ short l_whence; /* flag to choose starting offset */ off_t l_start; /* relative offset, in bytes */ off_t l_len; /* length, in bytes; 0 means */ /* lock to EOF */ /* returned with F_GETLK */ pid_t l_pid; …… }; The meaning of the struct flock member is as follows: l_type is the lock type: F_RDLOCK (shared read lock), F_WRLOCK (exclusive write lock) and F_UNLCK (unlock); l_whence value, as shown in Table 5.3; l_start is relative to the starting position of l_whence offset (note that it cannot be locked from the beginning of the file); l_len is the length of the lock area. If it is 0, the end of file (EOF) is locked. If data is added to the file, it will also be locked. l_pid is the process ID preventing the current process from locking (returned by the command F_GETLK). For the shared read locks and the exclusive write lock mentioned above, the basic rules are: any number of processes can have a shared read lock on a given byte, but only one process can have an exclusive write lock on a given byte. Further, if there has already been one or more read locks on a given byte, you cannot add a write lock on the byte; if there is a write lock on a byte, you cannot add any more locks. The above rules apply to lock requests made by different processes, but do not apply to lock requests proposed by the single process. That is to say, if a process already has a lock in a file range, and the process attempts to add a lock in the same range, it is also OK, and the new lock will replace the existing lock at the moment. Therefore, if a process adds a write lock to a file, and then attempts to add a read lock to the file, it will be executed successfully, and the original write lock will be replaced by the read lock. We will verify this view via Program List 5.16. In addition, when the read lock is added, the file descriptor must be read open. When the write lock is added, the file descriptor must be write open. Manual OS. SpaceChain 151 Application Development Manual SpaceChain OS Program List 5.16 Locking in the single process #include #include int main (int argc, char *argv[]) { int fd; struct flock fl; short lockt = F_WRLCK; fd = open("file", O_RDWR); if (fd < 0) { fprintf(stderr, "open file failed.\n"); return -1; } fl.l_type = lockt; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; if (fcntl(fd, F_SETLK, &fl) < 0) { perror("fcntl"); fprintf(stderr, "add write lock failed.\n"); close(fd); return -1; } fprintf(stdout, "add write lock success.\n"); lockt = F_RDLCK; fl.l_type = lockt; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; if (fcntl(fd, F_SETLK, &fl) < 0) { perror("fcntl"); fprintf(stderr, "add read lock failed.\n"); close(fd); return -1; } fprintf(stdout, "add read lock success.\n"); Manual OS. SpaceChain 152 Application Development Manual SpaceChain OS return 0; } Run the program under the SylixOS Shell: # ./lock_test add write lock success. add read lock success. SylixOS supports lock of the whole file via the traditional BSD function flock to lock the entire file. This function is an old API. #include int flock(int iFd, int iOperation); Prototype analysis of Function flock: For success of the function, return 0. For failure, return -1 and set the error number; Parameter iFd is the file descriptor; Parameter iOperation is the lock type (as shown in Table 5.14). Calling the flock function locks an open file, and the types of locks supported by this function are shown in Table 5.14. When the process uses the flock function to try to lock the file, if the file has already been locked by other processes, the process will be blocked until the lock is released, or when the flock function is called, the LOCK_NB parameter is used. When the file has been locked by other processes when attempts to lock, an error will return. The flock function can call the LOCK_UN parameter to release the file lock, or release the file lock by closing fd, which means that the flock lock will be released as the process exits. Table 5.14 Type of flock lock Type of flock lock LOCK_SH LOCK_EX Note Shared lock, multiple processes can use a lock, commonly used as read lock Exclusive lock, only allow to be occupied by a process, commonly used as write lock LOCK_NB Implement non-blocking, blocking at default LOCK_UN Unlock SylixOS supports the lockf function, which is a POSIX lock and can be seen as an package of the fcntl interface. Manual OS. SpaceChain 153 Application Development Manual SpaceChain OS #include int lockf(int iFd, int iCmd, off_t oftLen); Prototype analysis of Function lockf: For success of the function, return 0. For failure, return -1; Parameter iFd is the file descriptor; Parameter iCmd is the lock command; Parameter oftLen is the locked resource length, started from the current offset. The behavior of calling the lockf function is basically the same with that of the fcntl function. The file descriptor is input by the lockf function. The lock commands supported by this function are shown in Table 5.15. Table 5.15 Lockf lock command Lockf lock command Note F_ULOCK Unlock F_LOCK Request the exclusive lock in the blocking way, i.e., write lock F_TLOCK Request the exclusive lock in a non-blocking way, i.e., write lock F_TEST Get the lock status of the specified file area, and test whether it is locked 5.4.5 File memory mapping File memory mapping can map a disk file to a memory area in memory space. When the data is fetched from the buffer zone, it is equivalent to read the corresponding byte in the file. Correspondingly, when data is stored in the buffer zone, the corresponding byte is automatically written to the file. This makes it possible to perform I/O operations without using the read function and the write function. To use this feature, one shall firstly tell the kernel to map a given file into a memory area. This is realized via the mmap function. These technical details will be introduced in Chapter 12 Memory Management. Manual OS. SpaceChain 154 Application Development Manual SpaceChain OS Chapter 6 Thread management 6.1 Thread The thread is also called as the task, the instruction stream of a certain single sequence, and it is the smallest unit scheduling of the operating system. A standard thread consists of thread handle (or ID), current instruction pointer (PC), CPU register set and thread stack. Each thread is the scheduling unit of the operating system. The thread itself has only limited and indispensable resources during operation, such as CPU register, stack and so on. The kernel thread shares all kernel resources, such as the kernel file descriptor table, while the in-process thread shares all resources in the process, such as the process file descriptor table. A CPU can only run one thread at a moment (multi-CPU system can run multiple threads at the same time). If there are multiple threads in the system, the CPU requires running switch between several threads, equivalent to concurrent execution of multiple threads macroscopically. The correspondence between time and thread of CPU is determined via the scheduling algorithm of the operating system, For example, the time sharing operating system divides time into smaller fragments, called as time slices. After each thread runs for a period, the operating system will command the CPU to switch to another thread for execution. Each thread in the real-time operation system has its own priority. When it is required to execute the thread with high priority, the operating system will immediately switch the current CPU to execute the thread with higher priority, and such dispatching algorithm satisfy demands of the system for real-time signal response. 6.2 Thread state machine Multiple threads in the same process or kernel can be executed concurrently. However, threads has discontinuity during operation due to mutual restraint between threads. The threads also have three basic states of block, ready and run. Meanings of three states are as follows: Block: the thread lacks the conditions or resources to make it run, and can enter the ready state after conditions are satisfied. Ready: The thread already has all resources to make it run, waiting for scheduling of the operating system; Run: the thread has been scheduled by the operating system (the operating system distributes a CPU to the thread for execution of the thread code). Manual OS. SpaceChain 155 Application Development Manual SpaceChain OS The thread created in SylixOS system is always at any of these three states. Where, the blockage state is divided into the following due to different reasons: wait for semaphores, wait for messages, sleep and so on. State switching between threads is shown in Figure 6.1. Figure 6.1 State conversion diagram of the thread In Figure 6.1, the initial state is just a state before the thread was created. Let's see switching relationship between other three states: Ready → Run: the thread at the ready state is scheduled by the system, and the right to use CPU is gotten; Run → Ready: the threat at the running state is occupied by other threads, or the right to use CPU is abandoned; Ready → Block: other threads actively suspend it (not recommended for SylixOS); Block → Ready: the waiting resource becomes available; Run → Block: waiting for semaphores, receiving messages and sleeping make it blocked. It is required to indicate that SylixOS does not recommend using the thread suspend function to suspend other threads (a kind of blockage). Therefore, the structured design of the program will be damaged, so that design of the application is complicated and unpredictable, and it is easy to cause design error. After transition relationships of these states are analyzed, let's see the application interface function in SylixOS causing these changes in state: Table 6.1 State change function Function name Lw_Thread_Create State changes Create a thread, and the thread will enter the ready state Manual OS. SpaceChain 156 Application Development Manual SpaceChain OS Lw_Thread_Init Initialize a thread, and the thread enters the initial state Lw_Thread_Start Enable a thread at the initial state to enter the ready state Lw_Thread_Suspend Enable the thread to enter the blocked state (not recommended) Lw_Thread_Resume Enable the thread from the blocked state to the ready state (not recommended) Lw_Thread_ForceResume Force the thread to enter the ready state (not recommended) Lw_Thread_Yield Enable the thread actively abandon CPU to enter the ready state Lw_Thread_Wakeup The thread is awakened from the sleep mode into the ready state Lw_Semaphore_Wait Block thread Lw_Semaphore_Post Recover the thread from the blocked state to the ready state Lw_SemaphoreC_Wait Block thread Lw_SemaphoreC_Post Recover the thread from the blocked state to the ready state Lw_SemaphoreB_Wait Block thread Lw_SemaphoreB_Post Recover the thread from the blocked state to the ready state Lw_SemaphoreM_Wait Block thread Lw_SemaphoreM_Post Recover the thread from the blocked state to the ready state Lw_MsgQueue_Receive Block thread Lw_MsgQueue_Send Recover the thread from the blocked state to the ready state Lw_Time_Sleep Thread sleep enters the blocked state Lw_Time_SSleep Thread sleep enters the blocked state Lw_Time_MSleep Thread sleep enters the blocked state 6.3 SylixOS thread SylixOS is the multi-thread operating system, which can create multiple threads at the same time. The specific maximum number of threads depends on the size of the system ① memory and related configuration when compilation the SylixOS operating system . 6.3.1 Thread creation 1. Thread attribute creation Each SylixOS thread has its own attribute, including priority of the thread, stack information, thread parameters and so on. #include ULONG Lw_ThreadAttr_Build(PLW_CLASS_THREADATTR pthreadattr, size_t stStackByteSize, UINT8 ucPriority, ULONG ulOption, PVOID pvArg); Manual OS. SpaceChain 157 Application Development Manual SpaceChain OS Prototype analysis of Function Lw_ThreadAttr_Build: For success of the function, return ERROR_NONE. For failure, return the error number; Output parameter pthreadattr returns the generated attribute block; Each thread attribute block consists of the structure LW_CLASS_THREADATTR. The structure members are as follows: typedef struct { PLW_STACK THREADATTR_pstkLowAddr; /* Stack low memory start size_t THREADATTR_stGuardSize; /* Stack alert area size size_t THREADATTR_stStackByteSize; /* Total stack size (bytes)*/ UINT8 THREADATTR_ucPriority; /* Thread priority */ ULONG THREADATTR_ulOption; /* Task options */ PVOID THREADATTR_pvArg; /* Thread parameters */ PVOID THREADATTR_pvExt; /* Extended data segment address pointer */ */ */ } LW_CLASS_THREADATTR; For users of SylixOS application development, it is only required to care about the bold part, and the operating system will set by default for other members. Parameter stStackByteSize is the stack size (byte); Parameter ucPriority is the thread priority; In order to use thread priority reasonably, SylixOS sets some common priority values (the smaller the value, the higher the priority of the thread, and the system has priority ① scheduling), as follows . Table 6.2 Thread priority macro (part) Macro name Value LW_PRIO_HIGHEST 0 LW_PRIO_LOWEST 255 LW_PRIO_EXTREME LW_PRIO_HIGHEST LW_PRIO_CRITICAL 50 LW_PRIO_REALTIME 100 LW_PRIO_HIGH 150 LW_PRIO_NORMAL 200 LW_PRIO_LOW 250 LW_PRIO_IDLE LW_PRIO_LOWEST Manual OS. SpaceChain 158 Application Development Manual SpaceChain OS In application development, the task priority shall be between LW_PRIO_HIGH and LW_PRIO_LOW generally, so as not to influence the system kernel threads as much as possible. Parameter ulOption is the thread option; Parameter pvArg is the thread parameter; Table 6.3 Thread option Macro name Explanation LW_OPTION_THREAD_STK_CHK Inspect the thread stack during running LW_OPTION_THREAD_STK_CLR Zero the data during thread creation LW_OPTION_THREAD_USED_FP Save the floating-point arithmetic unit LW_OPTION_THREAD_SUSPEND Create thread rear blockage LW_OPTION_THREAD_INIT Initialize the thread LW_OPTION_THREAD_SAFE The thread created is the safe mode LW_OPTION_THREAD_DETACHED The thread shall be free of merger LW_OPTION_THREAD_UNSELECT The thread does not use the select function LW_OPTION_THREAD_NO_MONITOR The kernel tracker does not work on the thread The task cannot be occupied (not supported at LW_OPTION_THREAD_ UNPREEMPTIVE present) LW_OPTION_THREAD_SCOPE_PROCESS Competition in the progress (not supported at present) Note: SylixOS provides a quick function to get the default attribute block of the system, LwStud _ ThreadAttrStup _ GetDefault, and the return value of the function is the thread attribute block. For the attribute block, the default setting size of the thread stack is 4K, the priority is LW_PRIO_NORMAL, and the option is LW_OPTION_THREAD_STK_CHK. The reader can appropriately modify the returned attribute block, and modify the thread option (value assignment between options in the form of "or") and parameter usually. SylixOS provides the following set of functions to modify the thread's attribute blocks: #include ULONG Lw_ThreadAttr_SetGuardSize(PLW_CLASS_THREADATTR size_t pthreadattr, stGuardSize); ULONG Lw_ThreadAttr_SetStackSize(PLW_CLASS_THREADATTR pthreadattr, size_t stStackByteSize); ULONG Lw_ThreadAttr_SetArg(PLW_CLASS_THREADATTR pthreadattr, PVOID pvArg); The Lw_ThreadAttr_SetGuardSize function modifies the size of the stack alerting area of the thread attribute block, and the parameter stGuardSize appoints the size of the new stack alerting area. The Lw_ThreadAttr_SetStackSize function modifies the stack Manual OS. SpaceChain 159 Application Development Manual SpaceChain OS size of the thread attribute block, and the parameter stStackByteSize appoints the new stack size. The Lw_ThreadAttr_SetArg function can set the thread's startup parameter pvArg. 2. Thread stack Each thread has its own stack area. These areas are used for thread's function call, distribution of automatic variable, function return value and so on. Each thread control block saves the initial position, terminal position and stack warning point (for stack overflow check) of the stack area. The thread is the basic unit of SylixOS scheduling. When task scheduling occurs, the thread stack area will save the thread's current environment (for recovery in the context). Therefore, setting of the thread stack must be reasonable. If it is too big, waste of memory space will be caused. If it is too small, stack overflow will be caused. All threads in SylixOS run in the same address space. For real-time requirements, there is no address protection mechanism between threads. Therefore, stack overflow will cause unpredictable errors. There is no formula applied mechanically for setting of the stack size. Generally, one can set a large value according to experience, store space to replace reliability, and use the ss command to view usage of each task stack in the shell environment. 3. Thread creation #include LW_HANDLE Lw_Thread_Create(CPCHAR pcName, PTHREAD_START_ROUTINE PLW_CLASS_THREADATTR LW_OBJECT_ID pfuncThread, pthreadattr, *pulId); Prototype analysis of Function Lw_Thread_Create: For success of the function, return a thread ID created successfully (LW_HANDLE type). For failure, return the error number; Parameter pcName is the thread name; The parameter pfuncThread is the thread entry function, i.e., the starting address of the thread code segment; Parameter pthreadattr is the attribute block pointer of the thread (when NULL, the default property block will be used); The parameter pulId is the pointer of the thread ID, and the content is the same with the return value. It can be NULL. The function can create an SylixOS thread. Let's take a look at how SylixOS creates a thread via the following example. Program List 6.1 Thread creation instance Manual OS. SpaceChain 160 Application Development Manual SpaceChain OS #include PVOID tTest (PVOID pvArg) { while (1) { printf("thread running...\n"); sleep(1); } } int main (int argc, char *argv[]) { LW_CLASS_THREADATTR threadattr; LW_HANDLE hThreadId; Lw_ThreadAttr_Build(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); hThreadId = Lw_Thread_Create("t_test", tTest, &threadattr, LW_NULL); if (hThreadId == LW_OBJECT_HANDLE_INVALID) { return (PX_ERROR); } return (ERROR_NONE); } Run the program under the SylixOS Shell: #./thread_test thread running... thread running... #ts thread show >> NAME TID PID PRI STAT ERRNO DELAY PAGEFAILS FPU CPU ---------------- ------- ----- --- ---- ------- ---------- --------- --- --…… thread_test 4010033 10 200 SEM 0 0 1 0 t_test 4010034 10 200 SLP 71 88 0 0 …… Output results of the program are printed every 1 second as expected. From output results of the ts command, it can be seen that the "t_test" thread has been created (ID: 4010034 priority: 200), indicating that our thread is created successfully. Manual OS. SpaceChain 161 Application Development Manual SpaceChain OS This program uses two functions above mentioned and creation of thread attributes, and distributes 4 * LW_CFG_KB_SIZE (LW_CFG_KB_SIZE is the built-in macro of SylixOS system, and the value is 1024) stack size, thread priority of LW_PRIO_NORMAL and thread option of LW_OPTION_THREAD_STK_CHK, and there is no thread parameters. The name of the created thread is "t_test". This thread does not do any substantial thing, but is only a simple print. However, it is enough to explain the process and method of creating the SylixOS thread. 4. Thread initialization #include LW_HANDLE Lw_Thread_Init(CPCHAR pcName, PTHREAD_START_ROUTINE pfuncThread, PLW_CLASS_THREADATTR LW_OBJECT_ID pthreadattr, *pulId); ULONG Lw_Thread_Start(LW_OBJECT_HANDLE ulId); Prototype analysis of Function Lw_Thread_Init: For success of the function, return the threadID. For failure, return the error number; Parameter pcName is the thread name; Parameter pfuncThread is the entry function of the thread; Parameter pthreadattr is the thread attribute (when NULL, the default property block will be used); Parameter pulld is the ID pointer, and can be NULL; Prototype analysis of Function Lw_Thread_Start: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the thread ID; The Lw_Thread_Init function will create a thread like the Lw_Thread_Create function. However, there is an essential difference between both functions, i.e., the thread created by the Lw_Thread_Init function is only at an initial state. The thread is not ready. The scheduler will not allocate CPU use right to the thread. Only when Lw_Thread_Start function is called, the thread can be at the ready state, and can be scheduled by the scheduler. The following program shows how to use the thread initialize function. Program List 6.2 Thread initialization instance Manual OS. SpaceChain 162 Application Development Manual SpaceChain OS #include PVOID tTest (PVOID pvArg) { while (1) { printf("Thread running...\n"); sleep(1); } return (LW_NULL); } int main (int argc, char *argv[]) { LW_CLASS_THREADATTR threadattr; LW_HANDLE hThreadId; INT iRet; Lw_ThreadAttr_Build(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); hThreadId = Lw_Thread_Init("t_test", tTest, &threadattr, LW_NULL); if (hThreadId == LW_OBJECT_HANDLE_INVALID) { return (PX_ERROR); } iRet = Lw_Thread_Start(hThreadId); if (iRet) { return (PX_ERROR); } return (ERROR_NONE); } Run the program under the SylixOS Shell: #./thread_init Thread running... Thread running... #ts thread show >> NAME TID PID PRI STAT ERRNO DELAY PAGEFAILS FPU CPU ---------------- ------- ----- --- ---- ------- ---------- --------- --- --…… Manual OS. SpaceChain 163 Application Development Manual SpaceChain OS thread_init 4010014 1 200 SEM 0 0 1 0 t_test 4010015 1 200 SLP 71 68 0 0 …… Output results of the program are printed every 1 second as expected. From output results of the ts command, it can be seen that the "t_test" thread has been created (ID: 4010015 priority: 200), indicating that our thread is created successfully. Comparing with Program List 6.1, we find that operation results of the two programs are the same, and properties of the thread created are also the same. Therefore, from the perspective of running behavior, effect of Program List 6.2 is the same with that of Program List 6.1. We will use the method in Program List 6.2 to actively control our threads to enter the ready state, and the method is very useful for some situations. Note: it might also be noted that all created functions introduced above cannot be called in the interrupt context. 6.3.2 Thread control 1. Thread suspend and resume The thread suspend is to make the specified thread at the non-ready state. The thread at the suspend state is ignored by the scheduler and is relatively "quiet" for debugging until the suspend is released. #include ULONG Lw_Thread_Suspend(LW_OBJECT_HANDLE ulId); ULONG Lw_Thread_Resume(LW_OBJECT_HANDLE ulId); Prototype analysis of Function Lw_Thread_Suspend: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the thread ID; Prototype analysis of Function Lw_Thread_Resume: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the thread ID; Suspend is a binary memoryless state of a task. It will not check whether the task was suspended or released before prior to suspend, so: Effects of repeated suspend of a certain task are the same with those of one-time suspend; Manual OS. SpaceChain 164 Application Development Manual SpaceChain OS If suspend and release suspend are completed by different tasks, one must ensure that they are conducted in the correct order; Let's see the following scenario, thread T1 sends a message to a certain thread T2 before suspend, and T2 releases the suspend state of T1 thread after receiving this message. …… T1:Send message to T2 thread T1:调用 Lw_Thread_Suspend 挂起自己 …… T2:Received T1 message T2:Call Lw_Thread_Resume to release T1 pending state …… We will find that there is a competitive risk after careful analysis of above scenario. T1 just sends the message. At this time, T2 with high priority receives the message to release thread suspend. Release has no effect at the moment, and T1 starts to suspend, thus entering the unlimited suspend state. We shall be careful about this situation. In addition, we must also pay attention to avoid deadlock of the system at suspend, which usually requires suspend of the thread after obtaining a certain system resource with mutex access. One shall especially avoid such situation at asynchronous suspend. To avoid the competition risk, we can attach the suspend state to the delayed state and the blocked state, causing the thread to enter the "delayed suspend" or "blocked suspend" state. The additional suspend state does not influence the original delay and block of the thread, which means that the suspend state and delayed or blocked state can coexist. During suspension, the delay thread still calculates the delay. If the delay expires, the task enters the suspend-only state. If the waiting condition is satisfied during suspension, the blocked thread will be unblocked, and enter the suspend-only state. If the thread is de-suspended when the delay expires or when the waiting condition occurs, the thread will returns to the original delay / block state. Note: Lw_Thread_Suspend function and Lw_Thread_Resume function can be called during interrupt, and thread suspend is unconditional. To check the thread state at the moment in SylixOS, one can view “STAT” column via Shell command ts. As shown below, thread “t_test” is at the “SLP” state. # ts thread show >> NAME TID PID PRI STAT ERRNO DELAY PAGEFAILS FPU CPU ---------------- ------- ----- --- ---- ------- ---------- --------- --- --Manual OS. SpaceChain 165 Application Development Manual SpaceChain OS …… …… t_test SLP …… …… 2. Thread delay The thread delay is to let the thread sleep, so that the scheduler can schedule other threads, and resume operation after thread sleep. #include VOID Lw_Time_Sleep(ULONG ulTick); VOID Lw_Time_SSleep(ULONG ulSeconds); VOID Lw_Time_MSleep(ULONG ulMSeconds); ① Function prototype analysis : ② Lw_Time_Sleep delay unit is Tick ; Lw_Time_SSleep delay unit is s Lw_Time_MSleep delay unit is ms; The minimum time delay which can be obtained by using Lw_Time_Sleep series function in SylixOS is 1Tick, If you specify ulTick to be 0, SylixOS will not delay, it will not affect the current thread's behavior. If you want a shorter time delay (for example: 1ms). The nanosleep function can be called. #include int nanosleep(const struct timespec *rqtp, strutc timespec *rmtp); Prototype analysis of Function nanosleep: For success of the function, return 0. For failure, return -1 and set the error number; Parameter rqtp is the sleep time; Parameter rmtp saves the remaining time; The function belongs to POSIX standard. The required wait time is represented by the pointer rqtp of the structure timespec. The structure represents time in seconds and nanoseconds. Unlike the above three functions, if rmtp is not NULL, the remaining time is returned via it. It might also be noted that this function can be awakened by the signal. If so, the error number errno is EINTR. For a detailed explanation of the signal, see Chapter 10 Signal System. Note: If no signal is interrupted during nanosleep sleep, rmtp always returns 0. Otherwise, it will return the time interval from the signal interrupt time point to delay completion. 3. Thread mutex Manual OS. SpaceChain 166 Application Development Manual SpaceChain OS Mutex access is a classical theoretical problem in the operating system. It is used to implement consistent access to shared resources. SylixOS implements different functions to provide multiple mutex mechanisms. Thread lock: Lw_Thread_Lock. Interrupt lock: Lw_Interrupt_Lock. Semaphore: Lw_Semaphore_Wait. When the time for shared resource accessed is very long, semaphore method is very effective. For example, when a thread wants to apply for a shared area locked by semaphore, then this thread will be blocked at the moment, so as to save CPU cycle. For detailed use of semaphore, see Chapter 7 Inter-thread Communication. Using the lock interrupt method will increase delay in interrupt response of the system. For general threads, lock interrupt is not a good method, and it is not suggested to adopt lock interrupt for general application development SylixOS. Let's take a look at the thread lock. In SylixOS, we can call the thread lock function to disable the scheduler. When the thread calls the thread lock function, the scheduler temporarily fails. Even if a high-priority thread is ready, the thread will not be called out of the processor until the thread calls the thread release function. This mutex scheme introduces a priority delay (scheduling delay) for the system; the high priority thread which requires high real-time performance must wait until the thread is unlocked before being scheduled. Therefore, superior real-time performance of SylixOS is sacrificed to some extent. Therefore, this method is also not recommended. Based on multiple considerations, the semaphore method is generally adopted for SylixOS application development to achieve mutex access. Note: the thread lock function only locks the current CPU's schedule, but does not affect the other CPU's schedule. The blocking function cannot be used during thread locking. Thread locking does not lock interrupt. When interrupt occurs, the interrupt service routine is called as usual. 6.3.3 End of thread End of thread means the end of the thread life cycle. End of thread includes 3 cases of operation end and exit, thread exit and thread delete. 1. Thread delete Thread delete is to return the thread resources to the operating system, and the deleted thread can no longer be scheduled. #include ULONG Lw_Thread_Delete(LW_OBJECT_HANDLE *pulId, PVOID pvRetVal); ULONG Lw_Thread_ForceDelete(LW_OBJECT_HANDLE *pulId, PVOID pvRetVal); Manual OS. SpaceChain 167 Application Development Manual SpaceChain OS Function prototype analysis: For success of two functions, return ERROR_NONE. For failure, return the error number; Parameter pulId is the handle of the thread to be deleted; Parameter pvRetVal is the value returned to the JOIN function; Calling above two functions can make the thread end, and release thread resources. Because SylixOS supports the process, the delete thread can only be a thread in the same process, and the main thread can only be deleted by itself. Active deletion of other executing threads may cause that unlocked resources cannot be released or atomic operations are interrupted. Therefore, it is not recommended to directly use thread delete function for call in SylixOS and any other operating system unless security is guaranteed. During application design, "request" delete mode shall be considered. When the thread finds that it has nothing to do or is requested to delete, the thread deletes itself (thread exit). 2. Thread exit #include ULONG Lw_Thread_Exit(PVOID pvRetVal); Prototype analysis of Function Lw_Thread_Exit: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter pvRetVal is the value returned to the JOIN function. 3. Thread cancel #include ULONG Lw_Thread_Cancel(LW_OBJECT_HANDLE *pulId); Prototype analysis of Function Lw_Thread_Cancel: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter pulId is the canceled thread ID. The method to cancel the thread is to send the Cancel signal to the target thread, but how to deal with the Cancel signal is determined by the target thread itself. It might also be noted that the thread cancel is a complicated process, and consistency of the resource shall be considered. For detailed information of thread cancel, see 6.4.4 Thread Cancel. 6.3.4 Multi-thread security Manual OS. SpaceChain 168 Application Development Manual SpaceChain OS The inherent advantages of the multi-thread model make SMP multi-core processor to realize true concurrent execution. However, multi-thread brings convenience and introduces some problems, such as the mutex access of global resources. For safe access to these resources, the competition conditions and deadlock shall be considered during program design. Multi-thread security is a mechanism where a resource can be safely used by multiple threads in the case of concurrent execution of multiple threads. Multi-thread security includes the protection and reentrancy of critical section of code. The critical section of code refers to code which is indivisible when processed. Once execution of this part of the code is started, no interruption is allowed. To ensure that execution of the critical section code is not interrupted, interrupt must be closed before entering the critical section, and interrupt must be immediately opened after the critical section code is executed. ① "Reentrability" means that the function can be called by multiple threads without destroying the data. Regardless of how many threads are used, the reentrant function always gets the expected results for each thread. The function which allows reentry is called as "reentrant function". Otherwise, it is the "non-reentrant function". The problem of code reentrability is caused by parallel operation of multiple threads. Therefore, code reentrability is called as "multi-thread security". In SylixOS, occasions which may cause code reentry include: multi-thread scheduling, interrupt service program scheduling and signal processing function scheduling. The following two functions are considered: char *ctime (const time_t *time) { static char cTimeBuffer[sizeof(ASCBUF)]; …… buffer /* Write a string to the /* Write a string to the */ return (cTimeBuffer); } char *ctime_r (const time_t *time, char *buffer) { …… buffer */ return (buffer); } Two functions will convert the time represented by Parameter time to a character string for return. The ctime function defines the character string as a local static buffer zone. Considering that multiple threads call ctime "simultaneously", call of different threads will cause the ctime function to modify the same character string buffer area apparently. Therefore, the ctime function is the non-reentrant function. In contrast, the Manual OS. SpaceChain 169 Application Development Manual SpaceChain OS character string buffer area of the ctime_r function is allocated by the caller, and different threads run to modify their respective buffer areas. Therefore, it is safe for multi-thread call. Some functions will inevitably use global or static variables, such as the malloc function. In order to protect global variables from being destroyed, mutex is an option. In addition to the method introduced above to implement function reentrancy, SylixOS also provides a "thread private data" mechanism to implement function reentry. Such protection sacrifices real-time performance of the system, and is only effective for single CPU system. It is not recommended to adopt the method in SylixOS unless necessary. Thread private data is an unsigned long value of the thread context record (which can be seen as a pointer to a global variable) and a temporary variable which saves the global variable value (used to restore the value of the global variable in the thread context). Every time the thread is called into the processor, the system automatically loads the value of the global variable from the thread context according to the pointer. Correspondingly, when the task is called out of the processor, the system automatically saves the value of the global variable to the thread context according to the pointer. The following group of functions implement operation of thread private data. #include ULONG Lw_Thread_VarAdd(LW_HANDLE ulId, ULONG *pulAddr); ULONG Lw_Thread_VarDelete(LW_HANDLE ulId, ULONG *pulAddr); ULONG Lw_Thread_VarSet(LW_HANDLE ulId, ULONG *pulAddr, ULONG ulValue); ULONG Lw_Thread_VarGet(LW_HANDLE ulId, ULONG *pulAddr); ULONG Lw_Thread_VarInfo(LW_HANDLE ulId, ULONG *pulAddr[], INT iMaxCounter); Function prototype analysis: Parameter ulId is the thread handle; Parameter pulAddr is the private data address; Parameter ulValue is the set value; Parameter iMaxCounter is the size of address list. Calling Lw_Thread_VarAdd function can declare a thread private data. For success of the function, return ERROR_NONE. For failure, return the error number; Calling Lw_Thread_Delete function can declare a thread private data declared. For success of the function, return ERROR_NONE. For failure, return the error number; Calling Lw_Thread_VarSet function can set the value of the thread private data; calling Lw_Thread_VarGet function can get the value of the thread private data. The function returns the value of private data or 0; Calling Lw_Thread_VarInfo function will get information of the thread private data. The function returns the number of private data. The process for the thread private data to implement reentrant is as follows: Manual OS. SpaceChain 170 Application Development Manual SpaceChain OS INT _G_iGlobal; VOID func (VOID) { …… if (Lw_Thread_VarAdd(threadId, (ULONG *)&_G_iGlobal) != 0) { …… /* Error handling */ } _G_iGlobal++; processing /* Global variable */ …… } Obviously, for each new thread generated based on the func function, the global variable iGlobal will be added. After the thread private data is declared, the system will save and load the duplicate of global variable iGlobal owned by each thread when the thread context switches, so that modification of global variable iGlobal by different threads will not affect each other, as shown in Figure 6.2. Private data Private data Figure 6.2 Thread private data It might also be noted that the thread private data must be declared before any value assignment. The Lw_Thread_VarSet function and the Lw_Thread_VarGet function are usually used to allow multiple cooperative threads participating in a job to obtain the thread private data values of other threads. It has the function of inter-thread communication. Program List 6.3 shows an instance of implementation of inter-thread communication by the private data. Program List 6.3 Private data implements inter-thread communication #include #include INT _G_iGlobal = 0; PVOID tTest (PVOID pvArg) Manual OS. SpaceChain 171 Application Development Manual SpaceChain OS { while (1) { fprintf(stdout, "tTest global value: %d\n", _G_iGlobal); sleep(1); } return (LW_NULL); } int main (int argc, char *argv[]) { LW_HANDLE hId; hId = Lw_Thread_Create("t_test", tTest, NULL, NULL); if (hId == LW_HANDLE_INVALID) { return (PX_ERROR); } if (Lw_Thread_VarAdd(hId, (ULONG *)&_G_iGlobal) != 0){ return (PX_ERROR); } while (1) { Lw_Thread_VarSet(hId, (ULONG *)&_G_iGlobal, 55); sleep(1); } Lw_Thread_Join(hId, NULL); return (ERROR_NONE); } Run the program under the SylixOS Shell, and the results are as follows: # ./var_test tTest global value: 55 tTest global value: 55 Each thread private data added will cause thread context switch, increasing unsigned long memory copy overhead, which is the flaw of thread private data. Compared to the previous method based on local dynamic variables, there are also limitations. When it is required to span the function scope, the global variables must be used. However, the commonly used method of "global variables + mutex" is more complicated than the method of thread private data, and mutex also requires a certain running overhead. Manual OS. SpaceChain 172 Application Development Manual SpaceChain OS 6.4 POSIX thread 6.4.1 Thread attribute All POSIX thread attributes are represented via an attribute object defined as the structure pthread_attr_t. POSIX defines a series of function settings and read thread attribute values. Therefore, application does not need to know details of pthread_attr_t definition. Management of thread attributes shall comply with the same mode usually: Each object is associated with the attribute object of its own type (thread and thread properties, mutex amount and mutex attribute and so on, and one attribute object contains multiple attributes (pthread_attr_t). Encapsulation of the attribute object makes application easier to transplant; The attribute object has an initialization function which sets the attribute as the default value; There is a destroy function corresponding to initialization for deinitialization; Each attribute object has a function which gets the attribute value from the attribute object. For success of the function, return 0. For failure, return the error number. Therefore, it can be returned to the caller via storing the attribute value in the memory unit appointed by a certain parameter of the function; Each attribute object has a function which sets the attribute value, so that the attribute value is transferred via the parameter. Calling the pthread_attr_init function can initialize a thread attribute object, and calling the pthread_attr_destroy function can destroy a thread attribute object. #include int pthread_attr_init(pthread_attr_t *pattr); int pthread_attr_destroy(pthread_attr_t *pattr); Prototype analysis of Function pthread_attr_init: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer requiring initialization; Prototype analysis of Function pthread_attr_destroy: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer requiring destroy. Manual OS. SpaceChain 173 Application Development Manual SpaceChain OS Initialization of the attribute object assigns default values to various attributes, and POSIX does not restrict the default value. After the attribute object is initialized, the program usually needs to set the suitable value for single attribute. Destroy attribute is an inverse process of attribute initialization, which usually sets the attribute value as the invalid value. If some system resources is dynamically distributed during initialization, these resources will be released during destroy. Only the thread option value is set as the invalid value in SylixOS. The attribute object cannot be used for thread creation after destroy unless reinitialization. The pthread_getattr_np function can be called to get a attribute object of a appointed thread. The function is not a part of the POSIX standard, but a kind of extension of the Linux and SylixOS systems. SylixOS also supports the FreeBSD extension function pthread_attr_get_np. #include int pthread_attr_get_np(pthread_t thread, pthread_attr_t *pattr); int pthread_getattr_np(pthread_t thread, pthread_attr_t *pattr); Prototype analysis of Function pthread_attr_get_np: For success of the function, return 0. For failure, return the error number; Parameter thread is the thread handle; The output parameter pattr is the attribute object of the return thread. The pthread_attr_get_np and pthread_getattr_np functions have the same functions and parameter types, and the pthread_getattr_np function calls the pthread_attr_get_np function on underlying implementation. Calling the following functions can get or set the name of the POSIX thread. #include int pthread_attr_setname(pthread_attr_t *pattr, const char *pcName); int pthread_attr_getname(const pthread_attr_t *pattr, char **ppcName); Prototype analysis of Function pthread_attr_setname: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; Parameter pcName is the thread name to be set. Prototype analysis of Function pthread_attr_getname: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; The output parameter ppcName is the name of the return thread. Manual OS. SpaceChain 174 Application Development Manual SpaceChain OS Calling the pthread_attr_setname function can set the name of the thread, and calling the pthread_attr_getname function can get the name of the thread. The default thread name for initializing the thread attribute object in SylixOS is "pthread". Thread attributes specified by POSIX are as follows. 1. Stack size #include int pthread_attr_setstacksize(pthread_attr_t *pattr, size_t stSize); int pthread_attr_getstacksize(const pthread_attr_t *pattr, size_t *pstSize); Prototype analysis of Function pthread_attr_setstacksize: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; Parameter stSize is the stack size. Prototype analysis of Function pthread_attr_ getstacksize: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; The output parameter pstSize returns the stack size. Calling the pthread_attr_setstacksize function can set the size of the thread stack, and calling the pthread_attr_getstacksize function will get the stack size of the appointed thread. The default stack value for initializing the thread attribute object in SylixOS is 0, which means that the stack size will inherit that of the creator. ① Note: The stack size set shall not be less than 128 bytes . Otherwise, EINVAL error value will be returned. 2. Stack address #include int pthread_attr_setstackaddr(pthread_attr_t *pattr, void *pvStackAddr); int pthread_attr_getstackaddr(const pthread_attr_t *pattr, void **ppvStackAddr); Prototype analysis of Function pthread_attr_setstackaddr: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; Parameter pvStackAddr is the stack address. Prototype analysis of Function pthread_attr_getstackaddr: Manual OS. SpaceChain 175 Application Development Manual SpaceChain OS For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute object pointer; The output parameter ppvStackAddr is the the return address of the stack. Calling the pthread_attr_setstackaddr function can set the new starting address of the stack, and calling the pthread_attr_getstackaddr function will get the starting address of the stack. The default stack address for initializing the thread attribute object in SylixOS is LW_NULL, which means that the system will automatically distribute the stack space. #include int pthread_attr_setstack(pthread_attr_t void *pattr, *pvStackAddr, size_t stSize); int pthread_attr_getstack(const pthread_attr_t void *pattr, **ppvStackAddr, size_t *pstSize); Prototype analysis of Function pthread_attr_setstack: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Parameter pvStackAddr is the stack address; Parameter stSize is the stack size. Prototype analysis of Function pthread_attr_getstack: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; The output parameter ppvStackAddr is the the return address of the stack; The output parameter pstSize is the size of the return stack. Calling the pthread_attr_setstack function can set the starting address and the size of the stack simultaneously, and calling the pthread_attr_getstack function will get the starting address and the size of the stack simultaneously. 3. Guard area of the thread stack #include int pthread_attr_setguardsize(pthread_attr_t *pattr, size_t stGuard); int pthread_attr_getguardsize(pthread_attr_t *pattr, size_t *pstGuard); Prototype analysis of Function pthread_attr_setguradsize: For success of the function, return 0. For failure, return the error number; Manual OS. SpaceChain 176 Application Development Manual SpaceChain OS Parameter pattr is the thread attribute pointer; Parameter stGuard is the size of the stack guard area. Prototype analysis of Function pthread_attr_getguradsize: For success of the function, return 0. For failure, return the error number; Output parameter pstGuard is the size of the return stack guard area. Calling the pthread_attr_setguradsize function can set the size of the stack guard area, and calling the pthread_attr_getguradsize function will get the size of the stack guard area. The size of the default stack guard area for initializing the thread attribute object in SylixOS is LW_CFG_THREAD_DEFAULT_GUARD_SIZE. 4. Detach status #include int pthread_attr_setdetachstate(pthread_attr_t *pattr, int iDetachState); int pthread_attr_getdetachstate(const pthread_attr_t *pattr, int *piDetachState); int pthread_detach(pthread_t thread); Prototype analysis of Function pthread_attr_setdetachstate: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Parameter iDetachState is the detach state value. Prototype analysis of Function pthread_attr_getdetachstate: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; The output parameter piDetachState is the return detach state value. Prototype analysis of Function pthread_detach: For success of the function, return 0. For failure, return the error number; Parameter thread is the thread handle to be detached. The thread detach states is divided into the join state and the separate state. At the join state, the thread creates a new thread to block itself until the new thread exits. Calling the pthread_attr_setdetachstate function can set the detach state of the thread attribute object, and calling the pthread_attr_getname function will get the detach state of the thread attribute object. The default detach state for initializing the thread attribute object in SylixOS is the join state (PTHREAD_CREATE_JOINABLE). Manual OS. SpaceChain 177 Application Development Manual SpaceChain OS If the thread is created in the join state, the thread can call the pthread_detach function to make it into a separate state. Otherwise, it is infeasible. 5. Inherit scheduling #include int pthread_attr_setinheritsched(pthread_attr_t *pattr, int iInherit); int pthread_attr_getinheritsched(const pthread_attr_t *pattr, int *piInherit); Prototype analysis of Function pthread_attr_setinheritsched: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Parameter iInherit is the inherit attribute. Prototype analysis of Function pthread_attr_getinheritsched: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Inherit scheduling determines whether to inherit the scheduling parameter from the parent thread (PTHREAD_INHERIT_SCHED) or be appointed explicitly (PTHREAD_EXPLICIT_SCHED) when creating the thread. Calling the pthread_attr_setinheritsched function can set inheritance of the thread attribute object, and calling the pthread_attr_getinheritsched function will get the scheduling strategy of the thread attribute object (inheritance). The default scheduling strategy for initializing the thread attribute object in is explicitly appointed. 6. Dispatching strategy #include int pthread_attr_setschedpolicy(pthread_attr_t *pattr, int iPolicy); int pthread_attr_getschedpolicy(const pthread_attr_t *pattr, int *piPolicy); Prototype analysis of Function pthread_attr_setschedpolicy: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Parameter iPolicy is the scheduling strategy. Prototype analysis of Function pthread_attr_getschedpolicy: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; Manual OS. SpaceChain 178 Application Development Manual SpaceChain OS The output parameter piPolicy returns the scheduling strategy. The item appoints the scheduling strategy to create the new thread, including SCHED_FIFO, SCHED_RR and SCHED_OTHER (SCHED_OTHER is the custom scheduling strategy specified by POSIX, and SCHED_OTHER is equal to SCHED_RR in SylixOS.). For detailed contents of these two scheduling strategies, see Section 6.6. Calling the pthread_attr_setschedpolicy function can set the scheduling strategy of the thread attribute object, and calling the pthread_attr_getschedpolicy function will get the scheduling strategy of the thread attribute object. The default scheduling strategy for initializing the thread attribute object in SylixOS is SCHED_RR. 7. Scheduling parameter #include int pthread_attr_setschedparam(pthread_attr_t const struct sched_param int pthread_attr_getschedparam(const pthread_attr_t struct sched_param *pattr, *pschedparam); *pattr, *pschedparam); Prototype analysis of Function pthread_attr_setschedparam: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; The output paramete pschedparam is the scheduling parameter; Prototype analysis of Function pthread_attr_getschedparam: For success of the function, return 0. For failure, return the error number; Parameter pattr is the thread attribute pointer; The output parameter pschedparam returns the scheduling parameter. After the thread is created successfully, the scheduling parameter is allowed to be modified dynamically. For detailed contents of the scheduling parameter, see Section 6.7. Calling the pthread_attr_setschedparam function can set the scheduling parameter of the thread attribute object, and calling the pthread_attr_getschedpolicy function will get the scheduling parameter of the thread attribute object. The default scheduling parameter for initializing the thread attribute object in SylixOS only sets the thread priority as LW_PRIO_NORMAL. 6.4.2 Thread creation #include int pthread_create(pthread_t *pthread, Manual OS. SpaceChain 179 Application Development Manual SpaceChain OS const pthread_attr_t void *pattr, *(*start_routine)(void *), void *arg); Prototype analysis of Function pthread_create: For success of the function, return 0. For failure, return the error number; Output parameter pthread is the return thread handle; Parameter pattr is the thread attribute object pointer; Parameter start_routine is the thread function; Parameter arg is the entry function parameter. Calling the pthread_create function can create a POSIX thread, and the thread attribute object pattr can be created or dynamically set via the pthread_attr_* series of functions. If the pattr is NULL, the system will set a default thread attribute object. When the thread function appointed by the start_routine returns, the new thread ends. It might also be noted that the thread function start_routine has only one pointer parameter arg, which means that multiple parameters shall be packaged as a structure for transfer. The POSIX thread handle is defined as the pthread_t type variable. POSIXPOSIXPOSIXThe thread creator gets the created thread handle when the thread is created, the thread can get its own thread handle via calling the pthread_self function, and POSIX also defines the pthread_equal function to compare two threads for equality. #include pthread_t pthread_self(void); int pthread_equal(pthread_t thread1, pthread_t thread2); Prototype analysis of Function pthread_self: For success of the function, return the current thread handle. For failure, return 0. Prototype analysis of Function pthread_equal: The function returns comparative results; Parameter thread1 is the thread handle; Parameter thread2 is the thread handle; The following program shows how to creat the POSIX thread. The following program calls the function pthread_join to thread join, which will cause the main thread to wait for the child thread until exit (the specific usage of the pthread_join function is introduced in the next section). Program List 6.4 POSIX thread creation #include Manual OS. SpaceChain 180 Application Development Manual SpaceChain OS #include #include void *routine (void *arg) { fprintf(stdout, "pthread running...\n"); return (NULL); } int main (int argc, char *argv[]) { pthread_t tid; pthread_attr_t attr; int ret; ret = pthread_attr_init(&attr); if (ret != 0) { fprintf(stderr, "pthread attr init failed.\n"); return (-1); } ret = pthread_create(&tid, &attr, routine, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } pthread_join(tid, NULL); pthread_attr_destroy(&attr); return (0); } The above program calls the function pthread_attr_init to initialize the thread attribute object, and correspondingly calls the function pthread_attr_destroy to deinitialize the attribute object. Actually, we are accustomed to setting the second parameter of the pthread_create function as NULL during programming, so as to tell the operating system to set the thread attribute object as the default value. Run the program under the SylixOS Shell, and the results are as follows: # ./pthread_test pthread running... 6.4.3 Thread exit Manual OS. SpaceChain 181 Application Development Manual SpaceChain OS #include void pthread_exit(void *status); Prototype analysis of Function pthread_exit: The function has no return value; Parameter status is the thread exit status code. Thread end, i.e., the thread explicitly or implicitly calls the pthread_exit function.The status usually represents an integer, and can also point to a more complex data structure. The thread end code will be got by another thread which has joind with the thread. The single thread can exit in 3 ways: The thread can simply return from the thread entry function, and the return value is the thread exit code; Threads can be canceled by other threads in the same process (see 6.4.4 Thread cancel); The thread explicitly calls the pthread_exit function. The thread can wait for another thread to exit synchronously and get its exit code. It might also be noted that the synchronously waiting target thread must be at the join state, as shown in Figure 6.3. Figure 6.3 Thread synchronization wait #include int pthread_join(pthread_t thread, void **ppstatus); Prototype analysis of Function pthread_join: For success of the function, return 0. For failure, return the error number; Parameter thread is the thread handle requiring join; Output parameter ppstatus is the thread exit status. Calling the pthread_join function can merge the appointed thread. Calling thread will always block wait until it returns. After the thread returns, the pthread_join function will get the exit code of the thread via the ppstatus parameter. Manual OS. SpaceChain 182 Application Development Manual SpaceChain OS One can set the ppstatus as NULL if not interested in the return value of the thread. In this case, the thread calling the pthread_join function can wait for the appointed thread to terminate, but it does not get the termination status of the thread. The following program shows how to get the exit code of the thread via pthread_join. Program List 6.5 Get the exit code of the thread #include #include void *routine (void *arg) { fprintf(stdout, "thread 1 return.\n"); return ((void *)1); } void *routine1 (void *arg) { fprintf(stdout, "thread 2 exit.\n"); pthread_exit((void *)2); } int main (int argc, char *argv[]) { pthread_t tid, tid1; int ret; void *retval; ret = pthread_create(&tid, NULL, routine, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_join(tid, &retval); if (ret != 0) { fprintf(stderr, "pthread join thread 1 failed.\n"); return (-1); } fprintf(stdout, "thread 1 return code: %ld\n", (long)retval); ret = pthread_create(&tid1, NULL, routine1, NULL); if (ret != 0) { Manual OS. SpaceChain 183 Application Development Manual SpaceChain OS fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_join(tid1, &retval); if (ret != 0) { fprintf(stderr, "pthread join thread 2 failed.\n"); perror("pthread_join"); return (-1); } fprintf(stdout, "thread 2 exit code: %ld\n", (long)retval); return (0); } The above program uses two methods for thread exit: return thread and call the pthread_exit function to exit. Run the program under the SylixOS Shell, and the results are as follows: # ./join_test thread 1 return. thread 1 return code: 1 thread 2 exit. thread 2 exit code: 2 It can be seen from execution results that when a thread exits by calling the pthread_exit function or simply returns from the thread function, other threads in the process can get the exit code of the thread by calling the pthread_join function. As mentioned earlier, the untyped pointer parameters of the pthread_create function and the pthread_exit function can transfer a more complex structure. However, it shall be noted that the memory used by the structure must remain valid after the caller completes the call. For example, if the structure is allocated on the stack calling the thread, the memory contents of other threads may have changed when using the structure. For another example, the thread allocates a structure on its own stack and then transfers the pointer pointing to the structure to the pthread_exit function. When the thread calling the pthread_join function tries to use the structure, the stack may have been revoked, and the memory has been used for other purposes. 6.4.4 Thread cancel Canceling a thread shall guarantee that the thread can release any locked and allocated memory it holds, so as to maintain consistency in the whole the system. It has Manual OS. SpaceChain 184 Application Development Manual SpaceChain OS cert A simple thread cancel: the cancel thread calls a cancel thread function, and the canceled thread dies. In this case, the resources held by the canceled thread are not released. The cancel thread shall guarantee that the canceled one is at the safe cancellation state. In a system requiring high reliability, such guarantee is very difficult or cannot be realized. This cancel is called as unrestricted asynchronous cancel. Asynchronous cancel security is related with the asynchronous cancel, i.e., a piece of code can be canceled at any point during execution without causing inconsistency. The function satisfying the condition is called as the asynchronous cancel security function. Apparently, the asynchronous cancel security function does not involve use of mutex and so on. The POSIX standard requires that these functions are asynchronous cancel security functions: pthread_cancel, pthread_setcancelstate and pthread_setcanceltype. The POSIX standard defines a more secure thread cancel mechanism. A thread can sent the cancel request to other threads of the process in a reliable and controlled manner, and the target thread can suspend the request and enable the actual cancel action to take place later, called as the delay cancel. The target thread can also define the thread clearing function automatically called by the system after canceled. A concept related with the delay cancel is the cancel point. POSIX cancels the point function and a series of cancel control functions to realize the delay cancel via defining a cancelable state for a thread. The thread can call the pthread_cancel function to request cancellation of a thread. #include int pthread_cancel(pthread_t thread); Prototype analysis of Function pthread_cancel: For success of the function, return 0. For failure, return the error number; Parameter thread is the thread handle. The pthread_cancel function is asynchronous with the cancel action of the target thread. According to different settings of the target thread, the cancel request may be ignored, executed immediately or delayed. In order to clarify these actions, here we need to understand the cancel state, cancel type and cancel point concept of the thread. 1. Cancellation status The thread cancel state determines whether the appointed thread can be canceled. The cancel state is divided into allow cancel and prohibit cancel, as shown in Table 6.4. Setting a thread as prohibit cancel means that the thread can only return from itself or call the pthread_exit function to exit. #include Manual OS. SpaceChain 185 Application Development Manual SpaceChain OS int pthread_setcancelstate(int newstate, int *poldstate); Prototype analysis of Function pthread_setcancelstate: For success of the function, return 0. For failure, return the error number; The parameter newstate is the new state, as shown in Table 6.4; The output parameter poldstate returns to the previous state. Table 6.4 Cancel state Cancellation status Note LW_THREAD_CANCEL_ENABLE Allow cancel LW_THREAD_CANCEL_DISABLE Prohibit cancel Calling the pthread_setcancelstate function appoints newstate parameter value as LW_THREAD_CANCEL_ENABLE to allow cancel, and LW_THREAD_CANCEL_DISABLE to prohibit cancel. If the parameter poldstate is not NULL, it returns the previous cancel state. 2. Cancellation type The cancel type is the thread cancel mode, which is divided into asynchronous cancel and delay cancel, as shown in Table 6.5. #include int pthread_setcanceltype(int newtype, int *poldtype); Prototype analysis of Function pthread_setcancelstate: For success of the function, return 0. For failure, return non-0 value; Parameter newtype is the new type, as shown in Table 6.5; Output parameter poldtype returns the previous type. Table 6.5 Cancel type Cancellation type Note LW_THREAD_CANCEL_ASYNCHRONOUS Asynchronous cancel LW_THREAD_CANCEL_DEFERRED Delay cancel Calling the pthread_setcanceltype function can set the cancellation type. When the poldtype is non-NULL, it returns the previous cancel type. When the cancel state is set to prohibit, the cancel request for the thread will be Manual OS. SpaceChain 186 Application Development Manual SpaceChain OS ignored. When the cancel state is set to allow, if the cancel request is received, the system action is determined by the selected cancel type. Asynchronous cancel, the cancel request is executed immediately; Delay cancel, the cancellation request is suspended, and executed until running to the next cancel point. 3. Cancel point When the delay cancel mechanism is canceled, a thread defines the cancel point where it can be canceled. When the cancel request is received, the canceled thread exits when it reaches the cancel point or when a cancel point call is blocked. By the delay cancel, the program does not require prohibit/ allow cancel operation when entering the critical section. It can be canceled at the cancel point during the delay cancel, and the restriction may enable the cancel request to be suspended for any length of time. Therefore, if a certain call may enable the thread to be blocked or enter a certain process for a long term, POSIX requests that these calls belong to a cancel point, or call these calls as cancel point calls, so as to prevent the cancel request from falling into long-term wait. Functions owning the cancel point in SylixOS are as shown in Table 6.6. Table 6.6 Functions owning the cancel point Function name Function name Function name Lw_Thread_Join send open Lw_Thread_Start sendmsg close sleep aio_suspend read Lw_Thread_Cond_Wait mq_send pread system mq_timedsend write wait mq_reltimedsend_np pwrite waitid mq_receive readv waitpid mq_timedreceive writev wait3 mq_reltimedreceive_np lockf wait4 pthread_barrier_wait fsync reclaimchild sem_wait fdatasync accept4 sem_timedwait pselect connect sem_reltimedwait_np select recv tcdrain pause recvfrom fcntl sigsuspend recvmsg creat sigwait sigwaitinfo sigtimedwait msgrcv msgsnd pthread_join pthread_testcancel Manual OS. SpaceChain 187 Application Development Manual SpaceChain OS The following program is an example of thread delay cancel. The program creates the thread thread0, and sets the cancel type as the delay cancel in the thread. The sleep function is a function which owns a cancel point. Therefore, the program will check whether the thread has the cancel request when running to the sleep function. When the cancel request is checked, the thread will be canceled at the next cancel point. Program List 6.6 Thread delay cancel #include #include void *thread0 (void *arg) { int oldstate; int oldtype; pthread_setcancelstate(LW_THREAD_CANCEL_ENABLE, &oldstate); pthread_setcanceltype(LW_THREAD_CANCEL_DEFERRED, &oldtype); while (1) { fprintf(stdout, "thread0 running...\n"); sleep(1); } return (NULL); } int main (int argc, char *argv[]) { pthread_t tid; int ret; ret = pthread_create(&tid, NULL, thread0, NULL); if (ret != 0) { return (-1); } sleep(3); pthread_cancel(tid); pthread_join(tid, NULL); fprintf(stdout, "thread0 cancel.\n"); return (0); } Run the program under the SylixOS Shell, and the cancel situation of the thread can Manual OS. SpaceChain 188 Application Development Manual SpaceChain OS be seen from the results. # ./cancel_test thread0 running... thread0 running... thread0 running... thread0 cancel. The functions shown in Table 6.6 are realized via directly or indirectly calling the pthread_testcancel function. Therefore, the target thread can also call the pthread_testcancel function to check the cancel request. The difference between pthread_testcancel and other cancellation point calls is that the function does nothing except for creating a cancel point, i.e., specifically responds to cancel request. #include void pthread_testcancel(void); If the pthread_testcancel function does not check the cancel request, it is directly returned to the caller. When the cancel request is checked, the thread will be deleted and no longer returned to the caller. The thread can arrange the function where it needs to call when it exits, such function is called as the thread cleanup processing program, and a thread can create multiple cleanup processing programs. The processing programs are recorded in the stack, i.e., the sequence of execution is opposite with that of registration, as shown in Figure 6.4. Figure 6.4 Cleanup function stack #include void pthread_cleanup_pop(int iNeedRun); void pthread_cleanup_push(void (*pfunc)(void *), void *arg); Prototype analysis of Function pthread_cleanup_pop: Parameter iNeedRun indicates whether to execute. Prototype analysis of the function pthread_cleanup_push: Manual OS. SpaceChain 189 Application Development Manual SpaceChain OS Parameter pfunc is the cleanup function to be executed; Parameter arg is the cleanup function parameter. If iNeedRun is 0, the cleanup function will not be called, the pthread_cleanup_pop function will delete the cleanup processing program created via the last pthread_cleanup_push call. These functions must be used in the paired form in the action scope same with the thread. The following program shows how to use these two functions. Program List 6.7 Thread cleanup #include #include void cleanup (void *arg) { fprintf(stdout, "cleanup: %s.\n", (char *)arg); } void *routine (void *arg) { fprintf(stdout, "thread 1 running...\n"); pthread_cleanup_push(cleanup, "thread1 first"); pthread_cleanup_push(cleanup, "thread1 second"); pthread_cleanup_pop(0); pthread_cleanup_pop(0); return ((void *)1); } void *routine1 (void *arg) { fprintf(stdout, "thread 2 running...\n"); pthread_cleanup_push(cleanup, "thread2 first"); pthread_cleanup_push(cleanup, "thread2 second"); if (arg) { return ((void *)2); } Manual OS. SpaceChain 190 Application Development Manual SpaceChain OS pthread_cleanup_pop(0); pthread_cleanup_pop(0); return ((void *)2); } int main (int argc, char *argv[]) { pthread_t tid, tid1; int ret; void *retval; ret = pthread_create(&tid, NULL, routine, (void *)1); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_join(tid, &retval); if (ret != 0) { fprintf(stderr, "pthread join thread 1 failed.\n"); return (-1); } fprintf(stdout, "thread 1 return code: %ld\n", (long)retval); ret = pthread_create(&tid1, NULL, routine1, (void *)2); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_join(tid1, &retval); if (ret != 0) { fprintf(stderr, "pthread join thread 2 failed.\n"); perror("pthread_join"); return (-1); } fprintf(stdout, "thread 2 exit code: %ld\n", (long)retval); return (0); } Run the program under the SylixOS Shell: # ./push_test Manual OS. SpaceChain 191 Application Development Manual SpaceChain OS thread 1 running... thread 1 return code: 1 thread 2 running... cleanup: thread2 second. cleanup: thread2 first. thread 2 exit code: 2 From running results of the program, it can be seen that both threads run and exit normally, while the thread 1 does not call the cleanup function. As we mentioned before, the pthread_cleanup_pop function parameter iNeedRun is 0, and it will not call the clean function. However, the pthread_cleanup_pop function parameter iNeedRun of the thread2 is also 0, but it calls the cleanup function. This shows that the clean function is called after the thread function exits, and the call sequence is opposite with that during installation. 6.5 POSIX thread key value The POSIX thread key value is also called as the thread private data. Intrinsically, the thread key values has same names and different values for multiple threads, as shown in Figure 6.5. In Section 6.3.4 we discussed the role of thread private data in multi-threaded security. The following describes API of the POSIX thread key and how to use it. (线程=thread) Figure 6.5 Thread key value Note:global chain tables manages all keys in SylixOS. Therefore, keys in SylixOS are visual in the whole system. #include int pthread_key_create(pthread_key_t *pkey, void (*destructor)(void *)); int pthread_key_delete(pthread_key_t key); Prototype analysis of Function pthread_key_create: For success of the function, return 0. For failure, return the error number; Output parameter pkey returns the thread key created; Manual OS. SpaceChain 192 Application Development Manual SpaceChain OS Parameter destructor is the delete function. Prototype analysis of Function pthread_key_delete: For success of the function, return 0. For failure, return the error number; Parameter key is the key to be deleted. Calling the pthread_key_create function can create a key, which can be used by all threads in the same process. Therefore, the key is usually defined as a global variable. If Parameter destructor is not NULL, it is automatically called when the key is deleted. If it is not required to release any memory, Parameter destructor can be set as NULL. Calling the pthread_key_delete function will delete a key. It might also be noted that, the key cannot be deleted in the parameter destructor function, because the key belongs to the whole system, i.e., all threads is visual. If the key in the destructor function is deleted, other threads may access it again, which will cause unpredictable errors. A key corresponds to the only destructor function shared by all threads. The destructor function must be appointed when the key is created, and cannot be changed. If the destructor function is appointed, SylixOS will automatically call the cleanup function when the thread exits. The key returned by the pthread_key_create function is the pthread_key_t type, which may represent different types in different system realization. The key value is just the array index in some systems, while the key value represents an address value in SylixOS. Therefore, we will not get the preconceive results when trying to print a key value. Calling the pthread_setspecific function can associate the thread private data with the key.(the key is returned via the previous pthread_key_create call) The function of the pthread_getspecific function is opposite with that of the pthread_setspecific function, and the private data (pvalue) associated with the key in the thread is returned. #include int pthread_setspecific(pthread_key_t key, const void *pvalue); void *pthread_getspecific(pthread_key_t key); Prototype analysis of Function pthread_setspecific: For success of the function, return 0. For failure, return the error number; Parameter key is the key; Parameter pvalue is the value to be set. Prototype analysis of Function pthread_getspecific: For success of the function, return 0. For failure, return the error number; Parameter key is the key; Manual OS. SpaceChain 193 Application Development Manual SpaceChain OS Parameter pvalue of the pthread_setspecific function is an untyped pointer, which can point to any data type, including complex data structure. When the thread terminates, the pointer will be transfered as a parameter to the destructor function (pthread_key_create parameter) corresponding to the key. The following program shows how to use POSIX thread key. Program List 6.8 Use of POSIX thread key #include #include static char *str = "this is pthread key."; static pthread_key_t key; void *thread (void *arg) { void *keyval; pthread_setspecific(key, str); sleep(2); keyval = pthread_getspecific(key); fprintf(stdout, "keyval child thread is: %s\n", (char *)keyval); return (NULL); } int main (int argc, char *argv[]) { pthread_t tid; int ret; void *keyval; ret = pthread_key_create(&key, NULL); if (ret != 0) { fprintf(stderr, "pthread key create failed.\n"); return (-1); } ret = pthread_create(&tid, NULL, thread, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); Manual OS. SpaceChain 194 Application Development Manual SpaceChain OS } keyval = pthread_getspecific(key); fprintf(stdout, "keyval main thread is: %s\n", (char *)keyval); pthread_join(tid, NULL); pthread_key_delete(key); return (0); } Run the program in SylixOS Shell, and it can be seen from program running results that two threads in the program share a key, but the key value set in the child thread is not visible in the main thread. As shown in Figure 6.5, although the thread ID_x and the thread ID_y share the key key_n, their values value_x and value_y are different. # ./key_test keyval main thread is: (null). keyval child thread is: this is pthread key. 6.6 SylixOS thread scheduling A significant difference between real-time system and time-sharing system is reflected in the scheduling strategy. Real-time system scheduling concerns delay in response to real-time events. However, the traditional time-sharing system scheduling considers multiple objectives: fairness, efficiency, utilization and throughput. Therefore, the real-time system usually adopts priority scheduling, that is to say, the operating system always selects the highest priority from the ready task queue for operation. 6.6.1 Priority scheduling A thread will monopolize processor running once it gets the processor, and the system will schedule other threads unless it decides to abandon the processor for a certain reason, the scheduling mode is called as "non-preemptive scheduling", i.e., the system reselects the thread according to priority scheduling after the thread actively makes way for the processor. The scheduling mode will cause that the ready thread with high priority cannot be timely responded. Therefore, the real-time response speed will be reduced, When a thread is running, the operating system can deprive the processor allocated to it and allocate it to other threads according to a certain principle, the scheduling mode is called as "preemptive scheduling", and the scheduler principle includes: priority principle, time slice principle and so on. The preemptive priority scheduling principle is adopted between different priorities in SylixOS. Manual OS. SpaceChain 195 Application Development Manual SpaceChain OS It can be seen from the above that the "preemptive scheduling” mode shall be adopted when focusing on real-time response. As long as the higher-priority thread is ready, the system immediately interrupts the current thread to schedule the high-priority thread, so as to guarantee that the high-priority thread can get the processor at any time, which is basic requirements for the real-time system. ① The SylixOS kernel supports 256 priorities : 0 to 255. Priority 0 is the highest and Priority 255 is the lowest. The priority is determined when the thread is created, and dynamic modification is allowed during program execution. For the kernel, the priority is determined when a thread is selected from the ready queue, i.e., the kernel will not dynamically calculate the priority of each thread. Therefore, the scheduling strategy belongs to the static scheduling strategy. Compared with scheduling of the dynamic scheduling strategy, the thread priority shall be dynamically determined and scheduled according to a certain target. The efficiency of the static scheduling strategy is higher than that of the dynamic scheduling strategy. #include ULONG Lw_Thread_SetPriority(LW_OBJECT_HANDLE ulId, UINT8 ucPriority); ULONG Lw_Thread_GetPriority(LW_OBJECT_HANDLE ulId, UINT8 *pucPriority); Prototype analysis of Function Lw_Thread_SetPriority: For success of the function, return 0. For failure, return the error number; Parameter ulld is the thread handle to be set; Parameter ucPriority is the new priority value. Prototype analysis of Function Lw_Thread_GetPriority: For success of the function, return 0. For failure, return the error number; Parameter ulId is the thread handle; Output parameter pucPriority returns the thread priority. Calling the Lw_Thread_SetPriority function can set the priority of a appointed thread, and calling the Lw_Thread_GetPriority function can get the priority of the appointed thread. Note: we can use the Shell command sprio to dynamically modify the priority of the running thread. [Command format] sprio [priority thread_id] [Common option] None [Instructions for parameters] Manual OS. SpaceChain 196 Application Development Manual SpaceChain OS priority :Priority value thread_id :Target thread ID 6.6.2 RR (Round-Robin) scheduling The above-mentioned priority-based scheduling strategy has such problems: if it is not preempted by the higher-priority thread, or makes way for the processor due to blockage or other reasons, the thread will keep on running. In the circumstance, the thread with the same priority will not run. RR scheduling is based on such philosophy: on the premise of higher-priority thread scheduling still runs preferentially, fairness in a certain significance is pursued during scheduling between threads with the same priority. RR scheduling divides thread running into time slices. After the thread runs for a time slice, the kernel calls it out of the processor and places it at the queue tail of the ready thread with same priority, and reselects thread running conforming to conditions. The effect of RR scheduling is to "make way for" the processor to the next thread after each thread runs a time slice, like rotation, so it is also called rotation scheduling. It can be seen that RP scheduling does not change characteristics of two real-time scheduling of "priority-based" and "preemptible". If the RR scheduling strategy is adopted, one issue worth considering is determination of the time slice size. The small time slice facilitates the thread with same priority to fairly share the processor, but increases the scheduling overhead. If the time slice is increased, the scheduling overhead decreases, but the scheduling effect will tend to priority scheduling. The reasonable time slice will be compromised between fairness and efficiency. Calling the following function can dynamically change and get the time slice of the thread. #include ULONG Lw_Thread_SetSlice(LW_OBJECT_HANDLE ulId, UINT16 usSlice); ULONG Lw_Thread_GetSlice(LW_OBJECT_HANDLE ulId, UINT16 *pusSliceTemp); ULONG Lw_Thread_GetSliceEx(LW_OBJECT_HANDLE UINT16 ulId, UINT16 *pusSliceTemp, *pusCounter); Prototype analysis of Function Lw_Thread_SetSlice: For success of the function, return 0. For failure, return the error number; Parameter ulId is the thread handle; Parameter usSlice is the new time slice of the thread. Prototype analysis of Function Lw_Thread_GetSlice: Manual OS. SpaceChain 197 Application Development Manual SpaceChain OS For success of the function, return 0. For failure, return the error number; Parameter ulId is the thread handle; Output parameter pusSliceTemp returns the time slice. Prototype analysis of Function Lw_Thread_GetSliceEx: For success of the function, return 0. For failure, return the error number; Parameter ulId is the thread handle; Output parameter pusSliceTemp returns the time slice; Output parameter pusCounter returns the remaining time slice. Calling the Lw_Thread_SetSlice function can set the running slice of the thread, calling the Lw_Thread_GetSlice function can get the time slice of the thread and calling the Lw_Thread_GetSliceEx function will also get the remaining time slice of the thread. Calling the following functions can modify the thread scheduling strategy in SylixOS. #include ULONG Lw_Thread_SetSchedParam(LW_OBJECT_HANDLE ulId, UINT8 ucPolicy, UINT8 ucActivatedMode); ULONG Lw_Thread_GetSchedParam(LW_OBJECT_HANDLE ulId, UINT8 *pucPolicy, UINT8 *pucActivatedMode); Prototype analysis of Function Lw_Thread_SetSchedParam: For success of the function, return 0. For failure, return non-0 value; Parameter ulId is the thread handle; Parameter ucPolicy is the scheduling strategy; Parameter ucActivatedMode is the response model, as shown in Table 6.7. Table 6.7 Thread response mode Response mode Note LW_OPTION_RESPOND_IMMIEDIA High-speed response thread (only for test) LW_OPTION_RESPOND_STANDARD Common response thread LW_OPTION_RESPOND_AUTO Automatic Prototype analysis of Function Lw_Thread_GetSchedParam: For success of the function, return 0. For failure, return non-0 value; Manual OS. SpaceChain 198 Application Development Manual SpaceChain OS Output parameter pucPolicy returns the scheduling strategy; Output parameter pucActivatedMode returns the thread response mode, as shown in Table 6.7. Calling the Lw_Thread_SetSchedParam function can set the thread scheduling strategy, and calling the Lw_Thread_GetSchedParam function can get the thread scheduling strategy. 6.7 POSIX thread scheduling The scheduling behavior is affected by two factors: scheduling strategy and task priority. Each task has a priority. The system maintains a list of ready tasks for each allowed priority. The list has a certain sequence, list head task and list tail task.If there is a new task ready, it will be put at the suitable place in the list (it is usually required to select this according to the scheduling strategy). For the real-time system, the response speed is the most important. Therefore, as real-time extension of the basic definition of POSIX, the scheduling strategy defined by POSIX 1003.1b is based on the priority. Other evaluation indicators such as fairness and throughput capacity are secondary. POSIX standard specifies that the high-priority thread has the large priority digital. The priority in SylixOS is opposite. Therefore, SylixOS performs priority conversion, and definition is as follows: #include #define PX_PRIORITY_CONVERT(prio) (LW_PRIO_LOWEST - (prio)) The macro does not concern application development, but understanding the macro can clarity relationship between POSIX priority and SylixOS priority. POSIX defines the structure sched_param to represent the scheduling-related parameter, and implementation in SylixOS is shown below: struct sched_param { int sched_priority; int sched_ss_low_priority; /* Low scheduling priority for */ struct timespec sched_ss_repl_period; struct timespec sched_ss_init_budget; /* POSIX scheduling priority */ /* SCHED_SPORADIC parameter */ /* sporadic server. */ /* Replenishment period for */ /* sporadic server. */ /* Initial budget for sporadic */ /* server. int sched_ss_max_repl; /* Max pending replenishments */ /* for sporadic server. Manual OS. */ */ SpaceChain 199 Application Development Manual SpaceChain OS …… }; Note: SylixOS only supports the priority setting of the structure at present, while others are reserved items. POSIX defines the following functions to dynamically select thread scheduling strategy (SCHED_FIFO and SCHED_RR). #include pid①, int sched_setscheduler(pid_t int iPolicy, const struct sched_param *pschedparam); int sched_getscheduler(pid_t pid); Prototype analysis of Function sched_setscheduler: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pid is the process ID; Parameter iPolicy is the scheduling strategy. Output parameter pschedparam is the scheduler parameter. Prototype analysis of Function sched_getscheduler: For success of the function, return the scheduler strategy (SCHED_FIFO and SCHED_RR). For failure, return -1 and set the error number; Parameter pid is the process ID; In Section 6.4.1, when the scheduling strategy is introduced, we learned that the thread scheduling policy can be set via calling the pthread_attr_setschedpolicy function, the method is set before the thread is created, i.e., a static change method. However, a method to dynamically change the thread priority is provided by calling the sched_setscheduler function. Calling the sched_getscheduler function can get the scheduling policy of the appointed thread. It might also be noted that the sched_setscheduler function will set the thread priority while setting the scheduling strategy, and later we will introduce what range values the POSIX thread priority shall satisfy. Calling the following two functions can get the maximum and minimum values of the POSIX thread priority, and the priority set by application shall be within the range of these two values (excluded). ① Process operation usually refers to operation of the main thread of the process in SylixOS. Therefore, the process scheduling strategy here usually refers to the thread scheduling strategy. Manual SpaceChain OS. 200 Application Development Manual SpaceChain OS #include int sched_get_priority_max(int iPolicy); int sched_get_priority_min(int iPolicy); Prototype analysis of Function sched_get_priority_max: The function returns the maximum priority value of POSIX; Parameter iPolicy is the scheduling strategy. Prototype analysis of Function sched_get_priority_min: The function returns the minimum priority value of POSIX; Parameter iPolicy is the scheduling strategy. POSIX allows different priority ranges to be defined when different scheduling strategies are adopted. For implementation of current SylixOS, all scheduling strategies have the same priority range. Calling the sched_setscheduler function sets the process priority while setting the scheduling priority. Actually, calling the sched_setparam function can set the process priority, and calling the sched_getparam function can get the priority of the appointed process. #include int sched_setparam(pid_t pid, const struct sched_param *pschedparam); int sched_getparam(pid_t pid, struct sched_param *pschedparam); Prototype analysis of Function sched_setparam: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pid is the process ID; Output paramete pschedparam is the scheduling parameter. Prototype analysis of Function sched_getparam: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pid is the process ID; The output parameter pschedparam returns the scheduling parameter. If pid equals 0, the current thread priority is set. It might also be noted that if the set priority is the same with the current priority of the appointed thread, nothing is done and 0 is returned. #include Manual OS. SpaceChain 201 Application Development Manual SpaceChain OS int sched_rr_get_interval(pid_t pid, struct timespec *interval); int sched_yield(void); Prototype analysis of Function sched_rr_get_interval: For success of the function, return 0. For failure, return -1 and set the error number; Parameter pid is the process ID; Output parameter interval returns the time remaining for the process or thread. Calling the sched_rr_get_interval function will return the time slice size (timespec type time value) of the appointed thread. The function is valid only when the scheduling strategy is SCHED_RR. Otherwise, it returns -1 and sets errno as EINVAL. Calling the sched_yield function will actively abandon the processor once. The following program shows how to use the POSIX thread scheduling function. Program List 6.9 Use of the POSIX thread scheduling function #include #include int main (int argc, char *argv[]) { struct sched_param param; struct sched_param newparam; struct timespec time; int ret; fprintf(stdout, "Max prio: %d, Min: %d\n", sched_get_priority_max(SCHED_RR), sched_get_priority_min(SCHED_RR)); ret = sched_getparam(0, ¶m); if (ret != 0) { perror("sched_getparam"); return (-1); } fprintf(stdout, "old prio: %d\n", param.sched_priority); param.sched_priority = 40; ret = sched_setscheduler(0, SCHED_RR, ¶m); if (ret != 0) { return (-1); } Manual OS. SpaceChain 202 Application Development Manual SpaceChain OS ret = sched_getparam(0, &newparam); if (ret != 0) { perror("sched_getparam"); return (-1); } fprintf(stdout, "new prio: %d\n", newparam.sched_priority); sched_rr_get_interval(0, &time); fprintf(stdout, "time slice %lld(s):%ld(ns)\n", time.tv_sec, time.tv_nsec); return (0); } Run the program in the SylixOS Shell: Running results show that the range of available priority of the POSIX thread in SylixOS is from 1 to 254, indicating that priority 0 and priority 255 are not recommended by the Sylix OS kernel. # ./sched_test Max prio: 254, Min prio: 1 old prio: 55 new prio: 40 time slice 0(s):70000000(ns) 6.8 SylixOS RMS scheduling A very good method to solve multi-task scheduling conflicts for the periodic task is rate monotonic scheduling (Rate Monotonic Scheduling RMS), and RMS appoints the priority based on the task cycle. In RMS, the task with the shortest cycle has the highest priority, the task with the next shortest cycle has the next highest priority, and so forth. When multiple tasks can be executed at the same time, the task with the shortest cycle is executed preferentially. If the function deems the task priority as the rate, then this is a monotonically increasing function. #include LW_HANDLE Lw_Rms_Create(CPCHAR ULONG LW_OBJECT_ID pcName, ulOption, *pulId); ULONG Lw_Rms_Delete(LW_HANDLE *pulId); ULONG Lw_Rms_DeleteEx(LW_HANDLE *pulId, BOOL ULONG Lw_Rms_Cancel(LW_HANDLE bForce); ulId); Manual OS. SpaceChain 203 Application Development Manual SpaceChain OS Prototype analysis of Function Lw_Rms_Create: For function success, return RMS handle. LW_HANDLE_INVALID, and set the error number; Parameter pcName is RMS name; Parameter ulOption is RMS option; Output parameter pulId returns RMS handle. For failure, return Prototype analysis of Function Lw_Rms_Delete: For success of the function, return 0. For failure, return the error number; Parameter pulId is RMS handle pointer. Prototype analysis on Function Lw_Rms_DeleteEx: For success of the function, return 0. For failure, return the error number; Parameter pulId is RMS handle pointer; Parameter bForce is delete type. Prototype analysis of Function Lw_Rms_Cancel: For success of the function, return 0. For failure, return the error number; Parameter ulId is RMS handle. Calling Lw_Rms_Create function can create an RMS scheduler; calling Lw_Rms_Delete function can delete an RMS scheduler. It might also be noted that if RMS scheduler is at the task blocking state, the RMS object will not be deleted, and errno will be set as ERROR_RMS_STATUS; Calling Lw_Rms_DeleteEx function can delete an RMS scheduler. Different from Lw_Rms_Delete function, if bForce is true, the RMS scheduler will be deleted regardless of the state. Otherwise, the behavior is the same with that of the Lw_Rms_Delete function; calling the Lw_Rms_Cancel function will stop the specified RMS scheduler, but will not delete RMS object. #include ULONG Lw_Rms_Period(LW_HANDLE ulId, ULONG ulPeriod); ULONG Lw_Rms_ExecTimeGet(LW_HANDLE *pulId, ULONG *pulExecTime); Prototype analysis of Function Lw_Rms_Period: For success of the function, return 0. For failure, return the error number; Parameter ulId is the RMS handle; Manual OS. SpaceChain 204 Application Development Manual SpaceChain OS Parameter ulPeriod is the program execution cycle. Prototype analysis of Function Lw_Rms_ExecTimeGet: For success of the function, return 0. For failure, return the error number; Parameter ulId is the RMS handle; Output parameter pulExecTime returns the running time. After the Lw_Rms_Period function is called, RMS scheduler will start work according to the period specified by Parameter ulPeriod; calling the Lw_Rms_ExecTimeGet function will obtain the time from the start of Lw_Rms_Period function call to the current execution (unit: Tick). #include ULONG Lw_Rms_Status(LW_HANDLE ulId, UINT8 *pucStatus, ULONG *pulTimeLeft, LW_HANDLE *pulOwnerId); ULONG Lw_Rms_GetName(LW_HANDLE ulId, PCHAR pcName); Prototype analysis of Function Lw_Rms_Status: For success of the function, return 0. For failure, return the error number; Parameter ulId is the RMS handle; Output parameter pucStatus returns RMS state; Output parameter pulTimeLeft returns the remaining waiting time; Output parameter pulOwnerId returns the owner ID. Prototype analysis of Function Lw_Rms_GetName: For success of the function, return 0. For failure, return the error number; Parameter ulId is the RMS handle; Output parameter pcName returns RMS name. Calling Lw_Rms_Status will return the status of the RMS scheduler. If Parameter pucStatus is not NULL, the status as shown in Table 6.8 will be returned . If Parameter pulTimeLeft is not NULL, the remaining time of the schedule will be returned. If Parameter pulOwnerId is not NULL, the thread handle of the RMS scheduler owner will be returned; calling the Lw_Rms_GetName function will return the name of the RMS scheduler. Table 6.8 RMS scheduler status Manual OS. SpaceChain 205 Application Development Manual SpaceChain OS State name Note LW_RMS_INACTIVE RMS scheduler just created LW_RMS_ACTIVE Initialize the cycle, and measure the execution time LW_RMS_EXPIRED With task blocking As an extension to POSIX, SylixOS provides the following set of functions to implement the POSIX RMS scheduler. Compared with the previous RMS implementation, the following functions are easier to use, and have higher time accuracy (nanosecond-level). #include int sched_rms_init(sched_rms_t *prms, pthread_t thread); int sched_rms_destroy(sched_rms_t *prms); int sched_rms_period(sched_rms_t *prms, const struct timespec *period); Prototype analysis of Function sched_rms_init: For success of the function, return 0. For failure, return -1 and set the error number; Parameter prms is the RMS scheduler pointer; Parameter thread is the call thread handle. Prototype analysis of Function sched_rms_destroy: For success of the function, return 0. For failure, return -1 and set the error number; Parameter prms is the RMS scheduler pointer; Prototype analysis of Function sched_rms_period: For success of the function, return 0. For failure, return -1 and set the error number; Parameter prms is the RMS scheduler pointer; Parameter period is the program execution cycle。 Calling the sched_rms_init function will initialize the RMS scheduler specified by Parameter prms. Unlike the Lw_Rms_Create function, the application creates an RMS scheduler of sched_rms_t type, and then the sched_rms_init function is called to initialize the scheduler for the former. That is to say, the scheduler will be created and destroyed by the application. However, the RMS scheduler created by the latter is managed by the kernel, i.e., the application will not directly manage the scheduler used. Manual OS. SpaceChain 206 Application Development Manual SpaceChain OS Calling the sched_rms_destroy function will destroy the scheduler initialized by the sched_rms_init function. The destroyed scheduler cannot be used unless it is reinitialized. Calling the sched_rms_period function enables the RMS scheduler to start working. The following program shows how to use the RMS scheduler. Program List 6.10 RMS scheduler #include #include sched_rms_t rms; void process_func(void) { int i = 1; for (; i >= 0; --i) { sleep(1); } } void *rms_thread (void *arg) { struct timespec *period = (struct timespec *)arg; sched_rms_init(&rms, pthread_self()); while (1) { if (sched_rms_period(&rms, period) != 0) { break; } process_func(); fprintf(stdout, "rms thread running...\n"); } return (NULL); } int main (int argc, char *argv[]) { pthread_t tid; int ret; Manual OS. SpaceChain 207 Application Development Manual SpaceChain OS struct timespec period.tv_nsec = 0; period.tv_sec = 3; period; ret = pthread_create(&tid, NULL, rms_thread, (void *)&period); if (ret < 0) { fprintf(stderr, "pthread_create error.\n"); return (-1); } pthread_join(tid, NULL); sched_rms_destroy(&rms); return (0); } Run the program under the SylixOS Shell: # ./rms_test rms thread running... rms thread running... …… The scheduling cycle set by the program is 3 seconds, and the running time of the running function process_func of the thread rms_thread is more than 2 seconds and less than 3 seconds. Therefore, the thread can be scheduled normally, and program operation results also prove the point. If we change the i value in the process_func function to a value larger than or equal to 2, the thread will only be scheduled once. The running time of the thread at the moment is greater than the cycle value of the RMS scheduler, which will cause timeout overflow error of the scheduler (EOVERFLOW). 6.9 SylixOS coroutine Coroutine is the executable code sequence smaller than the thread. A thread can have multiple coroutines, which share resources of the thread except the stack, such as the priority, kernel object and so on. All coroutines in the thread share the kernel object of the thread itself. Therefore, the scheduler itself does not know existence of the coroutine, and the coroutine is executed when the thread to which it belongs is scheduled. The internal coroutine of a thread cannot be preempted, and can only run in cycle. Only when the current running coroutine voluntarily abandons the processor, other coroutines in the same thread can get the processor. When the thread is deleted, all coroutines within the thread are deleted at the same time. Manual OS. SpaceChain 208 Application Development Manual SpaceChain OS The SylixOS kernel supports coroutines instead of using third-party library emulation, which makes coroutine management within SylixOS easier and more efficient. Calling the Lw_Coroutine_Create function will create a coroutine in the current thread. It might also be noted that creation of each thread creation will create a starting coroutine at default. Therefore, the thread always runs from the starting coroutine. #include PVOID Lw_Coroutine_Create(PCOROUTINE_START_ROUTINE pCoroutineStartAddr, size_t stStackByteSize, PVOID pvArg); Prototype analysis of Function Lw_Coroutine_Create: For success of the function, return the coroutine control pointer. For failure, return LW_NULL and set the error number; Parameter pCoroutineStartAddr is the coroutine starting address; Parameter stStackByteSize is the coroutine stack size ; Parameter pvArg is the entry parameter. ① Calling the Lw_Coroutine_Delete function will delete an appointed coroutine. If the current coroutine system is deleted, the system will directly call the Lw_Coroutine_Exit function for exit. The thread will exit when the last coroutine in the thread exits. #include ULONG Lw_Coroutine_Delete(PVOID pvCrcb); ULONG Lw_Coroutine_Exit(VOID); Prototype analysis of Function Lw_Coroutine_Delete: For success of the function, return 0. For failure, return the error number; Parameter pvCrcb is the coroutine control pointer. Prototype analysis of Function Lw_Coroutine_Exit: For success of the function, return 0. For failure, return the error number; As we mentioned earlier, the scheduler does not know existence of the coroutine, and the coroutines run in cycle, which determines that coroutine scheduling must be managed by the user program. SylixOS provides the following functions to change the scheduling sequence of the coroutine. #include VOID Lw_Coroutine_Yield(VOID); ULONG Lw_Coroutine_Resume(PVOID pvCrcb); Prototype analysis of Function Lw_Coroutine_Resume: Manual OS. SpaceChain 209 Application Development Manual SpaceChain OS For success of the function, return 0. For failure, return non-0 value; Parameter pvCrcb is the coroutine control pointer. Calling the Lw_Coroutine_Yield function can actively abandon the processor, and calling the Lw_Coroutine_Resume function can recover the appointed coroutine. Calling the Lw_Coroutine_StackCheck function can check the coroutine stack. #include ULONG Lw_Coroutine_StackCheck(PVOID pvCrcb, size_t *pstFreeByteSize, size_t *pstUsedByteSize, size_t *pstCrcbByteSize); Prototype analysis of Function Lw_Coroutine_StackCheck: For success of the function, return 0. For failure, return the error number; Parameter pvCrcb is the coroutine control pointer; Output parameter pstFreeByteSize returns the idle stack size; Output parameter pstFreeByteSize returns the use stack size; Output parameter pstCrcbByteSize returns the size of the coroutine control block. Parameters pstFreeByteSize, pstUsedByteSize and pstCrcbByteSize can be NULL. If the corresponding parameter is NULL, stack of the appointed type will not be concerned. It might also be noted that if service of the coroutine stack is checked, the stack check option must be used for the stack the coroutine parent, as shown in Table 6.3. The following program shows how to use the SylixOS coroutine. Program List 6.11 Coroutine use #include #include VOID coroutine0 (PVOID pvArg) { INT i; for (i = 0; i < 5; i++) { fprintf(stdout, "coroutine0 running...\n"); Lw_Time_SSleep(1); } } Manual OS. SpaceChain 210 Application Development Manual SpaceChain OS VOID coroutine1 (PVOID pvArg) { INT i; for (i = 0; i < 5; i++) { fprintf(stdout, "coroutine1 running...\n"); Lw_Time_SSleep(1); } } PVOID tTest (PVOID pvArg) { PVOID pcCrcb0, pcCrcb1; pcCrcb0 = Lw_Coroutine_Create(coroutine0, 2 * 1024, LW_NULL); if (pcCrcb0 == LW_NULL) { return (LW_NULL); } pcCrcb1 = Lw_Coroutine_Create(coroutine1, 2 * 1024, LW_NULL); if (pcCrcb1 == LW_NULL) { return (LW_NULL); } Lw_Coroutine_Yield(); run /* Make other coroutines */ while (1) { Lw_Time_SSleep(10); } return ((PVOID)1); } int main (int argc, char *argv[]) { LW_HANDLE hId; hId = Lw_Thread_Create("t_test", tTest, LW_NULL, LW_NULL); if (hId == LW_HANDLE_INVALID) { return (PX_ERROR); } Manual OS. SpaceChain 211 Application Development Manual SpaceChain OS Lw_Thread_Join(hId, LW_NULL); return (ERROR_NONE); } Run the program under the SylixOS Shell: # ./coroutine_test coroutine0 running... coroutine0 running... coroutine0 running... coroutine0 running... coroutine0 running... coroutine1 running... coroutine1 running... coroutine1 running... coroutine1 running... coroutine1 running... It can be seen from running results that the coroutine0 gets execution first, and is not interrupted due to delay and blockage at running time. Let's analyze the process below. In the tTest thread, the program creates the coroutine0 coroutine firstly and then the coroutine1 coroutine. It was introduced above that when the thread is created, a starting coroutine will be created at default. Therefore, the thread starts running from the starting coroutine firstly. Here the program calls the Lw_Coroutine_Yield function to actively abandon the processor, and the coroutine0 get the processor for running at the moment. The coroutines run in cycle. Therefore, the coroutine0 will not actively abandon the processor before running (the program does not actively call the Lw_Coroutine_Yield function. Meanwhile, it is proved that the scheduler does not perceive existence of the coroutine). The coroutine1 will be automatically switched to after running of coroutine0. This also indicates that the coroutine firstly created will get the processor in priority, which conforms to the FIFO principle. Manual OS. SpaceChain 212 Application Development Manual SpaceChain OS Chapter 7 Inter-thread communication 7.1 Shared resource The entity, such as a variable, device or memory block accessible by the thread, is called as resource. The resources which can be accessed by multiple threads are called as shared resources; while the behavior of simultaneous access to shared resources is called as shared resource competition. If you do not monopolize the shared resource while accessing the shared resource, it may cause resource exception (such as variable value confusion, device error, or the memory block content not being the expected value, etc.), which may lead to the program to run abnormally or even crash. Now, two threads (Thread A and Thread B) need to perform add one operation to the same Variable V (the initial value is 0). On the RISC machine, the load/store system structure is generally adopted, that is to say, access to the memory only allows load and store operations; the machine instruction process for auto increment operation of Variable V is as follows: (1) Load the address of Variable V to the working register 0 of the CPU; (2) The load instruction loads the contents of the address stored in working register 0 into working register 1; (3) The inc instruction adds 1 to the value of working register 1; (4) The store instruction saves the value of working register 1 to the address pointed to by working register 0. It can be seen from the above that the auto increment operation of Variable V is not completed in one step. If Thread A and Thread B complete the above four steps in sequence, then the final value of Variable V will be 2. If Thread A completes the previous three steps, then Thread B interrupts the work of Thread A, Thread B rewrites Variable V to 1; Thread A continues to perform the fourth step, and then the final value of Variable V is still 1, which is obviously not what we expect. In order to solve this problem, we need to perform mutex access to the process. Mutex is an exclusive behavior, which means that only one thread is allowed to access shared resources at the same time. There are several methods to implement mutex: disable interrupt, disable task scheduling, semaphore and so on. Manual OS. SpaceChain 213 Application Development Manual SpaceChain OS For the above process, we can add a lock (semaphore), which must be held before auto increment operation of Variable V, and released after the operation is completed; assuming that the lock has been occupied by Thread A, if the thread B also wants to apply for the lock, Thread B will be blocked because the lock is exclusive; this ensures that only one thread can access the variable at the same time, so that the value of Variable V will not have the risk of confusion. (变量 V 的自增操作=The autogenous operation of variable V) Figure 7.1 Auto increment operation of Variable V We call the area protected by the lock as the critical area. If the critical area protection code cannot be interrupted, then the process is atomic operation. Non-interruptible means that there is no blocking or hardware interrupt in the critical area, and the atomic operation masks the hardware interrupt response of the current CPU core. Therefore, the atomic operation shall be as brief as possible. In the multi-thread environment, operation of each variable needs to be treated with care. Multi-threaded programming can make our program clear and easy to implement, but it requires careful design. SylixOS has prepared us a large number of solutions to mutex problems during multi-threaded programming, such as semaphore, mutex lock, message queue and so on. 7.2 Inter-thread communication In the process of executing the thread, it is inevitable to communicate with other threads. For example, if Thread A notifies Thread B of the processing results after processing an event, Thread B will continue to run after receiving the processing results of the event. There are mainly the following types of inter-thread communication: Mutex type communication: shared resources require exclusive access, and semaphore and mutex can be used for mutex type communication. Notification type communication: the above Thread A informs Thread B, and the notification-type communication can be performed by using semaphore, event set and condition variable; Manual OS. SpaceChain 214 Application Development Manual SpaceChain OS Message type communication: a thread or an interrupt service program is only responsible for collecting data, but does not directly process data, and passes the data to another thread for data processing. Message queue can be used for message type communication. SylixOS operating system provides rich means for inter-thread communication, as shown in Table 7.1. These communication means satisfy demands for inter-thread communication for embedded system software development. Table 7.1 Inter-thread communication means Inter-thread communication means Binary semaphore Purpose Mutex type communication and notification type communication Counting semaphore Notification type communication Mutex type semaphore Mutex type communication Event set Notification type communication Condition variable Notification type communication Message queue Message type communication 7.3 SylixOS semaphore As described in 7.1, multiple threads must implement mutex access or synchronous access (for example, Thread B waits for the result of Thread A to continue running) to the shared data through a certain method when reading or writing certain shared data (global variables, etc.). Among them, semaphore is one of the most common methods. In fact, semaphore is an agreed mechanism: during mutex access to shared resources, it is agreed that when a thread gets the semaphore (Wait), other threads cannot get the semaphore again until the semaphore is released (Give). In the synchronization mechanism, it is agreed that the thread waiting for the semaphore (Take) (or waiting signal is more precise) shall be at the blocking state before receiving the semaphore until other thread send the semaphore (Post). In general, only three operations can be performed on the semaphore: create a semaphore (Create), wait semaphore (Wait) or pend (Pend), give semaphore (Give) or post (Post). The operating system shall include a semaphore waiting thread queue (for storing thread waiting for semaphore). When the semaphore can be obtained, the operating system selects a thread which can get the semaphore from the queue to continue running according to a certain strategy. SylixOS semaphore include four types: binary semaphore, counting semaphore, mutex semaphore (for short mutex) and read-write semaphore. Manual OS. SpaceChain 215 Application Development Manual SpaceChain OS The value of binary semaphore is limited to FALSE and TRUE; while the minimum value of the counting semaphore is 0, and the maximum value is determined when creating the counting semaphore. The binary semaphore is mainly used in the following situations: There is a resource which allows the thread to access, the binary semaphore is used as the mutex means, and the initial value is TRUE; The thread or interrupt informs another thread that an event occurs, and the initial value is FALSE. The counting semaphore is mainly used in the following situations: There are n resources which allows the thread to access, the counting semaphore is used as the remaining count of resources, and the initial value is n; The thread or interrupt informs another thread that some event occurs, the counting semaphore is used as the event count, and the initial value is 0. 7.3.1 Binary semaphore As mentioned above, the semaphore must be created before use. SylixOS provides the following functions to create a binary semaphore. #include LW_HANDLE Lw_SemaphoreB_Create(CPCHAR pcName, BOOL bInitValue, ULONG ulOption, LW_OBJECT_ID *pulId); Prototype analysis of Function Lw_SemaphoreB_Create: The function returns the handle of binary semaphore. For failure, return NULL and set the error number. Parameter pcName is the name of binary semaphore; Parameter bInitValue is the initial value of binary semaphore (FALSE or TRUE); Parameter ulOption is the creation option of binary semaphore, as shown in Table 7.2; Output parameter pulld is used to return the handle of binary semaphore (same with the return value), which can be NULL. Table 7.2 Creation Options of binary semaphore Macro name Meaning Manual OS. SpaceChain 216 Application Development Manual SpaceChain OS LW_OPTION_WAIT_PRIORITY Wait in priority order LW_OPTION_WAIT_FIFO Wait in FIFO order LW_OPTION_OBJECT_GLOBAL Global object LW_OPTION_OBJECT_LOCAL Native object SylixOS provides two semaphore waiting queues: Priority (LW_OPTION_WAIT_PRIORITY) and FIFO (LW_OPTION_WAIT_FIFO). The priority mode is to take the thread conforming to conditions from the queue for running according to the thread priority; the FIFO mode is to take the thread conforming to conditions from the queue for running according to the FIFO principle; It might be noted that either LW_OPTION_WAIT_PRIORITY or LW_OPTION_WAIT_FIFO can be selected. Likewise, either ① LW_OPTION_OBJECT_GLOBAL or LW_OPTION_OBJECT_LOCAL can also be selected. Different parameter bInitValue determines different purposes of binary semaphores, which can be used for mutex access of shared resources when the value of bInitValue is TRUE, as shown in Figure 7.2. When the value is FALSE, the bInitValue can be used for synchronization between multiple threads, as shown in Figure 7.3. Figure 7.2 Mutex access of shared resources Manual OS. SpaceChain 217 Application Development Manual SpaceChain OS Figure 7.3 Thread synchronization The binary semaphore not required can be deleted by calling the following functions, and SylixOS will recover kernel resources occupied (unknown errors will occur when trying to use the deleted binary semaphore). #include ULONG Lw_SemaphoreB_Delete(LW_HANDLE *pulId); Prototype analysis of Function Lw_SemaphoreB_Delete: For success of the function, return 0. For failure, return the error number; Parameter pulId is the handle of binary semaphore. If a thread needs to wait for a binary semaphore, the Lw_SemaphoreB_Wait function can be called It might be noted that the interrupt service routine cannot call the Lw_SemaphoreB_Wait function to wait for a binary semaphore, because the function will block the currently executing task when the value of binary semaphore is FALSE, and the interrupt service routine is used to handle the most urgent things. Therefore, blockage shall not be allowed. Otherwise, other threads will not get the scheduling opportunity. #include ULONG Lw_SemaphoreB_Wait(LW_HANDLE ulId, ULONG ULONG ulTimeout); Lw_SemaphoreB_TryWait(LW_HANDLE ulId); Prototype analysis of above functions: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of binary semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick. Parameter ulTimeout ① can use macros as shown in Table 7.3 in addition to digital. Table 7.3 Available macros of Parameter ulTimeout Manual OS. SpaceChain 218 Application Development Manual SpaceChain OS Macro name Meaning LW_OPTION_NOT_WAIT Immediately exit without waiting LW_OPTION_WAIT_INFINITE Always wait LW_OPTION_WAIT_A_TICK Wait for a clock tick LW_OPTION_WAIT_A_SECOND Wait for a second SylixOS provides a timeout mechanism for binary semaphore wait. It is returned immediately and errno is set as ERROR_THREAD_WAIT_TIMEOUT when the waiting time is timed out. Lw_SemaphoreB_TryWait is a non-blocking semaphore wait function, and difference between the function and Lw_SemaphoreB_Wait is that if the initial value created by binary semaphore is FALSE, Lw_SemaphoreB_TryWait will immediately exit and return, while Lw_SemaphoreB_Wait will be blocked until awakened. The interrupt service routine can use the Lw_SemaphoreB_TryWait function to try to wait for binary semaphore, because the Lw_SemaphoreB_TryWait function will immediately return when the value of binary semaphore is FALSE, and the current thread will not be blocked. Release of a binary semaphore can call the Lw_SemaphoreB_Post, Lw_SemaphoreB_Post2, or Lw_SemaphoreB_Release function. The Lw_SemaphoreB_Post2 function can return the activated thread handle via Parameter pulId when return. If the parameter pulId is set as NULL, the behavior is the same with Lw_SemaphoreB_Post. #include ULONG Lw_SemaphoreB_Post(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreB_Post: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of binary semaphore. #include ULONG Lw_SemaphoreB_Post2(LW_HANDLE ulId, LW_HANDLE *pulId); Prototype analysis of Function Lw_SemaphoreB_Post: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of binary semaphore; Parameter pulld returns the activated thread ID. #include ULONG Manual OS. Lw_SemaphoreB_Release(LW_HANDLE ulId, SpaceChain 219 Application Development Manual SpaceChain OS ULONG BOOL ulReleaseCounter, *pbPreviousValue); Prototype analysis of Function Lw_SemaphoreB_Release: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of binary semaphore; Parameter ulReleaseCounter is the release counter of binary semaphore; Output parameter pbPreviousValue is used to receive the original binary semaphore state, which can be NULL. The Lw_SemaphoreB_Release function is an advanced API, and multiple threads can be released at one time by calling the function when wait for the same semaphore (POSIX thread barrier calls the function to release multiple waiting threads, see Section 7.13). Figure 7.4 shows the operation process of the basic operation function of binary semaphore between threads and between interrupt and thread. (线程=thread) (中断=break off) Figure 7.4 SylixOS binary semaphore Calling the Lw_SemaphoreB_Clear function will clear binary semaphore, so that the initial value of binary semaphore will be set as FALSE. #include ULONG Lw_SemaphoreB_Clear(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreB_Clear: The function returns the error number; Manual OS. SpaceChain 220 Application Development Manual SpaceChain OS Parameter ulId is the handle of binary semaphore. Calling the Lw_SemaphoreB_Flush function will release all threads waiting on the appointed semaphore. #include ULONG Lw_SemaphoreB_Flush(LW_HANDLE ULONG ulId, *pulThreadUnblockNum); Prototype analysis of Function Lw_SemaphoreB_Flush: The function returns the error number; Parameter ulId is the handle of binary semaphore; Output parameter pulThreadUnblockNum is used to receive the number of unblocked threads, which can be NULL. The Lw_SemaphoreB_Status function returns state information of a valid semaphore. #include ULONG Lw_SemaphoreB_Status(LW_HANDLE ulId, BOOL *pbValue, ULONG *pulOption, ULONG *pulThreadBlockNum); Prototype analysis of Function Lw_SemaphoreB_Status: The function returns the error number; Parameter ulId is the handle of binary semaphore; Output parameter pbValue is used to receive the current value of binary semaphore (FALSE or TRUE); Output parameter pulOption is used to receive creation options of binary semaphore; Output parameter pulThreadBlockNum is used to receive the number of the thread currently blocked at the binary semaphore. Calling the Lw_SemaphoreB_GetName function can get the name of the appointed semaphore. #include ULONG Lw_SemaphoreB_GetName(LW_HANDLE ulId, PCHAR pcName) Prototype analysis of Function Lw_SemaphoreB_GetName: The function returns the error number; Parameter ulId is the handle of binary semaphore; Manual OS. SpaceChain 221 Application Development Manual SpaceChain OS Output parameter pcName is the name of the binary semaphore, and pcName shall be pointed at a character array with size of LW_CFG_OBJECT_NAME_SIZE. #include ULONG Lw_SemaphoreB_WaitEx(LW_HANDLE ulId, ULONG ulTimeout, PVOID *ppvMsgPtr); Prototype analysis of Function Lw_SemaphoreB_WaitEx: The function returns the error number; Parameter ulId is the handle of binary semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick; Output parameter ppvMsgPtr (a null pointer) is used to receive messages transferred by the Lw_SemaphoreB_PostEx function. #include ULONG Lw_SemaphoreB_PostEx(LW_HANDLE ulId, PVOID pvMsgPtr); Prototype analysis of Function Lw_SemaphoreB_PostEx: The function returns the error number; Parameter ulId is the handle of binary semaphore; Parameter pvMsgPtr is the message pointer (a null pointer which can point to any type of data). The message will be transferred to Output parameter ppvMsgPtr of the Lw_SemaphoreB_WaitEx function. The message passing function is added in the Lw_SemaphoreB_WaitEx and Lw_SemaphoreB_PostEx functions, and the additional message can be transferred in semaphore via Parameter pvMsgPtr. For example, the following program fragment shows the process: threadA () { PVOID ppvMsgPtr; Lw_SemaphoreB_WaitEx(ulId, &ppvMsgPtr); } threadB () { Lw_SemaphoreB_PostEx(ulId, "msg"); Manual OS. SpaceChain 222 Application Development Manual SpaceChain OS } Note: thread A and thread B are two different threads, and the above process implements a simply inter-thread synchronous creation of additional message. Function composition of Lw_SemaphoreB_WaitEx and Lw_SemaphoreB_PostEx has played the role of the traditional RTOS E-mail, and SylixOS does not provide API for E-mail. The following program shows how to use SylixOS binary semaphore, the program creates two threads and a SylixOS binary semaphore, two threads respectively perform auto increment operation and printing for Variable _G_iCount, and SylixOS binary semaphore is used as mutex means of the access variable _G_iCount. Program List 7.1 How to use SylixOS binary semaphore #include static LW_HANDLE _G_hLock; static INT _G_iCount = 0; static PVOID tTestA (PVOID pvArg) { INT iError; while (1) { iError = Lw_SemaphoreB_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE); if (iError != ERROR_NONE) { break; } _G_iCount++; printf("tTestA(): count = %d\n", _G_iCount); Lw_SemaphoreB_Post(_G_hLock); Lw_Time_MSleep(500); } return (LW_NULL); } static PVOID tTestB (PVOID pvArg) { INT iError; while (1) { Manual OS. SpaceChain 223 Application Development Manual SpaceChain OS iError = Lw_SemaphoreB_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE); if (iError != ERROR_NONE) { break; } _G_iCount++; printf("tTestB(): count = %d\n", _G_iCount); Lw_SemaphoreB_Post(_G_hLock); Lw_Time_MSleep(500); } return (LW_NULL); } int main (int argc, char *argv[]) { LW_HANDLE hThreadAId; LW_HANDLE hThreadBId; _G_hLock = Lw_SemaphoreB_Create("count_lock", LW_TRUE, LW_OPTION_WAIT_FIFO | LW_OPTION_OBJECT_LOCAL, LW_NULL); if (_G_hLock == LW_OBJECT_HANDLE_INVALID) { printf("semaphore create failed.\n"); return (-1); } hThreadAId = Lw_Thread_Create("t_testa", tTestA, LW_NULL, LW_NULL); if (hThreadAId == LW_OBJECT_HANDLE_INVALID) { printf("t_testa create failed.\n"); return (-1); } hThreadBId = Lw_Thread_Create("t_testb", tTestB, LW_NULL, LW_NULL); if (hThreadBId == LW_OBJECT_HANDLE_INVALID) { printf("t_testb create failed.\n"); return (-1); } Lw_Thread_Join(hThreadAId, LW_NULL); Manual OS. SpaceChain 224 Application Development Manual SpaceChain OS Lw_Thread_Join(hThreadBId, LW_NULL); Lw_SemaphoreB_Delete(&_G_hLock); return (0); } Run the program under the SylixOS Shell: # ./SemaphoreB tTestA(): count = 1 tTestB(): count = 2 tTestA(): count = 3 tTestB(): count = 4 tTestA(): count = 5 tTestB(): count = 6 7.3.2 Counting semaphore As mentioned above, counting semaphore is usually used for multiple threads to share a certain resource. For example, manage a certain equipment ID pool with semaphore. It is assumed that the ID pool can apply for 15 device IDs at the same time, and we use counting semaphore to perform mutex access for the ID pool in this case. For each equipment ID applied, the counting semaphore is reduced by 1, and it is reduced to 0, the thread for ID reapplication will be blocked. If the equipment ID is released, the counting semaphore is added by 1, and the new thread can be reapplied at the moment. The process is as shown in Figure 7.5. Manual OS. SpaceChain 225 Application Development Manual SpaceChain OS Figure 7.5 How to use counting semaphore A SylixOS counting semaphore can call the Lw_SemaphoreC_Create function for creation, and a handle of counting semaphore will be returned after successful creation. #include LW_HANDLE Lw_SemaphoreC_Create(CPCHAR pcName, ULONG ulInitCounter, ULONG ulMaxCounter, ULONG ulOption, LW_OBJECT_ID *pulId); Prototype analysis of Function Lw_SemaphoreC_Create: For success of the function, return the handle of the counting semaphore. For failure, return NULL and set the error number; Parameter pcName is the name of counting semaphore; Parameter ulInitCounter is the handle of counting semaphore; Parameter ulMaxCounter is the maximum value of counting semaphore; Parameter ulOption is the creation option of the counting semaphore, as shown in Table 7.2. Output parameter pulld returns ID of counting semaphore (same with the return value), which can be NULL. The value range of counting semaphore is 0 <= counted value (ulInitCounter) < ① ulMaxCounte . Especially, if the value of ulInitCounter is 0, it can be used for synchronization between multiple threads. A counting semaphore not used can be deleted via calling the following functions. The deleted semaphore system automatically recovers the occupied system resources (unknown errors will occur when trying to use the counting semaphore deleted). #include ULONG Lw_SemaphoreC_Delete(LW_HANDLE *pulId); Prototype analysis of Function Lw_SemaphoreC_Delete: For success of the function, return 0. For failure, return the error number; Parameter pulId is the handle of counting semaphore. If the thread needs to wait for a counted semaphore, the Lw_SemaphoreC_Wait function can be called. It might be noted that the interrupt service routine cannot call the Lw_SemaphoreC_Wait function to wait for a counting semaphore, because the Manual OS. SpaceChain 226 Application Development Manual SpaceChain OS Lw_SemaphoreC_Wait will block the current thread when the value of counting semaphore is 0 (thread synchronization function). #include ULONG Lw_SemaphoreC_Wait(LW_HANDLE ulId, ULONG ULONG ulTimeout); Lw_SemaphoreC_TryWait(LW_HANDLE ulId); Prototype analysis of above two functions: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of counting semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick. Difference between Lw_SemaphoreC_TryWait and Lw_SemaphoreC_Wait is that if the current value of counting semaphore is FALSE, Lw_SemaphoreC_TryWait will immediately exit and return ERROR_THREAD_WAIT_TIMEOUT, while Lw_SemaphoreC_Wait will be blocked until awakened. The interrupt service routine can use the Lw_SemaphoreC_TryWait function to try to wait for counting semaphore, because the Lw_SemaphoreC_TryWait function will immediately return when the value of counting semaphore is FALSE, and the current thread will not be blocked. Releasing a counting semaphore can call the Lw_SemaphoreC_Post function. #include ULONG Lw_SemaphoreC_Post(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreC_Post: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of counting semaphore. Releasing multiple counting Lw_SemaphoreC_Release function. semaphores at a time can call the #include ULONG Lw_SemaphoreC_Release(LW_HANDLE ulId, ULONG ulReleaseCounter, ULONG *pulPreviousCounter); Prototype analysis of Function Lw_SemaphoreC_Release: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of counting semaphore; Parameter ulReleaseCounter is the release counter of counting semaphore; Manual OS. SpaceChain 227 Application Development Manual SpaceChain OS Output parameter pulPreviousCounter is used to receive original semaphore counter, which can be NULL. Lw_SemaphoreC_Release is an advanced API, and POSIX read-write lock can call the function to release multiple read-write threads simultaneously (for POSIX read-write lock, see Section 7.8). Figure 7.6 shows the operation process of the basic operation function of counting semaphore between threads and between interrupt and thread. (线程=thread) (中断=break off) Figure 7.6 SylixOS counting semaphore Calling the Lw_SemaphoreC_Clear function will clear counting semaphore, so that the initial value of counting semaphore will be set as 0. #include ULONG Lw_SemaphoreC_Clear(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreC_Clear: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the handle of counting semaphore; Calling Lw_SemaphoreC_Flush will release all threads waiting on the appointed counting semaphore. #include ULONG Lw_SemaphoreC_Flush(LW_HANDLE ULONG ulId, *pulThreadUnblockNum); Prototype analysis of Function Lw_SemaphoreC_Flush: Manual OS. SpaceChain 228 Application Development Manual SpaceChain OS For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the handle of counting semaphore; Output parameter pulThreadUnblockNum is used to receive the number of unblocked threads, which can be NULL. The following two functions can get state information of the appointed counting semaphore. #include ULONG ULONG Lw_SemaphoreC_Status(LW_HANDLE ulId, ULONG *pulCounter, ULONG *pulOption, ULONG *pulThreadBlockNum); Lw_SemaphoreC_StatusEx(LW_HANDLE ulId, ULONG *pulCounter, ULONG *pulOption, ULONG *pulThreadBlockNum, ULONG *pulMaxCounter); Prototype analysis of above two functions: Above two functions return the error number; Parameter ulId is the handle of counting semaphore; Output parameter pulCounter is used to receive the current value of counting semaphore; Output parameter pulOption is used to receive creation options of counting semaphore; Output parameter pulThreadBlockNum is used to receive the number of the threat currently blocked in counting semaphore. Output parameter pulMaxCounter is used to receive the maximum counted value of the counting semaphore. The Lw_SemaphoreC_GetName function can get the name of the appointed counting semaphore. #include ULONG Lw_SemaphoreC_GetName(LW_HANDLE ulId, PCHAR pcName); Prototype analysis of Function Lw_SemaphoreC_GetName: The function returns the error number; Parameter ulId is the handle of counting semaphore; Manual OS. SpaceChain 229 Application Development Manual SpaceChain OS Output parameter pcName is the name of the counting semaphore, and pcName shall be pointed at a character array with size of LW_CFG_OBJECT_NAME_SIZE. The following program shows how to use SylixOS counting semaphore, and the program creates two threads and a SylixOS counting semaphore; counting semaphore is taken as the remaining count of resources, the initial number of resources is 5, and the maximum number is 100; Thread A is the consumer of the resource, and Thread B is the producer of the resource. Program List 7.2 How to use SylixOS counting semaphore #include static LW_HANDLE _G_hResCntSema; static PVOID tTestA (PVOID pvArg) { INT iError; while (1) { iError = Lw_SemaphoreC_Wait(_G_hResCntSema, LW_OPTION_WAIT_INFINITE); if (iError != ERROR_NONE) { break; } printf("tTestA(): get a resource\n"); } return (LW_NULL); } static PVOID tTestB (PVOID pvArg) { while (1) { Lw_Time_SSleep(1); Lw_SemaphoreC_Post(_G_hResCntSema); } return (LW_NULL); } int main (int argc, char *argv[]) { Manual OS. SpaceChain 230 Application Development Manual SpaceChain OS LW_HANDLE LW_HANDLE hThreadAId; hThreadBId; _G_hResCntSema = Lw_SemaphoreC_Create("res_sema", 5, 100, LW_OPTION_WAIT_FIFO | LW_OPTION_OBJECT_LOCAL, LW_NULL); if (_G_hResCntSema == LW_OBJECT_HANDLE_INVALID) { printf("semaphore create failed.\n"); return (-1); } hThreadAId = Lw_Thread_Create("t_testa", tTestA, LW_NULL, LW_NULL); if (hThreadAId == LW_OBJECT_HANDLE_INVALID) { printf("t_testa create failed.\n"); return (-1); } hThreadBId = Lw_Thread_Create("t_testb", tTestB, LW_NULL, LW_NULL); if (hThreadBId == LW_OBJECT_HANDLE_INVALID) { printf("t_testb create failed.\n"); return (-1); } Lw_Thread_Join(hThreadAId, LW_NULL); Lw_Thread_Join(hThreadBId, LW_NULL); Lw_SemaphoreC_Delete(&_G_hResCntSema); return (0); } Run the program under the SylixOS Shell: # ./SemaphoreC tTestA(): get a resource tTestA(): get a resource tTestA(): get a resource tTestA(): get a resource tTestA(): get a resource tTestA(): get a resource Manual OS. SpaceChain 231 Application Development Manual SpaceChain OS 7.3.3 Mutex semaphore During introduction to binary semaphore, it was discussed that if the bInitValue parameter is set as TRUE when binary semaphore is created, it can be used for mutex access of shared resources. Actually, mutex implemented by SylixOS binary semaphore is to initialize and mark a variable as 1, and reduce the variable by 1 (equal to 0 at the moment) at semaphore waiting (Wait). If another thread waits again, the semaphore will be blocked until released (variable added by 1), so that mutex access of shared resources is implemented. If there are only two threads in the system, the above process has no problem. However, once multiple threads are involved, the above process will have the following problems: A high-priority thread may also have access to the same shared resource (it is entirely possible). At the moment, only blocking wait can be performed. However, another thread with medium priority will control the thread occupying semaphore. The process causes failure in running high-priority thread for a long time (the situation is not allowed in SylixOS). The problem in above process is the classic priority inversion, which will be continuously discussed in Section 7.5. Mutex semaphore is used at the place where mutex access of shared resources, it can be understood as the binary semaphore with priority ceiling and priority inheritance mechanism (intended to solve the priority inversion problem) with an initial value of TRUE, and only the thread with mutex semaphore has the right to release mutex semaphore. Note: Mutex semaphore needs to record the owner thread and adjust the priority, but the interrupt priority cannot be modified. Therefore, the interrupt service routine cannot operate mutex semaphore. The following pseudocode fragment shows the process to use mutex semaphore. Define global shared resources (_G_shared) void *thread (void *) { Wait for mutex(Wait) Operation to shared resources (_G_shared) Release the mutex (Post) Thread exit (Exit) Manual OS. SpaceChain 232 Application Development Manual SpaceChain OS } void main_func (void) { Define mutex handler (semM) Create mutex (Create) Create thread (thread) Join the thread (join) Delete mutex (Delete) } A SylixOS mutex semaphore can be used after creation by calling the Lw_SemaphoreM_Create function, and the function will return a handle of mutex semaphore after successful creation. #include LW_HANDLE Lw_SemaphoreM_Create(CPCHAR pcName, UINT8 ucCeilingPriority, ULONG ulOption, LW_OBJECT_ID *pulId); Prototype analysis of Function Lw_SemaphoreM_Create: For success of the function, return the handle of the mutex semaphore. For failure, return NULL and set the error number; Parameter pcName is the name of mutex semaphore; Parameter ucCeilingPriority is valid when priority ceiling algorithm is used, and the parameter is the ceiling priority; Parameter ulOption is the creation option of mutex semaphore; Output parameter pulld returns the handle of mutex semaphore (same with the return value), which can be NULL. Create options include the create option of the binary signal. In addition, the unique create option of mutex semaphore shown in Table 7.4 can also be used. Table 7.4 Creation option of mutex semaphore Macro name Meaning Manual OS. SpaceChain 233 Application Development Manual SpaceChain OS LW_OPTION_INHERIT_PRIORITY Priority inheritance algorithm LW_OPTION_PRIORITY_CEILING Priority ceiling algorithm LW_OPTION_NORMAL Not checked during recursive locking (not recommended) LW_OPTION_ERRORCHECK Report errors during recursive locking LW_OPTION_RECURSIVE Support recursive locking It might be noted either LW_OPTION_INHERIT_PRIORITY or LW_OPTION_PRIORITY_CEILING can be selected. Likewise, only one of LW_OPTION_NORMAL, LW_OPTION_ERRORCHECK and LW_OPTION_RECURSIVE can be selected. A mutex semaphore not used can be deleted by calling the following functions. The semaphore system deleted will automatically recover system resources occupied (unknown errors will occur when trying to use the deleted mutex semaphore). #include ULONG Lw_SemaphoreM_Delete(LW_HANDLE *pulId); Prototype analysis of Function Lw_SemaphoreM_Delete: The function returns the error number; Parameter pulId is the handle of mutex semaphore; If the thread needs to wait for a mutex semaphore, the Lw_SemaphoreM_Wait function can be called Releasing a mutex semaphore can call the Lw_SemaphoreM_Post function. #include ULONG Lw_SemaphoreM_Wait(LW_HANDLE ULONG ulId, ulTimeout); Prototype analysis of Function Lw_SemaphoreM_Wait: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of mutex semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick. #include ULONG Lw_SemaphoreM_Post(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreM_Post: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of mutex semaphore; Manual OS. SpaceChain 234 Application Development Manual SpaceChain OS It might be noted that only the owner can release mutex semaphore Figure 7.7 shows the operation process of the basic operation function of mutex semaphore in the thread. (线程=thread) Figure 7.7 SylixOS mutex semaphore The following functions can get state information of mutex semaphore. #include ULONG ULONG Lw_SemaphoreM_Status(LW_HANDLE ulId, BOOL *pbValue, ULONG *pulOption, ULONG *pulThreadBlockNum); Lw_SemaphoreM_StatusEx(LW_HANDLE ulId, BOOL *pbValue, ULONG *pulOption, ULONG *pulThreadBlockNum, LW_HANDLE *pulOwnerId); Prototype analysis of above two functions: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of mutex semaphore; Output parameter pbValue is used to receive the current state of mutex semaphore; Output parameter pulOption is used to receive creation option of mutex semaphore; Output parameter pulThreadBlockNum is used to receive the number of threads currently blocked at the mutex semaphore. Output parameter pulOwnerId is used to receive the handle of the thread currently owning the mutex semaphore. Manual OS. SpaceChain 235 Application Development Manual SpaceChain OS If you want to get the name of a mutex semaphore, the following functions can be called. #include ULONG Lw_SemaphoreM_GetName(LW_HANDLE ulId, PCHAR pcName); Prototype analysis of Function Lw_SemaphoreM_GetName: For success of the function, return 0. For failure, return the error number; Parameter ulId is the handle of mutex semaphore; Output parameter pcName is the name of the mutex semaphore, and pcName shall be pointed at a character array with size of LW_CFG_OBJECT_NAME_SIZE. The following program shows how to use SylixOS mutex semaphore, the program creates two threads with different priorities and a SylixOS mutex semaphore, two threads perform auto increment operation and printing for Variable _G_iCount respectively, and SylixOS mutex semaphore is used as mutex means of Variable _G_iCount. Where, the mutex semaphore uses priority inheritance algorithm. Program List 7.3 How to use SylixOS counting mutex semaphore #include static LW_HANDLE _G_hLock; static INT _G_iCount = 0; static PVOID tTestA (PVOID pvArg) { INT iError; while (1) { iError = Lw_SemaphoreM_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE); if (iError != ERROR_NONE) { break; } _G_iCount++; printf("tTestA(): count = %d\n", _G_iCount); Lw_SemaphoreM_Post(_G_hLock); Lw_Time_MSleep(500); } return (LW_NULL); Manual OS. SpaceChain 236 Application Development Manual SpaceChain OS } static PVOID tTestB (PVOID pvArg) { INT iError; while (1) { iError = Lw_SemaphoreM_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE); if (iError != ERROR_NONE) { break; } _G_iCount++; printf("tTestB(): count = %d\n", _G_iCount); Lw_SemaphoreM_Post(_G_hLock); Lw_Time_MSleep(500); } return (LW_NULL); } int main (int argc, char *argv[]) { LW_CLASS_THREADATTR threadattr; LW_HANDLE hThreadAId; LW_HANDLE hThreadBId; _G_hLock = Lw_SemaphoreM_Create("count_lock", LW_PRIO_HIGH, LW_OPTION_WAIT_FIFO | LW_OPTION_OBJECT_LOCAL| LW_OPTION_INHERIT_PRIORITY | LW_OPTION_ERRORCHECK, LW_NULL); if (_G_hLock == LW_OBJECT_HANDLE_INVALID) { printf("mutex create failed.\n"); return (-1); } Lw_ThreadAttr_Build(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL - 1, Manual OS. SpaceChain 237 Application Development Manual SpaceChain OS LW_OPTION_THREAD_STK_CHK, LW_NULL); hThreadAId = Lw_Thread_Create("t_testa", tTestA, &threadattr, LW_NULL); if (hThreadAId == LW_OBJECT_HANDLE_INVALID) { printf("t_testa create failed.\n"); return (-1); } Lw_ThreadAttr_Build(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); hThreadBId = Lw_Thread_Create("t_testb", tTestB, &threadattr, LW_NULL); if (hThreadBId == LW_OBJECT_HANDLE_INVALID) { printf("t_testb create failed.\n"); return (-1); } Lw_Thread_Join(hThreadAId, LW_NULL); Lw_Thread_Join(hThreadBId, LW_NULL); Lw_SemaphoreM_Delete(&_G_hLock); return (0); } Run the program under the SylixOS Shell: # ./SemaphoreM tTestA(): count = 1 tTestB(): count = 2 tTestA(): count = 3 tTestB(): count = 4 tTestA(): count = 5 tTestB(): count = 6 7.3.4 Read-write semaphore As introduced in 7.8 POSIX read-write lock, when there are multiple readers and single writer, purely using mutex semaphore will greatly weaken the processing performance of the multi-threaded operating system. In order to satisfy requirements for Manual OS. SpaceChain 238 Application Development Manual SpaceChain OS high concurrency processing speed, SylixOS introduces read-write semaphore, and its application scenario is similar to POSIX read-write lock. SylixOS read-write semaphore satisfies the principle of write priority, that is to say, if the write semaphore has existed, the read semaphore cannot be applied until the write semaphore is released. However, when the read semaphore has existed, the read semaphore may be requested again. The mechanism satisfies high concurrency of read. A SylixOS read-write semaphore can be used after creation by calling the Lw_SemaphoreRW_Create function, and the function will return a handle of read-write semaphore after successful creation. #include LW_HANDLE Lw_SemaphoreRW_Create(CPCHAR ULONG pcName, ulOption, LW_OBJECT_ID *pulId); Prototype analysis of Function Lw_SemaphoreRW_Create: For success of the function, return the handle of the read-write semaphore. For failure, return NULL and set the error number; Parameter pcName is the name of read-write semaphore; Parameter ulOption is creation option of read-write semaphore; Output parameter pulld returns the handle of read-write semaphore (same with the return value), which can be NULL. A read-write semaphore not used can be deleted by calling the following functions. The semaphore system deleted will automatically recover system resources occupied (unknown errors will occur when trying to use the deleted read-write semaphore). #include ULONG Lw_SemaphoreRW_Delete(LW_HANDLE *pulId); Prototype analysis of Function Lw_SemaphoreRW_Delete: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter pulld is the handle of read-write semaphore. If the thread needs to wait for a read semaphore, the Lw_SemaphoreRW_RWait function can be called. If the thread needs to wait for a write semaphore, the Lw_SemaphoreRW_WWait can be called. Releasing a read-write semaphore can call the Lw_SemaphoreRW_Post function. #include ULONG Lw_SemaphoreRW_RWait(LW_HANDLE ulId, Manual OS. SpaceChain 239 Application Development Manual SpaceChain OS ULONG ulTimeout); Prototype analysis of Function Lw_SemaphoreRW_RWait: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the handle of read semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick. #include ULONG Lw_SemaphoreRW_WWait(LW_HANDLE ulId, ULONG ulTimeout); Prototype analysis of Function Lw_SemaphoreRW_WWait: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the handle of write semaphore; Parameter ulTimeout is the waiting timeout, and the unit is Tick. #include ULONG Lw_SemaphoreRW_Post(LW_HANDLE ulId); Prototype analysis of Function Lw_SemaphoreRW_Post: For success of the function, return ERROR_NONE. For failure, return the error number; Parameter ulId is the handle of read-write semaphore; It might be noted that only the owner of read-write semaphore can release the read-write semaphore. Calling the following functions can get detailed information of read-write semaphore: #include ULONG Lw_SemaphoreRW_Status(LW_OBJECT_HANDLE ulId, ULONG *pulRWCount, ULONG *pulRPend, ULONG *pulWPend, ULONG *pulOption, LW_OBJECT_HANDLE *pulOwnerId); Prototype analysis of Function Lw_SemaphoreRW_Status: For success of the function, return ERROR_NONE. For failure, return the error number; Manual OS. SpaceChain 240 Application Development Manual SpaceChain OS Parameter ulId is the handle of read-write semaphore; Parameter pulRWCount returns the number of threads which are performing concurrent operation of read-write semaphore, and the parameter can be NULL. Parameter pulRPend returns the number of blocks of current read operation, and the parameter can be NULL. Parameter pulWPend returns the number of blocks of current write operation, and the parameter can be NULL. Parameter pulOption returns option information of current read-write semaphore, and the parameter can be NULL. Parameter pulOwnerId returns the owner ID of current write semaphore, and the parameter can be NULL. 7.4 POSIX semaphore There are two types of POSIX semaphore: anonymous semaphore and named semaphore. The anonymous semaphore only exists in memory, which requires that the thread using semaphore can have access to memory. Therefore, the anonymous semaphore can be applied for inter-thread communication in the same process, and the memory shall be mapped to the address space between different processes. Named semaphores can be accessed via name and can therefore be applied for interprocess communication (see Chapter 9 Interprocess Communication). The essence of the POSIX semaphore is the counting semaphore. The POSIX semaphore is defined as the sem_t type, and the variable of the sem_t type shall be defined before use. For example: sem_t sem; 7.4.1 POSIX anonymous semaphore A POSIX anonymous semaphore can be used after creation by calling the sem_init function. #include int sem_init(sem_t *psem, int pshared, unsigned int value); Prototype analysis of Function sem_init: For success of the function, return 0. For failure, return -1 and set the error number; Output parameter psem returns the pointer of POSIX semaphore; Manual OS. SpaceChain 241 Application Development Manual SpaceChain OS Parameter pshared identifies whether the POSIX semaphore is shared by the process (SylixOS does not use the item); Parameter value is the initial value of the POSIX semaphore. After a POSIX anonymous semaphore is used (and it is no longer used in the future), it shall be deleted by calling the sem_destroy function, and SylixOS will recover kernel resources occupied by the semaphore. #include int sem_destroy(sem_t *psem); Prototype analysis of Function sem_destroy: The function returns the error number; Parameter psem is the pointer of POSIX semaphore; If the thread needs to wait for a POSIX semaphore, the sem_wait function can be called, and the interrupt service routine cannot call any API of the POSIX semaphore. Release a semaphore to use the sem_post function. #include int sem_wait(sem_t *psem); int sem_trywait(sem_t *psem); int sem_timedwait(sem_t *psem, const struct timespec *abs_timeout); int sem_reltimedwait_np(sem_t *psem, const struct timespec *rel_timeout); Prototype analysis of above several functions: For success of the function, return 0. For failure, return -1 and set the error number; Parameter psem is the pointer of POSIX semaphore; Parameter timeout is the waiting absolute timeout; Parameter rel_timeout is the waiting relative timeout. Sem_trywait is the "try wait" version of sem_wait. When the value of the POSIX semaphore is 0, sem_wait will be blocked until awakened, while sem_trywait will return immediately. The sem_timedwait is the version of the sem_wait with wait timeout, and the timeout is the absolute wait timeout. The absolute timeout can be got by adding a relative timeout based on the current time during use. For example: struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 1; Manual OS. SpaceChain 242 Application Development Manual SpaceChain OS sem_timedwait(&sem, &ts); Sem_reltimedwait_np is the non-POSIX standard version of sem_timedwait, and Parameter rel_timeout is relative wait timeout. For example: struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; sem_reltimedwait_np(&sem, &ts); It can be seen that the sem_reltimedwait_np is more convenient than the sem_timedwait in use. #include int sem_post(sem_t *psem); Prototype analysis of Function sem_post: For success of the function, return 0. For failure, return -1 and set the error number; Parameter psem is the pointer of POSIX semaphore; Figure 7.8 shows the operation process of the basic operation function of the anonymous semaphore in the thread. (线程=thread) Figure 7.8 POSIX anonymous semaphore The sem_getvalue function can be used to retrieve the semaphore value. It might be noted that when we try to use the value just read out, the semaphore value may have changed. It is not suggested to use the sem_getvalue function unless other synchronization mechanisms are used to avoid this competition. #include int sem_getvalue(sem_t *psem, int *pivalue); Prototype analysis of Function sem_getvalue: Manual OS. SpaceChain 243 Application Development Manual SpaceChain OS For success of the function, return 0. For failure, return -1 and set the error number; Parameter psem is the pointer of POSIX semaphore; Output parameter pivalue is used to receive the current counted value of the POSIX semaphore. The following program shows how to use POSIX semaphore, the program creates two threads and a POSIX semaphore, two threads perform auto increment operation and printing for Variable count respectively, and POSIX semaphore is used as mutex means of the access variable count. Program List 7.4 Use of POSIX semaphore #include #include #include static sem_t lock; static int count = 0; static void *thread_a (void *arg) { while (1) { sem_wait(&lock); count++; printf("thread_a(): count = %d\n", count); sem_post(&lock); usleep(500 * 1000); } return (NULL); } static void *thread_b (void *arg) { while (1) { sem_wait(&lock); count++; printf("thread_b(): count = %d\n", count); sem_post(&lock); usleep(500 * 1000); } return (NULL); } Manual OS. SpaceChain 244 Application Development Manual SpaceChain OS int main (int argc, char *argv[]) { pthread_t threada_tid; pthread_t threadb_tid; int ret; ret = sem_init(&lock, 1, 1); if (ret != 0) { fprintf(stderr, "semaphore create failed.\n"); return (-1); } ret = pthread_create(&threada_tid, NULL, thread_a, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_create(&threadb_tid, NULL, thread_b, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } pthread_join(threada_tid, NULL); pthread_join(threadb_tid, NULL); sem_destroy(&lock); return (0); } Run the program under the SylixOS Shell: # ./posix_sema thread_a(): count = 1 thread_b(): count = 2 thread_a(): count = 3 thread_b(): count = 4 thread_a(): count = 5 Manual OS. SpaceChain 245 Application Development Manual SpaceChain OS 7.5 Priority inversion 7.5.1 What's priority inversion We present an example of shared resource competition above: two threads shall perform increment operation of the same variable V (initial value of 0) simultaneously. The solution to shared resource competition is to add a lock. The lock is occupied before access to variable V, and released after access. In general, we can use THE binary semaphore with the initial value of TRUE or the counting semaphore with the initial value of 1 as the lock. Now we will slightly change the example. There are three threads (thread A, thread B and thread C) and a variable V, and thread A and thread B shall have access to Variable V simultaneously. Obviously, we need a lock (i.e., Lock L of the protected variable V). The priorities of Thread A, thread B and thread C are 1, 2 and 3 respectively, i.e., priority: thread A> thread C> thread B. We assume that now thread A and thread C are at the blocking state, and thread B is at running state. Thread B occupies lock L (#1 in Figure 7.9). At the moment, the event waited by Thread C enters the ready state. The priority of Thread C is higher than that of thread B, and thread C will preempt execution of Thread B (#2 in Figure 7.9); at the moment, the event waited by Thread A enters the ready state. The priority of Thread A is higher than that of thread C, and thread A will preempt execution of Thread C (#3 in Figure 7.9); At the moment, Thread A also applies for lock L. Lock L has been occupied by Thread B, Thread A must block and wait for Lock L to be released by Thread B, while Thread C with medium priority will continue to run (Figure 7.9 #4), At the moment, the running statuses of 3 threads are as follows: thread A blocked, thread C running normally, and thread B blocked, which obviously violates the real-time principle of RTOS. (线程=thread) Figure 7.9 Priority inversion When a high-priority thread has access to shared resources via the semaphore mechanism, the semaphore has been occupied by a low-priority thread. However, the low-priority thread may be occupied by other medium-priority threads when access to Manual OS. SpaceChain 246 Application Development Manual SpaceChain OS shared resources. Therefore, high-priority threads are blocked by many threads with lower priority, and we call the phenomenon priority inversion. 7.5.2 How to solve priority inversion There are two ways to solve the priority inversion problem: priority ceiling and priority inheritance (supported by SylixOS mutex semaphore simultaneously). The priority ceiling is to raise the priority of a thread to the highest priority of all threads which can have access to the resource when applying for a certain shared resource. The priority is called the priority ceiling of the resource. The method is simple and easy to implement, without complicated judgment. Even if the thread blocks running of the high-priority thread, the priority of the thread will be promoted as long as the thread has access to shared resources. Priority inheritance is shown as follows: when Thread A applies for the shared resource V which is being used by Thread B, if it is found that the priority of Thread B is lower than its own priority through comparison, the priority of Thread B is promoted to its own priority, and recovered to the original priority after Thread B releases the shared resource V. The method dynamically changes the priority of the thread only when the low-priority thread occupying the resource blocks the high-priority thread. Both binary semaphore and counting semaphore do not support priority ceiling and priority inheritance, and only mutex semaphore supports priority ceiling and priority inheritance. 7.6 POSIX mutex semaphore POSIX mutex semaphore is the “POSIX” interface function performing mutex access to shared resources, and the function implemented is same with that of SylixOS semaphore. The type of POSIX mutex semaphore is pthread_mutex_t. A variable of pthread_mutex_t type shall be defined during use. For example: pthread_mutex_t mutex; A POSIX mutex semaphore must be initialized firstly before use. The initial value of the mutex can be set as PTHREAD_MUTEX_INITIALIZER (static initialization), and dynamic initialization can also be performed by calling the pthread_mutex_init function. If the thread needs to wait for a mutex semaphore, the pthread_mutex_lock function can be called. Release a mutex semaphore to use the pthread_mutex_unlock function. A mutex semaphore will be deleted by calling the pthread_mutex_destroy function after use, and SylixOS will recover kernel resources occupied by the mutex semaphore. It Manual OS. SpaceChain 247 Application Development Manual SpaceChain OS might be noted that unknown errors will occur if a deleted semaphore is tried to be used again. (线程=thread) Figure 7.10 POSIX mutex semaphore The thread has attribute objects, SPOSIX mutex semaphore has its own attribute objects, and attribute blocks of POSIX mutex semaphore shall be used to create a POSIX mutex semaphore. The type of POSIX mutex semaphore attribute block is pthread_mutexattr_t. A variable of pthread_mutexattr_t type shall be defined during use. For example: pthread_mutexattr_t mutexattr; 7.6.1 Mutex semaphore attribute block 1. Initialization and deletion of the mutex semaphore attribute block #include int pthread_mutexattr_init(pthread_mutexattr_t *pmutexattr); Prototype analysis of Function pthread_mutexattr_init: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block. #include int pthread_mutexattr_destroy(pthread_mutexattr_t *pmutexattr); Prototype analysis of Function pthread_mutexattr_destroy: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block. 2. Set and get the type of mutex semaphore attribute block #include Manual OS. SpaceChain 248 Application Development Manual SpaceChain OS int pthread_mutexattr_settype(pthread_mutexattr_t *pmutexattr, int type); Prototype analysis of Function pthread_mutexattr_settype: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Parameter type is the type of POSIX mutex semaphore attribute block. The type of mutex semaphore attribute block can use the macro shown in Table 7.5. Table 7.5 Type of mutex semaphore attribute block Macro name Meaning PTHREAD_MUTEX_NORMAL Generate deadlock at recursive locking PTHREAD_MUTEX_ERRORCHECK Return error at recursive locking PTHREAD_MUTEX_RECURSIVE Support recursive locking PTHREAD_MUTEX_FAST_NP Equivalent to PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_ERRORCHECK_NP Equivalent to PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_RECURSIVE_NP Equivalent to PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_DEFAULT Equivalent to PTHREAD_MUTEX_RECURSIVE #include int pthread_mutexattr_gettype(const pthread_mutexattr_t int *pmutexattr, *type); Prototype analysis of Function pthread_mutexattr_gettype: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Output parameter type is the type of mutex semaphore attribute block. 3. Set and get algorithm type of the mutex semaphore attribute block #include int pthread_mutexattr_setprotocol(pthread_mutexattr_t *pmutexattr, int protocol); Prototype analysis of Function pthread_mutexattr_setprotocol: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Parameter protocol is the algorithm type of POSIX mutex semaphore attribute block. Manual OS. SpaceChain 249 Application Development Manual SpaceChain OS The type of mutex semaphore attribute block can use the macro shown in Table 7.6. Table 7.6 Algorithm type of the mutex semaphore attribute block Macro name Meaning PTHREAD_PRIO_NONE Priority inheritance algorithm, wait in FIFO order PTHREAD_PRIO_INHERIT Priority inheritance algorithm, wait in priority order PTHREAD_PRIO_PROTECT Priority ceiling #include int pthread_mutexattr_getprotocol( const pthread_mutexattr_t *pmutexattr, int protocol ); Prototype analysis of Function pthread_mutexattr_getprotocol: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Parameter protocol is the algorithm type of POSIX mutex semaphore attribute block. 4. Set and get the ceiling priority of mutex semaphore attribute block. #include int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *pmutexattr, int prioceiling); Prototype analysis of Function pthread_mutexattr_setprioceiling: For success of the function, return 0. For failure, return the error number; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Parameter prioceiling is the ceiling priority of POSIX mutex semaphore attribute block. #include int pthread_mutexattr_getprioceiling( const pthread_mutexattr_t *pmutexattr, int *prioceiling ); Prototype analysis of Function pthread_mutexattr_setprioceiling: For success of the function, return 0. For failure, return the error number; Manual OS. SpaceChain 250 Application Development Manual SpaceChain OS Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Output parameter prioceiling is the ceiling priority of POSIX mutex semaphore attribute block. 5. Set and get process shared attributes of the mutex semaphore attribute block #include int pthread_mutexattr_setpshared(pthread_mutexattr_t *pmutexattr, int pshared); Prototype analysis of Function pthread_mutexattr_setpshared①: The function returns 0; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Parameter pshared identifies whether the POSIX mutex semaphore attribute block is shared by processes. Process sharing parameters can use the macro shown in Table 7.7. Table 7.7 Parameter shared by processes Macro name Meaning PTHREAD_PROCESS_SHARED Process share PTHREAD_PROCESS_PRIVATE Process private #include int pthread_mutexattr_getpshared(const pthread_mutexattr_t *pmutexattr, int *pshared); Prototype analysis of Function pthread_mutexattr_getpshared: The function returns 0; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block; Output parameter pshared identifies whether the POSIX mutex semaphore attribute block is shared by processes. It might be noted that SylixOS (PTHREAD_PROCESS_PRIVATE). is always private to the process 7.6.2 Mutex semaphore 1. Initialization and deletion of the mutex semaphore Manual OS. SpaceChain 251 Application Development Manual SpaceChain OS #include int pthread_mutex_init(pthread_mutex_t *pmutex, const pthread_mutexattr_t *pmutexattr); Prototype analysis of Function pthread_mutex_init: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore; Parameter pmutexattr is the pointer of POSIX mutex semaphore attribute block, which can be NULL; #include int pthread_mutex_destroy(pthread_mutex_t *pmutex); Prototype analysis of Function pthread_mutex_destroy: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore; 2. Wait of mutex semaphore #include int pthread_mutex_lock(pthread_mutex_t *pmutex); int pthread_mutex_trylock(pthread_mutex_t *pmutex); int pthread_mutex_timedlock(pthread_mutex_t int pthread_mutex_reltimedlock_np(pthread_mutex_t *pmutex, const struct timespec *abs_timeout); const struct timespec *pmutex, *rel_timeout); Prototype analysis of above functions: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore; Parameter abs_timeout is the waiting absolute timeout; Parameter rel_timeout is the waiting relative timeout. The pthread_mutex_trylock is the "try wait" version of pthread_mutex_lock. When POSIX mutex semaphore has been occupied, the pthread_mutex_lock will be blocked until awakened, while the pthread_mutex_trylock will return immediately. The pthread_mutex_timedlock is the version of the pthread_mutex_lock with wait timeout, and the abs_timeout is the absolute wait timeout. The absolute timeout can be got by adding a relative timeout based on the current time during use. The pthread_mutex_reltimedlock_np is the non-POSIX standard version of the pthread_mutex_timedlock, and Parameter rel_timeout is relative wait timeout. Manual OS. SpaceChain 252 Application Development Manual SpaceChain OS 3. Release of mutex semaphore #include int pthread_mutex_unlock(pthread_mutex_t *pmutex); Prototype analysis of Function pthread_mutex_unlock: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore. 4. Set and get the ceiling priority of mutex semaphore. #include int pthread_mutex_setprioceiling(pthread_mutex_t *pmutex, int prioceiling); Prototype analysis of Function pthread_mutex_setprioceiling: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore; Parameter prioceiling is the ceiling priority of POSIX mutex semaphore. #include int pthread_mutex_getprioceiling(pthread_mutex_t *pmutex, int *prioceiling); Prototype analysis of Function pthread_mutex_getprioceiling: For success of the function, return 0. For failure, return the error number; Parameter pmutex is the pointer of POSIX mutex semaphore; Output parameter prioceiling is the ceiling priority of POSIX mutex semaphore. The following program shows how to use POSIX mutex semaphore, the program creates two threads and a POSIX mutex semaphore, two threads perform auto increment operation and printing for Variable count respectively, and POSIX mutex semaphore is used as mutex for access to Variable count. Where, the mutex semaphore uses priority inheritance algorithm, and wait in priority order. Program List 7.5 Use of POSIX mutex semaphore #include #include static pthread_mutex_t lock; static int count = 0; static void *thread_a (void *arg) Manual OS. SpaceChain 253 Application Development Manual SpaceChain OS { while (1) { pthread_mutex_lock(&lock); count++; printf("thread_a(): count = %d\n", count); pthread_mutex_unlock(&lock); usleep(500 * 1000); } return (NULL); } static void *thread_b (void *arg) { while (1) { pthread_mutex_lock(&lock); count++; printf("thread_b(): count = %d\n", count); pthread_mutex_unlock(&lock); usleep(500 * 1000); } return (NULL); } int main (int argc, char *argv[]) { pthread_mutexattr_t mutexattr; pthread_t threada_tid; pthread_t threadb_tid; int ret; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); pthread_mutexattr_setprotocol(&mutexattr, PTHREAD_PRIO_INHERIT); ret = pthread_mutex_init(&lock, &mutexattr); if (ret != 0) { fprintf(stderr, "mutex create failed.\n"); return (-1); } pthread_mutexattr_destroy(&mutexattr); ret = pthread_create(&threada_tid, NULL, thread_a, NULL); Manual OS. SpaceChain 254 Application Development Manual SpaceChain OS if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } ret = pthread_create(&threadb_tid, NULL, thread_b, NULL); if (ret != 0) { fprintf(stderr, "pthread create failed.\n"); return (-1); } pthread_join(threada_tid, NULL); pthread_join(threadb_tid, NULL); pthread_mutex_destroy(&lock); return (0); } Run the program under the SylixOS Shell: # ./posix_mutex thread_a(): count = 1 thread_b(): count = 2 thread_a(): count = 3 thread_b(): count = 4 thread_a(): count = 5 thread_b(): count = 6 The above program sets the type of mutex attribute as PTHREAD_MUTEX_NORMAL, which will not check recursive locking (a possible recursive locking as shown in the Figure 7.11), and deadlock will occur in case of recursive locking as shown in the following figure (we will discuss deadlock concept in details in Section 7.7). Therefore, it is not suggested to use the lock of the type in actual application, and SylixOS suggests to set the type of lock as PTHREAD_MUTEX_ERRORCHECK. The lock of the type will automatically check lock recursion when locked, and Error EDEADLK will be returned in case of lock recursion. Manual OS. SpaceChain 255 Application Development Manual SpaceChain OS (加锁=lock) (解锁=unlock) Figure 7.11 Recursive locking Program List can be simplified as the following pseudocode, which locks shared resources in different thread contexts. so as to well avoid recursive lock. thread_a () { lock(lock) count++ unlock(lock) } thread_b () { lock(lock) count++ unlock(lock) } main () { Create lock(lock) Create thread thread_a thread_b } Manual OS. SpaceChain 256 Application Development Manual SpaceChain OS 7.7 Deadlock 7.7.1 What's deadlock The so-called deadlock refers to the indefinite standoff situation where multiple threads circularly wait for resources occupied by other parties. Obviously, various threads involving deadlock will be at blocking state without external force. Just like two people passing the single-plank bridge, if both of them want to pass firstly, and refuse to retreat, deadlock will occur due to resource competition; however, if either of them goes on the bridge after checking that there is no person at the opposite side of the bridge, the problem will be solved. Figure 7.12 Deadlock 7.7.2 Generation conditions of deadlock If there are four necessary conditions in the computer system simultaneously, deadlock will occur. In other words, as long as one of the following four conditions not be provided, the system will not be deadlocked. Mutex conditions That is to say, a certain resource can only be occupied by a thread for a period, and cannot be occupied by two or more than two threads simultaneously. The exclusive resources such as CD-ROM driver and printer can only be occupied by other threads after actively released by the thread occupying the resources. It is determined by attribute of resources. For example, the single-plank bridge is a kind of exclusive resource, and people on both sides cannot cross the bridge simultaneously. Non-preemptive condition Before the resource got by the thread is used up, the resource applicant cannot forcibly seize the resource from the resource occupant, while can only be released independently by the occupant thread of the resource. If the person passing the single-plank bridge cannot force the person at the opposite side to retreat, or cannot illegally push the person at the opposite off the bridge, the person at the opposite side can pass the bridge after the person on the bridge passes the bridge and the deck is empty (i.e., the resources occupied are actively released). Manual OS. SpaceChain 257 Application Development Manual SpaceChain OS Possession and application conditions The thread has occupied a resource at least, while applies for the new resources; the resource has been occupied by other threads, and the thread is blocked at the moment; However, it still continue to occupy the resources occupied when waiting for the new resources. Take passing the single-plank bridge as an example again, A and B meet on the bridge. A walks through a section of deck (i.e., occupy some resources), and needs to walk the rest of deck) (apply for new resources). However, the part of deck has been occupied by B (B walks through a section of deck). A cannot move forward or backward; B is in the same condition. Circular wait conditions There is a thread wait sequence {P1,P2,...,Pn}. Where, P1 waits a certain resource occupied by P2, P2 waits a certain resource occupied by P3, ……, while Pn waits a certain resource occupied by P1, causing circular wait. Just like the above case of crossing single-wood bridge, A waits for the bridge occupied by B, while B waits for the bridge occupied by B, causing circular wait. The above four conditions we mentioned will occur simultaneously during deadlock. That is to say, deadlock will not occur as long as a necessary condition is not satisfied. 7.7.3 Deadlock prevention The four necessary conditions for deadlock are introduced above. As long as any of the four conditions is broken, the deadlock will not occur. This provides possibility to solve the deadlock problem. Generally, the method to solve the deadlock is divided into three kinds of deadlock prevention, avoidance, detection and recovery (note: deadlock detection and recovery is a method). Deadlock prevention is a strategy to guarantee that the system does not enter the deadlock state. Its basic thought is to require the thread to comply with certain protocols when applying for resources, so as to break one or more of the four necessary conditions causing deadlock, and guarantee that the system does not enter the deadlock state. Mutex breaking conditions That is to say, the thread is allowed to have access to some resources. However, some resources are not allowed to be accessed simultaneously, such as printers, etc. which is determined by the attribute of the resources themselves. Therefore, the approach has no practical value. Non-preemptive breaking conditions That is to say, the thread is allowed to forcibly seize some resources from the occupant. That is to say, when a thread has occupied some resources, and cannot be immediately satisfied when the new resource is applied, it must release all occupied Manual OS. SpaceChain 258 Application Development Manual SpaceChain OS resources, and reapply in the future. The resources released can be allocated to other threads. This means that the resources occupied by the thread are occupied snugly. It is difficult to implement the method of deadlock prevention, and the system performance will be reduced. Possession and application breaking conditions The resource pre-allocation strategy can be implemented. The thread applies for all necessary resources to the system at a time. If all resources required for a certain thread cannot be satisfied, any resource will not be allocated, and the thread does not run temporarily. Only when the system can satisfy all resource demands of the current thread, all resources applied can be allocated to the thread at a time. The running thread has occupied all necessary resources, and phenomena of simultaneous resource occupation and resource application will not occur. Therefore, deadlock will not occur. However, the strategy also has the following disadvantages: In many cases, a thread is impossible to know all resources required before execution. The thread is dynamic and unpredictable during execution; The resource utilization rate is low. No matter when the allocated resources are used, a thread can be executed only after all necessary resources are occupied. Even if some resources are used once by the thread finally, the thread has occupied them for the duration of its existence, causing long-term occupation without use. This is a great waste of resources apparently; Reduces concurrency of the thread. Because of limited resources and existed waste, the number of threads which can be allocated with all necessary resources is less inevitably. Break cycle wait conditions Implement resource allocation strategy in order. Adopting the strategy, the resources are classified and numbered in advance, and allocated by number, so that the thread will not form the loop when applying for and occupying resources. Request of all threads to resources must be proposed in ascending order of resource numbers. The thread can apply for large resources after small resources are occupied, and the loop will not be generated, so as to prevent deadlock. Compared with the previous strategy, the strategy has greatly improved resource utilization and system throughput. However, the following disadvantages are existed: Request of the thread to resources is restricted, it is difficult to reasonably number all resources in the system, and system overhead is increased. In order to comply with the number application order, the resources not used temporarily shall also be applied in advance, so that the duration when the thread occupies the resources is increased. Manual OS. SpaceChain 259 Application Development Manual SpaceChain OS SylixOS does not support avoidance, detection and recovery of deadlock, which can only be prevented. Generally, we use circular wait breaking conditions to prevent deadlock, and use timeout wait to dissolve deadlock simultaneously. However, it is required that the application has the perfect timeout error processing mechanism. 7.8 POSIX read-write lock We present an example of shared resource competition above: two threads shall perform increment operation of the same variable V (initial value of 0) simultaneously. The solution to shared resource competition is to add a lock. The lock is occupied before access to variable V, and released after access. Generally, we can use the binary semaphore with an initial value of TRUE or the counting semaphore or mutex semaphore with an initial value of 1 as the lock. We now slightly modify the example. There are ten threads (thread A, thread 1... thread 9) and a Variable V. Thread A needs to write Variable V, and thread [1-9] need to read Variable V. Apparently, Variable V has more reader threads than writer threads. In this case, if we continue to use the binary semaphore with an initial value of TRUE or the counting semaphore or mutex semaphore with an initial value of 1 as the lock, other reader threads will be blocked on the lock when a reader thread occupies the lock, apparently causing low read concurrent efficiency of shared resources, because direct read operation by multiple threads to Variable V will not confuse the value of Variable V. In order to solve the problem of low read concurrent efficiency of the common locking mechanism in the case of "read more and write less” of shared resources, the POSIX standard defines read-write lock and operation thereof, and the read-write lock has three states: read state, write state and unlock state. Provisions for read-write lock: the read-write lock at the read state can lock any read lock again, while the requested write lock will be firstly responded after the read lock is unlocked (SylixOS supports the write lock priority principle); the read-write lock at the write state will not respond to any lock, i.e., any lock request will fail. The type of POSIX read-write lock is pthread_rwlock_t. pthread_mutexattr_t type is defined during use. For example: A variable of pthread_rwlock_t rwlock; A POSIX read-write lock can be used after creation by calling the pthread_rwlock_init function. If the thread needs to wait a read-write lock, the pthread_rwlock_rdlock or pthread_rwlock function is called separately according to use (read or write) for shared resources, and the interrupt service routine cannot call any POSIX read-write lock function. A read-write lock is unlocked by calling the pthread_rwlock_unlock function. Manual OS. SpaceChain 260 Application Development Manual SpaceChain OS A read-write lock shall be deleted by calling the pthread_rwlock_destroy function after use (guarantee no use in the future), and SylixOS will recover kernel resources occupied by the read-write lock. (线程=thread) Figure 7.13 POSIX read-write lock A POSIX read-write lock attribute block shall be used as the parameter when a POSIX read-write lock is created. The type of POSIX read-write lock attribute block is pthread_rwlockattr_t. A variable of pthread_rwlockattr_t type is defined during use. For example: pthread_rwlockattr_t rwlockattr; ① 7.8.1 Read-write lock attribute block 1. Initialization and deletion of the read-write lock attribute block #include int pthread_rwlockattr_init(pthread_rwlockattr_t *prwlockattr); Prototype analysis of Function pthread_rwlockattr_init: For success of the function, return 0. For failure, return the error number; Parameter prwlockattr is the pointer of POSIX read-write lock attribute block; #include int pthread_rwlockattr_destroy(pthread_rwlockattr_t *prwlockattr); Prototype analysis of Function pthread_rwlockattr_destroy: For success of the function, return 0. For failure, return the error number; Parameter prwlockattr is the pointer of POSIX read-write lock attribute block; 2. Set and get process shared attributes of the read-write lock attribute block #include int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *prwlockattr, Manual OS. SpaceChain 261 Application Development Manual SpaceChain OS int pshared); Prototype analysis of Function pthread_rwlockattr_setpshared: For success of the function, return 0. For failure, return the error number; Parameter prwlockattr is the pointer of POSIX read-write lock attribute block; Parameter pshared identifies whether the POSIX read-write lock attribute block is shared by processes. #include int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *prwlockattr, int *pshared); Prototype analysis of Function pthread_rwlockattr_getpshared: For success of the function, return 0. For failure, return the error number; Parameter prwlockattr is the pointer of POSIX read-write lock attribute block; Output parameter pshared identifies whether the POSIX read-write lock attribute block is shared by processes. 7.8.2 Read-write lock 1. Initialization and deletion of the read-write lock #include int pthread_rwlock_init(pthread_rwlock_t *prwlock, const pthread_rwlockattr_t *prwlockattr); Prototype analysis of Function pthread_rwlock_init: For success of the function, return 0. For failure, return the error number; Parameter prwlock is the pointer of POSIX read-write lock; Parameter prwlockattr is the pointer of POSIX read-write lock attribute object, which can be NULL; #include int pthread_rwlock_destroy(pthread_rwlock_t *prwlock); Prototype analysis of Function pthread_rwlock_destroy: For success of the function, return 0. For failure, return the error number; Parameter prwlock is the pointer of POSIX read-write lock. 2. Read wait of read-write lock Manual OS. SpaceChain 262 Application Development Manual SpaceChain OS #include int pthread_rwlock_rdlock(pthread_rwlock_t *prwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *prwlock); int pthread_rwlock_timedrdlock(pthread_rwlock_t *prwlock, const struct timespec *abs_timeout); Prototype analysis of above three functions: For success of the function, return 0. For failure, return the error number; Parameter prwlock is the pointer of POSIX read-write lock; Parameter abs_timeout is the waiting absolute timeout. The pthread_rwlock_timedrdlock is the version of the pthread_rwlock_rdlock with wait timeout, and the abs_timeout is the absolute wait timeout (see Chapter 11 Time Management). The pthread_rwlock_tryrdlock is the "try wait" version of the pthread_rwlock_rdlock. When the read-write lock has been occupied by the write lock, the pthread_rwlock_rdlock will be blocked until awakened, while the pthread_rwlock_tryrdlock will return immediately, and the error number EBUSY is returned. 3. Write wait of read-write lock #include