Cplus2 Linux Development Course Note
来自牛客 C++ 项目.
Ch2. 多进程开发
2.1 进程概述
- 程序是包含一系列信息的文件, 这些信息描述了如何在运行时创建一个进程.
- 进程是正在运行的程序的实例, 是一个具有一定独立功能的程序关于某个数据集合的一次运行活动, 是操作系统动态执行的基本单元. 在传统的操作系统中, 进程既是基本的分配单元, 也是基本的执行单元.
- 进程是内核定义的抽象实体, 并为该实体分配用以执行程序的各项系统资源. 从内核角度看, 进程由用户内存空间和一系列内核数据结构组成.
- 单道程序, 多道程序, 时间片, Linux 进程调度算法.
- 并行: 指在同一时刻,有多条指令在多个处理器上同时执行. 并发: 指在同一时刻只能有一条指令执行, 但多个进程指令被快速地轮换执行, 使得宏观上具有多个进程同时执行的效果.
- 内核会为每个进程分配一个进程控制块 (Processing Control Block, PCB), 维护进程相关的信息. Linux 下的 PCB 是
task_struct
结构体, 可在/usr/src/linux-headers-xxx/include/linux/sched.h
文件中查询相关定义. 其成员包括不限于: 进程 id, 进程状态, 进程切换时需要保存和恢复的 CPU 寄存器, 描述虚拟地址空间的信息, 描述控制终端的信息, 当前工作目录, umask 掩码, 文件描述符表, 和信号相关的信息, 用户 id 与组 id, 会话和进程组, 进程可用资源上限 (ulimit -a
) 等.
2.2 进程状态转换
- 三态模型. 就绪: 进程具备运行条件, 等待系统分配处理器以便运行; 运行: 进程占有处理器正在运行; 阻塞: wait 或 sleep, 进程不具备运行条件, 正在等待某个事件的完成. 五态模型. 新建: 进程刚被创建时的状态, 尚未进入就绪队列; 就绪; 运行; 阻塞; 终止: 正常结束或异常终止, 或被操作系统或其他进程终止. 终止态进程不再执行, 待其他进程完成了对其的信息抽取后, 操作系统将删除该进程.
- 查看进程: |
ps aux / ajx
| | --------------------------------------------- | |a
: 显示终端上的所有进程, 包括其他用户的进程 | |u
: 详细信息 | |x
: 没有控制终端的进程 | |j
: 与作业控制相关的信息 |tty
: 终端信息;STAT
: 状态信息;PPID
,PID
,PGID
: 父进程, 进程, 进程组 id. ## 2.3 进程创建
Chapter 1. Getting Started
The operating system runs a C++ program by calling
main
.On most systems, the value returned from main is a status indicator. A return value of
0
indicates suceess. A nonzero return has a meaning that is defined by the system. Ordinarily a nonzero return indicates what kind of error occurred.A type defines both the contents of a data element and the operations that are possible on those data.
The value returned from
main
is accessed in a system-dependent manner. On both UNIX and Windows systems, after executing the program, you must issue an appropriateecho
command.
On UNIX systems, we obtain the status by writingTo see the status on a Windows system, we write1
$ echo $?
1
$ echo %ERRORLEVEL%
The output operator
<<
takes two operands: the left-hand operand must be anostream
object; the right-hand operand is a vaule to print. The result of the output operator is its left-hand operand. The input operator>>
behaves analogously to the output operator.The manipulator
endl
has the effect of ending the current line and flushing the buffer associated with that device. Flushing buffer ensures that all the output the program has generated so far is actually written to the output stream, rather than sitting in memory waiting to be written. By default, readingcin
flushescout
;cout
is also flushed when the program ends normally; writes tocerr
are not buffered, usually used for error messages or other output that is not part of the normal logic of the program.
Programmers often add print statements during debugging. Such statements should always flush the stream. Otherwise, if the program crashes, output my be left in the buffer, leading to incorrect inferences about where the program crashed.When a comment pair does span multiple lines (surely, the best way is to use single-line comments cause comment pairs do not nest), it is often a good idea to indicate visually that the inner lines are part of a multiple comment (e.g. begin each line with an asterisk).
The best way to comment a block of code is to insert single-line comments at the beginning of each line in the section we want to ignore since that code might contain nested comment pairs but comment pairs do not nest.
The variable defined in init-statement of
for
's header exists only inside thefor
; it is not possible to use the variable after the loop terminates.When we use an
istream
as a condition,the effect is to test the state of the stream. If the stream is valid—that is, if the stream hasn't encountered an error—then the test succeeds.1
while (std::cin >> value)
Anistream
becomes invalid when we hit end-of-file (e.g. ctrl+z on Win or ctrl+d on UNIX) or encounter an invalid input, such as reading a value that is not an integer.Most operating systems support file redirection (to avoid tediously repeated typing), which lets us associate a named file with the standard input and the standard output:
This command will read input from a file named1
$ exefile <infile >outfile
infile
and write its output to a file namedoutfile
in the current directory.
Chapter 2. Variables and Basic Types
C++ is a statically typed language; type checking is done at compile time.
To give meaning to memory at a given address, we must know the type of the value stored there. The type determines how many bits are used and how to interpret those bits.
Which of the two character representations
signed char
andunsigned char
is equivalent tochar
depends on the compiler. So computations usingchar
are especially problematic becausechar
issigned
on some machines andunsigned
on others. If you need a tiny integer, explicitly specify eithersigned char
orunsigned char
.
Do not use plainchar
orbool
in arithmetic expressions. Use them only to hold characters or truth values.If we assign an out-of-range value to an object of unsigned type, the result is the remainder of the value modulo the number of values the target type can hold. If we assign an out-of-range value to an object of signed type, the result is undefined. Expressions that mix signed and unsigned values can yield suprising results when the signed value is negative since signed values are automatically converted to unsigned in an arithmetic expression.
Programs usually should avoid implementation-defined behavior. Such programs are said to be nonportable. When program is moved to another machine, code that relied on implementation-defined behavior may fail.
Tracking down these sorts of problems in previously working programs is, mildly put, unpleasant.The type of a string literal is array of constant
char
s. The compiler appends a null character ('\0'
) to every string literal. Thus, the actual size of a string literal is one more that its apparent size.Two string literals that appear adjacent to one another and that are seperated only by spaces, tabs or newlines are concatenated into a single literal.
We use this form of literal when we need to write a literal that would otherwise be too large to fit comfortably on a single line.Initialization is not assignment. Initialization happens when a variable is given a value when it is created. Assignment obliterates an object's current value and replaces that value with a new one.
When used with variables of built-in type, the list initialization has one important property: The compiler will not let us initialize variables of built-in type if the initializer might lead to the loss of information:
1
2
3long double ld = 3.1415926536;
int a{ld}, b = {ld}; // error: narrowing conversion required
int c(ld), d = ld; // ok: but value will be truncatedThe value of an object of built-in type that is not explicitly initialized depends on where it is defined. Variables defined outside any function body are initialized to zero. With one exception, variables of built-in type defined inside a function are uninitialized. The value of an uninitialized variable of built-in type is undefined. (NCC)
DEFINITION \(\subseteq\) DECLARATION.
To support seperate compilation, C++ distinguishes between declarations and definitions. A declaration makes a name known to the program. A file that wants to use a name defined elsewhere includes a declaration for that name.
Variables must be defined exactly once but can be declared many times. To use the same variable in multiple files, we must define that variable in one—and only one—file. Other files that use that variable must declare—but not define—that variable. It is an error to provide an initializer on anextern
inside a function.Local object with a same as the global object will hide the global one. Use scope operator
::
to override the default scoping rules. When the scope operator has an empty left-hand side, it is a request to fetch the name on the right-hand side from the global scope.Ordinarily, when we initialize a variable, the value of the initializer is copied into the object we are creating. When we define a reference, instead of copying the initializer's value, we bind the reference to its initializer. References must be initialized and cannot be rebound to refer to other different objects.
A reference is not an object. Instead, it is just another name for an already existing object. So we may not define a reference to a reference. Since references do not have addresses, we may not define a reference or pointer to a reference.nullptr
is a literal that has a special type that can be converted to any other pointer type. If the pointer is 0, then the condition isfalse
. Any nonzero pointer evaluates astrue
. Using an invalid pointer as a condition or in a comparison is undefined.A
void*
pointer holds an address, but the type of the object at that address is unknown. We cannot use avoid*
to operate on the object it addresses—we don't know that object's type. Generally, we use avoid*
pointer to deal with memory as memory, rather than using the pointer to access the object stored in that memory.In the code
the1
int i = 1024, *p = &i, &r = i;
int
is called base type,*
and&
are type modifiers.It can be easier to understand complicated pointer or reference declarations if you read them from right to left.
1
2
3int *p;
int *&r = p; // r is a reference to the pointer p
int* ip, &r = ip; // invalid bindconst
variables are defined as local to the file. When we define aconst
with the same name in multiple files, it is as if we had written definitions for seperate variables in each file. When we have aconst
that we want to share across multiple files but whose initializer is not a constant expression, we need to useextern
on both its definition and declaration(s).We can initialize a reference to
const
from any expression that can be converted to the type of the reference. In particular, we can bind a reference toconst
to a nonconst
object, a literal, or a more general expression.but here we cannot use1
2
3
4int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;r1
to changei
. When we usethe compiler transforms this into something like1
2double dval = 3.14;
const int &ri = dval;Since binding a reference to a temporary is almost surely not what the programmer intended and the language makes it illegal, we cannot use a nonconst reference in the above examples.1
2const int temp = dval;
const int &ri = temp;We use the term top-level
const
to indicate that the pointer itself is aconst
. When a pointer can point to aconst
object, we refer to thatconst
as a low-levelconst
. The distinction between top-level and low-level matters when we copy an object: top-levelconst
s are ignored at that time. On the other hand, low-levelconst
is never ignored. When we copy an object, both objects must have the same low-levelconst
qualification or there must be a conversion between the types of the two objects.
const
in reference types is always low-level.When define a pointer in a
constexpr
declaration, theconstexpr
specifier applies to the pointer, not the type to which the pointer points, which means that theconstexpr
imposes a top-levelconst
on the objects it defines.Attension,
1
2typedef char *pstring;
const pstring *cstr = 0; // cstr is a constant pointer to char, not a pointer to `const` `char`, since the type of pstring is "pointer to `char`", which means that the base type of declaration is a pointer typeAs with any other type specifier, we can define multiple variables using
auto
. Because a declaration can involve only a single base type, the initializers for all the variables in the declaration must have types that are consistent with each other.auto
ordinarily ignores top-levelconst
s. As usual in initializations, low-levelconst
s, such as when an initializer is a pointer toconst
, are kept. If we want the deduced type to have top-levelconst
, we must say so explicitly.From above we can see that when we ask for a reference to an1
2
3
4
5
6const int ci = 42;
auto a = ci; // a is an int
const auto b = ci; // b has type const int
auto &c = ci; // c is a const int&
auto &d = 42; // error: cannot bind a plain reference to a literal
const auto &e = 42; // okauto
-deduced type, top-levelconst
s in the initializer are not ignored.When the expression to which we apply
decltype
is a variable,decltype
returns the type of that variable, including top-levelconst
and references. Anddecltype
returns a reference type for expressions that yield objects that can stand on the left-hand side of the assignment.If we use1
2
3
4
5
6const int ci = 0, &cj = ci;
decltype(ci) a = 0; // a has type const int
decltype(cj) b; // error: b is a reference and must be initialized
int i = 42, *p = &i, &r = i;
decltype(r + 0) c; // ok: addition yields an int; c is an (uninitialized) int
decltype(*p) d; // error: d is int& and must be initializeddecltype(r)
, we will get a reference type. Another important difference betweendecltype
andauto
is that the deduction done bydecltype
depends on the form of its given expression.1
2decltype((i)) e; // error: e is int&
decltype(i) f; // ok: f is an int
Chapter 3. Strings, Vectors, and Arrays
Code inside headers ordinarily should not use
using
declarations. The reason is that the contents of a header are copied into the including program's text. If a header has ausing
declaration, then every program that includes that header gets that sameusing
declaration. As a result, a program that didn't intend to use the specified library name might encounter unexpected name conflicts. Use the C++ versions of C library headers.Like the input operator,
getline
returns itsistream
argument. The newline that causesgetline
to return is discarded; the newline is not stored in thestring
.String literals are not standard library
string
s. Thestring
library lets us convert both character literals and character string literals tostring
s. E.g. when we mixstring
s and string or character literals, at least one operand to each+
operator must be ofstring
type.When we use curly braces, we're saying that, if possible, we want to list initialize the object. That is, if there is a way to use the values inside the curly braces as a list of element initializers, the class will do so. Only if it is not possible to list initialize the object will the other ways to initialize the object be considered.
Since
vector
s grow efficiently, it is often unnecessary—and can result in poorer performance—to define avector
of a specific size.
The exception to this rule is if all the elements actually need the same value. If differing element values are needed, it is usually more efficient to define an emptyvector
and add elements as the values we need become known at run time.The body of a range
for
must not change the size of the sequence over which it is iterating. (NCC)A valid iterator either denotes an element or denotes a position one past the last element in a container. All other iterator values are invalid. If container is empty,
begin
returns the same iterator as the one returned byend
-they are both off-the-end iterators.Any operation, such as
push_back
, that changes the size of avector
potentially invalidates all iterators into thatvector
. It is important to realize that loops that use iterators should not add elements to the container to which the iterators refer.Iterators are equal if they denote the same element or if they are both off-the-end iterators for the same container. Otherwise, they are unequal. Subtracting two iterators yields the number that when added to the right-hand iterator yields the left-hand iterator. The iterators must denote elements in, or one past the end of, the same container.
In subtracting the result type is a signed integral type nameddifference_type
.In most expressions, when we use an object of array type, we are really using a pointer to the first element in that array.
Unlike subscripts for
vector
andstring
, the index of the built-in subscript operator is not anunsigned
type.
Chapter 4. Expressions
Roughly speaking, when we use an object as an rvalue, we use the object's value (its contents). When we use an object as an lvalue, we use the object's identity (its location in memory). We can use an lvalue when an rvalue is requried, but we cannot use an rvalue when an lvalue (i.e., a location) is required (with one exception that will be covered later).
sizeof
does not evaluate its operand, so dereferencing an invalid pointer as the operand tosizeof
is safe because the pointer is not actually used.
Chapter 5. Statements
- Throwing an exception terminates the current function and transfers control to a handler that will know how to handle this error. When a
catch
is selected to handle an exception, the associated block is executed.
Chapter 6. Functions
Parameter initialization works the same way as variable in initialization.
Using reference parameters could avoid copying objects of large class types or large containers. (Moreover, some class type cannot be copied.) Reference parameters that are not changed inside a function should be references to
const
.When you use the arguments in
argv
, remember that the optional arguments begin inargv[1]
,arg[0]
contains the program's name (or empty string), not user input.Never return a reference or pointer to a local object. Reference returns are LVALUES.
The form of a function that returns a pointer to an array is
1
Type (*function(parameter_list))[dimension]
Overloaded functions must differ in the number or the type(s) of their parameters. It is an error for two functions to differ only in terms of their return types. A parameter that has a top-level
const
is indistinguishable from one without a top-levelconst
. On the other hand, we can overload based on whether the parameter is a reference (or pointer) to theconst
or nonconst
version of a given type; suchconst
s are low-level.1
2
3
4
5
6
7
8
9
10
11Record lookup(Phone);
Record lookup(const Phone); // redeclares
Record lookup(Phone*);
Record lookup(Phone* const); // also redeclares
Record lookup(Account&);
Record lookup(const Account&); // new function that takes a const reference
Record lookup(Account*);
Record lookup(const Account*); // new function, takes a pointer to constIf a parameter has a default argument, all the parameters that follow it must also have default arguments. To override the default for the last parameter, we must also supply arguments for the first several parameters (if exist). So we may design the ordering of parameters so that those least likely to use a default value appear first and those most likely to use a default appear last.
Names used as default arguments are resolved in the scope of the function declaration.
The
inline
mechanism is meant to optimize small, straight-line functions that are called frequnetly. The specification is only a request to the compiler. The compiler may choose to ignore this request.
Chapter 7. Classes
Member functions access the object on which they were called through an extra, implicit parameter named
this
. When we call a member function,this
is initialized with the address of the object on which the function was invoked. Any direct use of a member of the class is assumed to be an implicit reference throughthis
. (this
is aconst
pointer.)Although
this
is implicit, it follows the normal initialization rules, which means that (by default) we cannot bindthis
to aconst
object. This means that we cannot call an ordinary member function on aconst
object. Aconst
following the parameter list (i.e.,const
member function) indicates thatthis
is a pointer toconst
.1
2
3
4
5
6
7
8
9
10
11
12
13struct Sales_data {
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data total;
total.isbn();
Sales_data::isbn(&total);
std::string Sales_data::isbn(const Sales_data *const this) { return this->bookNo; }Unlike other member functions, constructors may not be declared as
const
. When we create aconst
object of a class type, the object does not assume its "const
ness" until after the constructor completes the object's initialization.Classes that have members of built-in or compound type usually should rely on the synthesized default constructor only if all such members have in-class initializers. If we define any constructors, the class will not have a default constructor unless we define that constructor ourselves.
= default
defines the default constructor. We are defining this constructor only because we want to provide other constructors as well as the default constructor. We want this constructor to do exactly the same work as the synthesized version we had been using.We can define a class type using either keyword,
struct
orclass
. The only difference between them is the default access level. If we use thestruct
keyword, the members defined before the first access specifier arepublic
; if we useclass
, then the members areprivate
.
As a matter of programming style, when we define a class intending for all of its members to bepublic
, we usestruct
. If we intend to haveprivate
members, then we useclass
.Friend declarations may appear only inside a class definition. Friends are not members of the class and are not affected by the access control of the section in which they are declared.
Unlike ordinary members, members that define types must appear before they are used.
It sometimes (but not very often) happens that a class has a data member that we want to be able to modify, even inside a
const
member function. We indicate such members by including themutable
keyword in their declaration. The basis for this rule is that if a class requires control to initialize an object in one case, then the class is likely to require control in all cases.When we provide an in-class initializer, we must do so following an
=
sign or inside braces. (See this post.)A class can also make another class its friend or it can declare specific member functions of another (previously defined) class as friends. It is important to understand that friendship is not transitive.
A class must declare as a friend each function in a set of overloaded functions that it wishes to make a friend.
- Class definitions are processed in two phases:
- First, the member declarations are compiled.
- Function bodies are compiled only after the entire class has been seen.
Member function definitions are processed after the compiler processes all of the declarations in the class.