(610) 200-9673

原文:/www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3055.pdf
如下是原文的拷贝,会有我个人的一些理解注释.不翻译全文.并理解不一定完全正确,存在主观性,请个人注意鉴别.
lvalue reference is similar with T&;, T is type.
lvalue reference(左值引用), 可以用T&表示,T是类型.
rvalue reference is similar with T&&, T is type.
rvalue reference (右值引用),可以用T&&来表示.T是类型

注意:lvalue和lvalue reference,rvalue与rvalue reference不完全一致.
并不是同样的概念

I. Background
Rvalue references were introduced into C++0x to provide a mechanism for capturing an rvalue
temporary (which could previously be done in C++ using traditional lvalue references to const) and
allowing modification of its value (which could not). When used in the contexts of reference binding,
overload resolution, and template argument deduction, it was desired that a function returning an rvalue
reference should behave like a traditional function returning an rvalue. The most straightforward way
of achieving that goal was to classify such rvalue reference return values as rvalues, and that approach
is embodied in the current draft.
Unfortunately, however, the term “rvalue” implies certain characteristics that are incompatible with the
intended uses for rvalue references. In particular:
• Rvalues are anonymous and can be copied at will, with the copy assumed to be equivalent to the
original. Rvalue references, however, designate a specific object in memory (even if it is a
temporary), and that identity must be maintained.
• The type of an rvalue is fully known – that is, its type must be complete, and its static type is
the same as its dynamic type. By contrast, an rvalue reference must support polymorphic
behavior and should be able to have an incomplete type.
• The type of a non-class rvalue is never cv-qualified. An rvalue reference, however, can be
bound to a const or volatile non-class object, and that qualification must be preserved.
cv-qualified : c is standard for const and the v is standard for volatile.
In addition, rvalue references (like traditional lvalue references) can be bound to functions. Treating an
rvalue reference return value as an rvalue, however, introduces the novel concept of a function rvalue
into the language. There was previously no such idea – a function lvalue used in an rvalue context
becomes a pointer-to-function rvalue, not a function rvalue – so the current draft Standard does not
describe how such rvalues are to be treated. In particular, function calls and conversions to function
pointers are specified in terms of function lvalues, so most plausible uses of rvalue references to
functions are undefined in the current wording.
One possible resolution for these problems would be to maintain the current approach of treating an
rvalue reference return value as an rvalue but to add various caveats to the specification of rvalues so
that those coming from rvalue references would have special characteristics. This could be called the
“funny rvalue” approach. However, further examination of the current wording of the draft Standard
indicates that the problems listed above are probably only the tip of the iceberg: many of the
specifications that should apply to the objects to which rvalue references refer, such as object lifetime,
aliasing rules, etc., are phrased in terms of lvalues, so the list of rvalue caveats could get quite long.
This suggests an alternative approach: that rvalue reference return values should actually be seen as
lvalues, with a few exceptions to allow them to be treated as rvalues in the cases where that is intended,
i.e., in reference binding, overload resolution, and template argument deduction. This idea, dubbed the
“funny lvalue” approach, is embodied in earlier versions of this paper.
After extensive discussions in the Core Working Group at the Pittsburgh (March 8-13, 2010) meeting, a
third approach was suggested and is adopted in this version of the paper, namely, to establish
page 2 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Re
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
An lvalue refers to an object or function. Some rvalue expressions — those of (possibly cvqualified)
class or array type — also refer to objects.51
[Note: some built-in operators and function calls yield lvalues. [Example: if E is an expression
of pointer type, then *E is an lvalue expression referring to the object or function to which E
points. As another example, the function
int& f();
yields an lvalue, so the call f() is an lvalue expression. —end example] —end note]
Note: some built-in operators expect lvalue operands. [Example: built-in assignment operators
all expect their left-hand operands to be lvalues. —end example] Other built-in operators yield
rvalues, and some expect them. [Example: the unary and binary + operators expect rvalue
arguments and yield rvalue results. —end example] The discussion of each built-in operator in
Clause 5 indicates whether it expects lvalue operands and whether it yields an lvalue. —end
note]
The result of calling a function that does not return an lvalue reference is an rvalue. User
defined operators are functions, and whether such operators expect or yield lvalues is
determined by their parameter and return types.
An expression which holds a temporary object resulting from a cast to a type other than an
lvalue reference type is an rvalue (this includes the explicit creation of an object using
functional notation (5.2.3)).
Expressions are categorized according to the following taxonomy:
• An lvalue (so-called, historically, because lvalues could appear on the left-hand side
of an assignment expression) designates a function or an object. [Example: If E is
an expression of pointer type, then *E is an lvalue expression referring to the object
or function to which E points. As another example, the result of calling a function
whose return type is an lvalue reference is an lvalue. —end example]
page 4 of 20
expression
glvalue rvalue
lvalue xvalue prvalue
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
• An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its
lifetime (so that its resources may be moved, for example). An xvalue is the result
of certain kinds of expressions involving rvalue references (8.3.2). [Example: The
result of calling a function whose return type is an rvalue reference is an xvalue. —
end example]
• A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
• An rvalue (so-called, historically, because rvalues could appear on the right-hand
side of an assignment expression) is an xvalue, a temporary object (12.2) or subobject
thereof, or a value that is not associated with an object.
• A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [Example: The result
of calling a function whose return type is not a reference is a prvalue. The value of
a literal such as 12, 7.3e5, or true, is also a prvalue. —end example]
Every expression belongs to exactly one of the fundamental classifications in this
taxonomy: lvalue, xvalue, or prvalue. This property of an expression is called its value
category. [Note: The discussion of each built-in operator in Clause 5 indicates the category
of the value it yields and the value categories of the operands it expects. For example, the
built-in assignment operators expect that the left operand is an lvalue and that the right
operand is a prvalue and yield an lvalue as the result. User-defined operators are
functions, and the categories of values they expect and yield are determined by their
parameter and return types. —end note]
Whenever an lvalue a glvalue appears in a context where an rvalue a prvalue is expected, the
lvalue glvalue is converted to an rvalue a prvalue; see 4.1, 4.2, and 4.3.
The discussion of reference initialization in 8.5.3 and of temporaries in 12.2 indicates the
behavior of lvalues and rvalues in other significant contexts.
Class rvalues prvalues can have cv-qualified types; non-class rvalues prvalues always have cvunqualified
types. Rvalues Prvalues shall always have complete types or the void type; in
addition to these types, lvalues glvalues can also have incomplete types.
An lvalue for an object is necessary in order to modify the object except that an rvalue of class
type can also be used to modify its referent under certain circumstances. [Example: a member
function called for an object (9.3) can modify the object. —end example]

If a program attempts to access the stored value of an object through an lvalue a glvalue of
other than one of the following types the behavior is undefined52
5 Expressions (paragraphs 5-6)
If an expression initially has the type “lvalue reference to T” (8.3.2, 8.5.3), the type is adjusted
to T prior to any further analysis, and the expression designates the object or function denoted
page 5 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
by the lvalue reference, and the expression is an lvalue or an xvalue, depending on the
expression.
If an expression initially has the type “rvalue reference to T” (8.3.2, 8.5.3), the type is adjusted
to “T” prior to any further analysis, and the expression designates the object or function denoted
by the rvalue reference. If the expression is the result of calling a function, whether implicitly or
explicitly, it is an rvalue; otherwise, it is an lvalue. [Note: An expression is an xvalue if it is:
• the result of calling a function, whether implicitly or explicitly, whose return type is
an rvalue reference to object type,
• a cast to an rvalue reference to object type,
• a class member access expression designating a non-static data member in which
the object expression is an xvalue, or
• a .* pointer-to-member expression in which the first operand is an xvalue and the
second operand is a pointer to data member.
In general, the e ect of this rule is that named rvalue references are treated as lvalues and ff
unnamed rvalue references to objects are treated as rvalues xvalues; rvalue references to
functions are treated as lvalues whether named or not. —end note]
[Example:
struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = a static_cast<A&&>(a);
The expressions f(), f().m, static_cast<A&&>(a), and a + a are rvalues xvalues of
type A. The expression ar is an lvalue of type A. —end example]
1.3.4:
dynamic type
the type of the most derived object (1.8) to which the lvalue glvalue denoted by an lvalue a
glvalue expression refers. [Example: if a pointer (8.3.1) p whose static type is “pointer to class
B” is pointing to an object of class D, derived from B (Clause 10), the dynamic type of the
expression *p is “D.” References (8.3.2) are treated similarly. —end example] The dynamic
type of an rvalue a prvalue expression is its static type.
1.9 Program execution ¶12:
Accessing an object designated by a volatile lvalue glvalue (3.10), …are all side effects…
page 6 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
Evaluation of an expression (or a sub-expression) in general includes both value computations
(including determining the identity of an object for lvalue glvalue evaluation and fetching a
value previously assigned to an object for rvalue prvalue evaluation) and initiation of side
effects…
2.14.6 Boolean literals ¶1:
…Such literals are rvalues prvalues and have type bool.
2.14.7 Pointer literals ¶1:
…It is an rvalue a prvalue of type std::nullptr_t.
3 Basic concepts ¶1:
[Note: this Clause presents the basic concepts of the C++ language. It explains the difference
between an object and a name and how they relate to the notion of an lvalue value categories
for expressions. It introduces the concepts…
3.2 One definition rule ¶4:
• …
• an lvalue-to-rvalue conversion is applied to an lvalue a glvalue referring to an object of
type T…
• …
3.8 Object lifetime ¶6:
Similarly, before the lifetime of an object has started but after the storage which the object will
occupy has been allocated or, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, any lvalue which glvalue that refers to the
original object may be used but only in limited ways. Such an lvalue a glvalue refers to
allocated storage (3.7.4.2), and using the properties of the lvalue which glvalue that do not
depend on its value is well-defined. The program has undefined behavior if:
• an lvalue-to-rvalue conversion (4.1) is applied to such an lvalue a glvalue,
• the lvalue glvalue is used to access a non-static data member or call a non-static member
function of the object, or
• the lvalue glvalue is implicitly converted (4.10) to a reference to a base class type, or
• the lvalue glvalue is used as the operand of a static_cast (5.2.9) except when the
conversion is ultimately to cv char& or cv unsigned char&, or
• the lvalue glvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand
of typeid.
page 7 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
4 Standard conversions ¶3:
An expression e can be implicitly converted to a type T… The result is an lvalue if T is an
lvalue reference type or an rvalue reference to function type (8.3.2), an xvalue if T is an
rvalue reference to object type, and an rvalue a prvalue otherwise. The expression e is used
as an lvalue a glvalue if and only if the initialization uses it as an lvalue a glvalue.
4.1 Lvalue-to-rvalue conversion ¶1-2:
An lvalue A glvalue (3.10) of a non-function, non-array type T can be converted to an rvalue a
prvalue. [Footnote: For historical reasons, this conversion is called the “lvalue-to-rvalue”
conversion, even though that name does not accurately reflect the taxonomy of expressions
described in 3.10. —end footnote] If T is an incomplete type, a program that necessitates this
conversion is ill-formed. If the object to which the lvalue glvalue refers is not an object of type
T and is not an object of a type derived from T, or if the object is uninitialized, a program that
necessitates this conversion has undefined behavior. If T is a non-class type, the type of the
rvalue prvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue prvalue is
T.
54 [Footnote: In C++ class rvalues prvalues can have cv-qualified types (because they are
objects). This differs from ISO C, in which non-lvalues never have cv-qualified types. —end
footnote]
When an lvalue-to-rvalue conversion occurs in an unevaluated operand or a subexpression
thereof (Clause 5) the value contained in the referenced object is not accessed. Otherwise, if the
lvalue glvalue has a class type, the conversion copy-initializes a temporary of type T from the
lvalue glvalue and the result of the conversion is an rvalue a prvalue for the temporary.
Otherwise, if the lvalue glvalue has (possibly cv-qualified) type std::nullptr_t, the
rvalue prvalue result is a null pointer constant (4.10). Otherwise, the value contained in the
object indicated by the lvalue glvalue is the rvalue prvalue result.
4.2 Array-to-pointer conversion ¶1:
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted
to an rvalue a prvalue of type “pointer to T”. The result is a pointer to the first element of the
array.
4.3 Function-to-pointer conversion ¶1:
An lvalue of function type T can be converted to an rvalue a prvalue of type “pointer to T .”
The result is a pointer to the function.55
4.4 Qualification conversions ¶1-2:
An rvalue A prvalue of type “pointer to cv1 T” can be converted to an rvalue a prvalue of type
“pointer to cv2 T” if “cv2 T” is more cv-qualified than “cv1 T”.
An rvalue A prvalue of type “pointer to member of X of type cv1 T” can be converted to an
rvalue a prvalue of type “pointer to member of X of type cv2 T” if “cv2 T” is more cv-qualified
page 8 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
than “cv1 T”.
4.5 Integral promotions ¶1-6:
An rvalue A prvalue of an integer type… can be converted to an rvalue a prvalue of type int
if…; otherwise, the source rvalue prvalue can be converted to an rvalue a prvalue of type
unsigned int.
An rvalue A prvalue of type char16_t, char32_t, or wchar_t (3.9.1) can be converted to
an rvalue a prvalue of the first of the following types… an rvalue a prvalue of type
char16_t, char32_t, or wchar_t can be converted to an rvalue a prvalue of its
underlying type.
An rvalue A prvalue of an unscoped enumeration type whose underlying type is not fixed (7.2)
can be converted to an rvalue a prvalue of the first of the following types… an rvalue a prvalue
of an unscoped enumeration type can be converted to an rvalue a prvalue of the extended
integer type…
An rvalue A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can
be converted to an rvalue a prvalue of its underlying type. Moreover, if integral promotion can
be applied to its underlying type, an rvalue a prvalue of an unscoped enumeration type whose
underlying type is fixed can also be converted to an rvalue a prvalue of the promoted
underlying type.
An rvalue A prvalue for an integral bit-field (9.6) can be converted to an rvalue a prvalue of
type int…
An rvalue A prvalue of type bool can be converted to an rvalue a prvalue of type int…
4.6 Floating point promotion ¶1:
An rvalue A prvalue of type float can be converted to an rvalue a prvalue of type double…
4.7 Integral conversions ¶1:
An rvalue A prvalue of an integer type can be converted to an rvalue a prvalue of another
integer type. An rvalue A prvalue of an unscoped enumeration type can be converted to an
rvalue a prvalue of an integer type.
4.8 Floating point conversions ¶1:
An rvalue A prvalue of floating point type can be converted to an rvalue a prvalue of another
floating point type…
4.9 Floating-integral conversions ¶1-2:
An rvalue A prvalue of a floating point type can be converted to an rvalue a prvalue of an
integer type…
An rvalue A prvalue of an integer type or of an unscoped enumeration type can be converted to
page 9 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
an rvalue a prvalue of a floating point type…
4.10 Pointer conversions ¶1-3:
A null pointer constant is an integral constant expression (5.19) rvalue prvalue of integer type
that evaluates to zero or an rvalue a prvalue of type std::nullptr_t… A null pointer
constant of integral type can be converted to an rvalue a prvalue of type std::nullptr_t.
[Note: The resulting rvalue prvalue is not a null pointer value. —end note]
An rvalue A prvalue of type “pointer to cv T,” where T is an object type, can be converted to an
rvalue a prvalue of type “pointer to cv void”…
An rvalue A prvalue of type “pointer to cv D”, where D is a class type, can be converted to an
rvalue a prvalue of type “pointer to cv B”…
4.11 Pointer to member conversions ¶2:
An rvalue A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can
be converted to an rvalue a prvalue of type “pointer to member of D of type cv T”…
4.12 Boolean conversions ¶1:
An rvalue A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type
can be converted to an rvalue a prvalue of type bool. A zero value, null pointer value, or null
member pointer value is converted to false; any other value is converted to true. An rvalue
A prvalue of type std::nullptr_t can be converted to an rvalue a prvalue of type bool;
the resulting value is false.
5. Expressions ¶2, 9:
[Note: …Overloaded operators obey the rules for syntax specified in Clause 5, but the
requirements of operand type, lvalue value category, and evaluation order are replaced by the
rules for function call…
Whenever an lvalue a glvalue expression appears as an operand of an operator that expects an
rvalue a prvalue for that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), or functionto-pointer
(4.3) standard conversions are applied to convert the expression to an rvalue a
prvalue. [Note: because cv-qualifiers are removed from the type of an expression of non-class
type when the expression is converted to an rvalue a prvalue, an lvalue expression of type
const int can, for example, be used where an rvalue a prvalue expression of type int is
required. —end note]
5.1.1 General ¶1-3, 6, 7-8:
…A string literal is an lvalue; all other literals are rvalues prvalues.
The keyword this names a pointer… The expression is an rvalue a prvalue.
…The result is the entity denoted by the identifier, qualified-id, operator-function-id or literalpage
10 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
operator-id. The result is an lvalue if the entity is a function or variable and a prvalue
otherwise…
…The result is an lvalue if the entity is a function, variable, or data member and a prvalue
otherwise… The result is an lvalue if the member is a static member function or a data member
and a prvalue otherwise.
…The result is an lvalue if the member is a function or a variable and a prvalue otherwise.
A nested-name-specifier that names an enumeration… The result is an rvalue a prvalue.
5.1.2 Lambda expressions ¶2, 17:
The evaluation of a lambda-expression results in an rvalue a prvalue temporary (12.2)…
…[Note: the cast ensures that the transformed expression is an rvalue a prvalue. —end note]
5.2.2 Function call ¶10:
A function call is an lvalue if and only if the result type is an lvalue reference if the result type
is an lvalue reference or an rvalue reference to function type, an xvalue if the result type is
an rvalue reference to object type, and a prvalue otherwise.
5.2.3 Explicit type conversion (functional notation) ¶1-3:
…with the result being the value of t as an rvalue a prvalue.
The expression T()… creates an rvalue a prvalue of the specified type… [Note: if T is a nonclass
type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the
resulting rvalue prvalue (3.10). —end note]
…its value is that temporary object as an rvalue a prvalue.
5.2.5 Class member access ¶3-4:
…Abbreviating object-expression.id-expression as E1.E2, then the type and lvalue properties
value category of this expression are determined as follows…
If E2 is declared to have type “reference to T,” then E1.E2 is an lvalue; the type of E1.E2 is
T. Otherwise, one of the following rules applies.
• If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the
expression designates the named member of the class. The type of E1.E2 is T.
• …If E1 is an lvalue, then E1.E2 is an lvalue; if E1 is an xvalue, then E1.E2 is an
xvalue; otherwise, it is an rvalue a prvalue…
• If E2 is a (possibly overloaded) member function…
• If it refers to a static member function…
page 11 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
• Otherwise, if E1.E2 refers to a non-static member function… then E1.E2 is an
rvalue a prvalue…
• If E2 is a nested type…
• If E2 is a member enumerator and the type of E2 is T, the expression E1.E2 is an
rvalue a prvalue…
5.2.6 Increment and decrement ¶1:
…The result is an rvalue a prvalue…
5.2.7 Dynamic cast ¶2, 5:
If T is a pointer type, v shall be an rvalue a prvalue of a pointer to complete class type, and the
result is an rvalue a prvalue of type T. If T is an lvalue reference type, v shall be an lvalue of a
complete class type, and the result is an lvalue of the type referred to by T. If T is an rvalue
reference type, v shall be an expression having a complete class type, and the result is an rvalue
xvalue of the type referred to by T.
…The result is an lvalue if T is an lvalue reference, or an rvalue xvalue if T is an rvalue
reference…
5.2.8 Type identification ¶2-3, 5:
When typeid is applied to an lvalue a glvalue expression whose type is a polymorphic class
type (10.3), the result refers to a std::type_info object representing the type of the most
derived object (1.8) (that is, the dynamic type) to which the lvalue glvalue refers. If the lvalue
glvalue expression is obtained by applying the unary * operator to a pointer67 and the pointer is
a null pointer value (4.10), the typeid expression throws the std::bad_typeid exception
(18.7.3).
When typeid is applied to an expression other than an lvalue a glvalue of a polymorphic
class type, the result refers to a std::type_info object representing the static type of the
expression.

The top-level cv-qualifiers of the lvalue glvalue expression or the type-id that is the operand of
typeid are always ignored…
5.2.9 Static cast ¶1-3, 11-13:
…If T is an lvalue reference type or an rvalue reference to function type, the result is an
lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result
is an rvalue a prvalue…
An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,”
where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to
page 12 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification
than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.
The result has type “cv2 D.” An rvalue xvalue of type “cv1 B” may be cast to type “rvalue
reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B.” …
An lvalue A glvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2
T2” is reference-compatible with “cv1 T1” (8.5.3)…
Otherwise, an expression e can be explicitly converted to a type T… The expression e is used as
an lvalue a glvalue if and only if the initialization uses it as an lvalue a glvalue.
An rvalue A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to an
rvalue a prvalue of type “pointer to cv2 D,” where…If the rvalue prvalue of type “pointer to
cv1 B” points to a B…
An rvalue A prvalue of type “pointer to member of D of type cv1 T” can be converted to an
rvalue a prvalue of type “pointer to member of B” of type…
An rvalue A prvalue of type “pointer to cv1 void” can be converted to an rvalue a prvalue of
type…
5.2.10 Reinterpret cast ¶1, 6-8, 10-11:
…If T is an lvalue reference type or an rvalue reference to function type, the result is an
lvalue; if T is an rvalue reference to object type, the result is an rvalue xvalue; otherwise, the
result is an rvalue a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and functionto-pointer
(4.3) standard conversions are performed on the the expression v…
…Except that converting an rvalue a prvalue of type “pointer to T1” to the type…
… When an rvalue a prvalue v of type “pointer to T1” is converted… Converting an rvalue a
prvalue of type “pointer to T1” to the type…
…converting an rvalue a prvalue of one type to the other type…
An rvalue A prvalue of type “pointer to member of X of type T1” can be explicitly converted to
an rvalue a prvalue of type “pointer to member of Y of type T2” if T1 and T2 are both function
types or both object types.70 The null member pointer value (4.11) is converted to the null
member pointer value of the destination type. The result of this conversion is unspecified,
except in the following cases:
• converting an rvalue a prvalue of type “pointer to member function” to a different
pointer to member function type and back to its original type yields the original pointer
to member value.
• converting an rvalue a prvalue of type “pointer to data member of X of type T1” to the
type “pointer to data member of Y of type T2” (where the alignment requirements of T2
are no stricter than those of T1) and back to its original type yields the original pointer
page 13 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
to member value.
…The result is an lvalue for an lvalue references type or an rvalue reference to function type
or and an rvalue xvalue for an rvalue references to object type…
5.2.11 Const cast ¶1, 3-4, 9-10:
…If T is an lvalue reference type or an rvalue reference to function type, the result is an
lvalue; if T is an rvalue reference to object type, the result is an rvalue xvalue; otherwise, the
result is an rvalue a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and functionto-pointer
(4.3) standard conversions are performed on the the expression v…
…an rvalue a prvalue of type T1 may be explicitly converted…
…Similarly, for two object types T1 and T2, an expression of type T1 can be explicitly
converted to an rvalue xvalue of type T2 using the cast const_cast<T2&&>…
Casting from an lvalue of type T1 to an lvalue of type T2 using an lvalue reference cast or
casting from an expression of type T1 to an rvalue xvalue of type T2 using an rvalue reference
cast casts away constness if a cast from an rvalue a prvalue of type “pointer to T1” to the type
“pointer to T2” casts away constness.
Casting from an rvalue a prvalue of type “pointer to data member of X of type T1” to the type
“pointer to data member of Y of type T2” casts away constness if a cast from an rvalue a
prvalue of type “pointer to T1” to the type “pointer to T2” casts away constness.
5.3.1 Unary operators ¶1-2:
…[Note:…this lvalue must not be converted to an rvalue a prvalue, see 4.1. —end note]
The result of each of the following unary operators is an rvalue a prvalue.
5.4 Explicit type conversion (cast notation) ¶1, 4-5:
…The result is an lvalue if T is an lvalue reference type or an rvalue reference to function
type and an xvalue if T is an rvalue reference to object type;, otherwise the result is an
rvalue a prvalue. [Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are
ignored when determining the type of the resulting rvalue prvalue; see 3.10. —end note]
…the conversion is valid even if the base class is inaccessible:
• …
• a pointer to an object of an unambiguous non-virtual base class type, an lvalue or rvalue
a glvalue of an unambiguous non-virtual base class type, or a pointer to member of an
unambiguous non-virtual base class type may be explicitly converted to a pointer, a
reference, or a pointer to member of a derived class type, respectively.
The operand of a cast using the cast notation can be an rvalue a prvalue of type “pointer to
page 14 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
incomplete class type”…
5.5 Pointer-to-member operators ¶6:
…The result of a .* expression whose second operand is a pointer to a data member is an
lvalue only if its first operand is an lvalue and its second operand is a pointer to data member of
the same value category (3.10) as its first operand. The result of a .* expression whose
second operand is a pointer to a member function is a prvalue. The result of an ->*
expression is an lvalue only if its second operand is a pointer to data member and a prvalue
otherwise…
5.16 Conditional operator ¶2-6:
If either the second or the third operand has type (possibly cv-qualified) void…
• The second or the third operand (but not both) is a throw-expression (15.1); the result is
of the type of the other and is an rvalue a prvalue.
• Both the second and the third operands have type void; the result is of type void and
is an rvalue a prvalue…
Otherwise, if the second and third operand have different types and either has (possibly cvqualified)
class type, or if both are lvalues glvalues of the same value category and the same
type except for cv-qualification, an attempt is made to convert each of those operands to the
type of the other. The process for determining whether an operand expression E1 of type T1 can
be converted to match an operand expression E2 of type T2 is defined as follows:
• If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted
(Clause 4) to the type “lvalue reference to T2”, subject to the constraint that in the
conversion the reference must bind directly (8.5.3) to an lvalue.
• If E2 is an xvalue: E1 can be converted to match E2 if E1 can be implicitly
converted to the type “rvalue reference to T2,” subject to the constraint that the
reference must bind directly.
• If E2 is an rvalue or if neither of the conversions above cannot can be done and at least
one of the operands has (possibly cv-qualified) class type:
• if E1 and E2 have class type, and the underlying class types are the same or one
is a base class of the other: E1 can be converted to match E2 if the class of T2 is
the same type as, or a base class of, the class of T1, and the cv-qualification of
T2 is the same cv-qualification as, or a greater cv-qualification than, the cvqualification
of T1. If the conversion is applied, E1 is changed to an rvalue a
prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and
using that temporary as the converted operand.
• Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types
page 15 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
but the underlying classes are not either the same or one a base class of the
other): E1 can be converted to match E2 if E1 can be implicitly converted to the
type that expression E2 would have if E2 were converted to an rvalue a prvalue
(or the type it has, if E2 is an rvalue a prvalue).
Using this process, it is determined whether the second operand can be converted to
match the third operand, and whether the third operand can be converted to match the
second operand. If both can be converted, or one can be converted but the conversion is
ambiguous, the program is ill-formed. If neither can be converted, the operands are left
unchanged and further checking is performed as described below. If exactly one
conversion is possible, that conversion is applied to the chosen operand and the
converted operand is used in place of the original operand for the remainder of this
section.
If the second and third operands are lvalues glvalues of the same value category and have the
same type, the result is of that type and is an lvalue value category and it is a bit-field if the
second or the third operand is a bit-field, or if both are bit-fields.
Otherwise, the result is an rvalue a prvalue. If the second and third operands…
…After those conversions, one of the following shall hold:
• The second and third operands have the same type; the result is of that type. If the
operands have class type, the result is an rvalue a prvalue temporary of the result type…
5.18 Comma operator ¶1:
…the result is an lvalue if of the same value category as its right operand is an lvalue, and is a
bit-field if its right operand is an lvalue a glvalue and a bit-field.
7.1.6.1 Simple type specifiers ¶6:
If an attempt is made to refer to an object defined with a volatile-qualified type through the use
of an lvalue a glvalue with a non-volatile-qualified type, the program behavior is undefined.
8.3.2 References ¶1:
…[Example:
typedef int& A;
const A aref = 3; / ill-formed; lvalue reference to non-const reference
/ initialized with rvalue
The type of aref is “lvalue reference to int”, not “const lvalue reference to const int”.
—end example]
8.3.4 Arrays ¶5:
[Note: conversions affecting lvalues expressions of array type are described in 4.2. Objects of
array types cannot be modified, see 3.10. —end note]
page 16 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
8.5 Initializers ¶16:
The semantics of initializers are as follows…
• …
• If the destination type is a (possibly cv-qualified) class type:
• If the initialization is direct-initialization…
• Otherwise… The temporary is an rvalue a prvalue…
8.5.3 References ¶5:
• If the reference is an lvalue reference and the initializer expression…
• Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e.,
cv1 shall be const), or the reference shall be an rvalue reference and the initializer
expression shall be an rvalue or have a function type…
• If T1 is a function type, then
• if T2 is the same type as T1, the reference is bound to the initializer
expression lvalue;
• if T2 is a class type and the initializer expression can be implicitly
converted to an lvalue of type T1 (this conversion is selected by
enumerating the applicable conversion functions (13.3.1.6) and
choosing the best one through overload resolution (13.3)), the
reference is bound to the function lvalue that is the result of the
conversion;
• otherwise, the program is ill-formed.
• If Otherwise, if T1 and T2 are is a class types and…
8.5.4 List-initialization ¶3:
List-initialization of an object or reference of type T is defined as follows:
• …
• Otherwise, if T is a reference to class type or if T is any reference type and the initializer
list has no elements, an rvalue a prvalue temporary of the type referenced by T is listinitialized,
and the reference is bound to that temporary…
9.3.2 The this pointer ¶1:
In the body of a non-static (9.3) member function, the keyword this is an rvalue a prvalue
expression…
page 17 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
10.2 Member name lookup ¶12:
An explicit or implicit conversion from a pointer to or an lvalue an expression designating an
object of a derived class to a pointer or reference to one of its base classes shall unambiguously
refer to a unique object representing the base class…
12.1 Constructors ¶14:
During the construction of a const object, if the value of the object or any of its subobjects is
accessed through an lvalue a glvalue that is not obtained, directly or indirectly, from the
constructor’s this pointer, the value of the object or subobject thus obtained is unspecified.
[Example:…
12.2 Temporary objects ¶1:
Temporaries of class type are created in various contexts: binding an rvalue to a reference to a
prvalue (8.5.3), returning an rvalue a prvalue (6.6.3), a conversion that creates an rvalue a
prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1), entering a handler (15.3), and in
some initializations (8.5)…
12.7 Construction and destruction ¶3:
To explicitly or implicitly convert a pointer (an lvalue a glvalue) referring to an object of class
X to a pointer (reference) to a direct or indirect base class B of X…
13.3 Overload resolution ¶2:
Overload resolution selects the function to call in seven distinct contexts within the language:
• …
• invocation of a conversion function for conversion to an lvalue a glvalue or class rvalue
prvalue to which a reference (8.5.3) will be directly bound (13.3.1.6).
13.3.1 Candidate functions and argument lists ¶5:
…For non-static member functions declared without a ref-qualifier, an additional rule applies:
• even if the implicit object parameter is not const-qualified, an rvalue temporary can be
bound to the parameter as long as in all other respects the temporary argument can be
converted to the type of the implicit object parameter. [Note: The fact that such a
temporary an argument is an rvalue does not affect the ranking of implicit conversion
sequences (13.3.3.2). —end note]
13.3.1.1.1 Call to named function ¶2:
[Footnote:Note that cv-qualifiers on the type of objects are significant in overload resolution for
both lvalue glvalue and class rvalue prvalue objects. —end footnote]
page 18 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
13.3.1.4 Copy-initialization of class by user-defined conversion ¶1:
…the candidate functions are selected as follows:
• …
• …Conversion functions that return “reference to X” return lvalues or rvalues xvalues,
depending on the type of reference…
13.3.1.5 Initialization by conversion function ¶1:
…the candidate functions are selected as follows:
• …
• …Conversion functions that return “reference to cv2 X” return lvalues or rvalues
xvalues, depending on the type of reference…
13.3.1.6 Initialization by conversion function for direct reference binding ¶1:
Under the conditions specified in 8.5.3, a reference can be bound directly to an lvalue a glvalue
or class rvalue prvalue that is the result of applying a conversion function to an initializer
expression…
13.3.2 Viable functions ¶3:
If the parameter has reference type, the implicit conversion sequence includes the operation of
binding the reference, and the fact that an lvalue reference to non-const cannot be bound to an
rvalue and that an rvalue reference cannot be bound to an lvalue can affect the viability of
the function (see 13.3.3.1.4).
13.3.3.1 Implicit conversion sequences ¶2, 6:
Implicit conversion sequences are concerned only with the type, cv-qualification, and lvalueness
value category of the argument…
When the parameter type is not a reference, the implicit conversion sequence models a copyinitialization
of the parameter from the argument expression. The implicit conversion sequence
is the one required to convert the argument expression to an rvalue a prvalue of the type of the
parameter…
13.3.3.1.1 Standard conversion sequences ¶1:
…[Note: these categories are orthogonal with respect to lvalue-ness value category, cvqualification,
and data representation: the Lvalue Transformations do not change the cvqualification
or data representation of the type; the Qualification Adjustments do not change the
lvalue-ness value category or data representation of the type; and the Promotions and
Conversions do not change the lvalue-ness value category or cv-qualification of the type. —
end note]
page 19 of 20
PL22.16/10-0045 = WG21 N3055: Value Category Taxonomy, Rev. 6 2010-03-12
13.3.3.2 Ranking implicit conversion sequences ¶3:
Two implicit conversion sequences of the same form are indistinguishable conversion
sequences unless one of the following rules applies:
• Standard conversion sequence S1 is a better conversion sequence than standard
conversion sequence S2 if
• …
• S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object
parameter of a non-static member function declared without a ref-qualifier, and
S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.
[Example:
int i;
int f1();
int&& f2();
int g(const int&);
int g(const int&&);
int j = g(i); / calls g(const int&)
int k = g(f1()); / calls g(const int&&)
int l = g(f2()); / calls g(const int&&)

—end example]
14.2 Template parameters ¶6:
A non-type non-reference template-parameter is an rvalue a prvalue…

 

6476508720

内容来源:/www.infoq.com/cn/articles/cache-coherency-primer/
英文文章原文:/fgiesen.wordpress.com/2014/07/07/cache-coherency/

本文是RAD Game Tools程序员Fabian “ryg” Giesen在其博客上发表的《Cache coherency primer》一文的翻译,经作者许可分享至InfoQ中文站。该系列共有两篇,本文系第一篇。

我计划写一些关于多核场景下数据组织的文章。写了第一篇,但我很快意识到有大量的基础知识我首先需要讲一下。在本文中,我就尝试阐述这些知识。

缓存(Cache)
本文是关于CPU缓存的快速入门。我假设你已经有了基本概念,但你可能不熟悉其中的一些细节。(如果你已经熟悉了,你可以忽略这部分。)

在现代的CPU(大多数)上,所有的内存访问都需要通过层层的缓存来进行。也有些例外,比如,对映射成内存地址的I/O口、写合并(Write-combined)内存,这些访问至少会绕开这个流程的一部分。但这两者都是罕见的场景(意味着绝大多数的用户态代码都不会遇到这两种情况),所以在本文中,我将忽略这两者。

CPU的读/写(以及取指令)单元正常情况下甚至都不能直接访问内存——这是物理结构决定的;CPU都没有管脚直接连到内存。相反,CPU和一级缓存(L1 Cache)通讯,而一级缓存才能和内存通讯。大约二十年前,一级缓存可以直接和内存传输数据。如今,更多级别的缓存加入到设计中,一级缓存已经不能直接和内存通讯了,它和二级缓存通讯——而二级缓存才能和内存通讯。或者还可能有三级缓存。你明白这个意思就行。

缓存是分“段”(line)的,一个段对应一块存储空间,大小是32(较早的ARM、90年代/2000年代早期的x86和PowerPC)、64(较新的ARM和x86)或128(较新的Power ISA机器)字节。每个缓存段知道自己对应什么范围的物理内存地址,并且在本文中,我不打算区分物理上的缓存段和它所代表的内存,这听起来有点草率,但是为了方便起见,还是请熟悉这种提法。具体地说,当我提到“缓存段”的时候,我就是指一段和缓存大小对齐的内存,不关心里面的内容是否真正被缓存进去(就是说保存在任何级别的缓存中)了。

当CPU看到一条读内存的指令时,它会把内存地址传递给一级数据缓存(或可戏称为L1D$,因为英语中“缓存(cache)”和“现金(cash)”的发音相同)。一级数据缓存会检查它是否有这个内存地址对应的缓存段。如果没有,它会把整个缓存段从内存(或者从更高一级的缓存,如果有的话)中加载进来。是的,一次加载整个缓存段,这是基于这样一个假设:内存访问倾向于本地化(localized),如果我们当前需要某个地址的数据,那么很可能我们马上要访问它的邻近地址。一旦缓存段被加载到缓存中,读指令就可以正常进行读取。

如果我们只处理读操作,那么事情会很简单,因为所有级别的缓存都遵守以下规律,我称之为:

基本定律:在任意时刻,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。

一旦我们允许写操作,事情就变得复杂一点了。这里有两种基本的写模式:直写(write-through)和回写(write-back)。直写更简单一点:我们透过本级缓存,直接把数据写到下一级缓存(或直接到内存)中,如果对应的段被缓存了,我们同时更新缓存中的内容(甚至直接丢弃),就这么简单。这也遵守前面的定律:缓存中的段永远和它对应的内存内容匹配。

回写模式就有点复杂了。缓存不会立即把写操作传递到下一级,而是仅修改本级缓存中的数据,并且把对应的缓存段标记为“脏”段。脏段会触发回写,也就是把里面的内容写到对应的内存或下一级缓存中。回写后,脏段又变“干净”了。当一个脏段被丢弃的时候,总是先要进行一次回写。回写所遵循的规律有点不同。

回写定律:当所有的脏段被回写后,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。

换句话说,回写模式的定律中,我们去掉了“在任意时刻”这个修饰语,代之以弱化一点的条件:要么缓存段的内容和内存一致(如果缓存段是干净的话),要么缓存段中的内容最终要回写到内存中(对于脏缓存段来说)。

直接模式更简单,但是回写模式有它的优势:它能过滤掉对同一地址的反复写操作,并且,如果大多数缓存段都在回写模式下工作,那么系统经常可以一下子写一大片内存,而不是分成小块来写,前者的效率更高。

有些(大多数是比较老的)CPU只使用直写模式,有些只使用回写模式,还有一些,一级缓存使用直写而二级缓存使用回写。这样做虽然在一级和二级缓存之间产生了不必要的数据流量,但二级缓存和更低级缓存或内存之间依然保留了回写的优势。我想说的是,这里涉及到一系列的取舍问题,且不同的设计有不同的解决方案。没有人规定各级缓存的大小必须一致。举个例子,我们会看到有CPU的一级缓存是32字节,而二级缓存却有128字节。

为了简化问题,我省略了一些内容:缓存关联性(cache associativity),缓存组(cache sets),使用分配写(write-allocate)还是非分配写(上面我描述的直写是和分配写相结合的,而回写是和非分配写相结合的),非对齐的访问(unaligned access),基于虚拟地址的缓存。如果你感兴趣,所有这些内容都可以去查查资料,但我不准备在这里讲了。

一致性协议(Coherency protocols)
只要系统只有一个CPU核在工作,一切都没问题。如果有多个核,每个核又都有自己的缓存,那么我们就遇到问题了:如果某个CPU缓存段中对应的内存内容被另外一个CPU偷偷改了,会发生什么?

好吧,答案很简单:什么也不会发生。这很糟糕。因为如果一个CPU缓存了某块内存,那么在其他CPU修改这块内存的时候,我们希望得到通知。我们拥有多组缓存的时候,真的需要它们保持同步。或者说,系统的内存在各个CPU之间无法做到与生俱来的同步,我们实际上是需要一个大家都能遵守的方法来达到同步的目的。

注意,这个问题的根源是我们拥有多组缓存,而不是多个CPU核。我们也可以这样解决问题,让多个CPU核共用一组缓存:也就是说只有一块一级缓存,所有处理器都必须共用它。在每一个指令周期,只有一个幸运的CPU能通过一级缓存做内存操作,运行它的指令。

这本身没问题。唯一的问题就是太慢了,因为这下处理器的时间都花在排队等待使用一级缓存了(并且处理器会做大量的这种操作,至少每个读写指令都要做一次)。我指出这一点是因为它表明了问题不是由多核引起的,而是由多缓存引起的。我们知道了只有一组缓存也能工作,只是太慢了,接下来最好就是能做到:使用多组缓存,但使它们的行为看起来就像只有一组缓存那样。缓存一致性协议就是为了做到这一点而设计的。就像名称所暗示的那样,这类协议就是要使多组缓存的内容保持一致。

缓存一致性协议有多种,但是你日常处理的大多数计算机设备使用的都属于“窥探(snooping)”协议,这也是我这里要讲的。(还有一种叫“基于目录的(directory-based)”协议,这种协议的延迟性较大,但是在拥有很多个处理器的系统中,它有更好的可扩展性。)

“窥探”背后的基本思想是,所有内存传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(arbitrate):同一个指令周期中,只有一个缓存可以读写内存。窥探协议的思想是,缓存不仅仅在做内存传输的时候才和总线打交道,而是不停地在窥探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表它所属的处理器去读写内存时,其他处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其他处理器马上就知道这块内存在它们自己的缓存中对应的段已经失效。

在直写模式下,这是很直接的,因为写操作一旦发生,它的效果马上会被“公布”出去。但是如果混着回写模式,就有问题了。因为有可能在写指令执行过后很久,数据才会被真正回写到物理内存中——在这段时间内,其他处理器的缓存也可能会傻乎乎地去写同一块内存地址,导致冲突。在回写模型中,简单把内存写操作的信息广播给其他处理器是不够的,我们需要做的是,在修改本地缓存之前,就要告知其他处理器。搞懂了细节,就找到了处理回写模式这个问题的最简单方案,我们通常叫做MESI协议(译者注:MESI是Modified、Exclusive、Shared、Invalid的首字母缩写,代表四种缓存状态,下面的译文中可能会以单个字母指代相应的状态)。

MESI以及衍生协议
本节叫做“MESI以及衍生协议”,是因为MESI衍生了一系列紧密相关的一致性协议。我们先从原生的MESI协议开始:MESI是四种缓存段状态的首字母缩写,任何多核系统中的缓存段都处于这四种状态之一。我将以相反的顺序逐个讲解,因为这个顺序更合理:

失效(Invalid)缓存段,要么已经不在缓存中,要么它的内容已经过时。为了达到缓存的目的,这种状态的段将会被忽略。一旦缓存段被标记为失效,那效果就等同于它从来没被加载到缓存中。
共享(Shared)缓存段,它是和主内存内容保持一致的一份拷贝,在这种状态下的缓存段只能被读取,不能被写入。多组缓存可以同时拥有针对同一内存地址的共享缓存段,这就是名称的由来。
独占(Exclusive)缓存段,和S状态一样,也是和主内存内容保持一致的一份拷贝。区别在于,如果一个处理器持有了某个E状态的缓存段,那其他处理器就不能同时持有它,所以叫“独占”。这意味着,如果其他处理器原本也持有同一缓存段,那么它会马上变成“失效”状态。
已修改(Modified)缓存段,属于脏段,它们已经被所属的处理器修改了。如果一个段处于已修改状态,那么它在其他处理器缓存中的拷贝马上会变成失效状态,这个规律和E状态一样。此外,已修改缓存段如果被丢弃或标记为失效,那么先要把它的内容回写到内存中——这和回写模式下常规的脏段处理方式一样。
如果把以上这些状态和单核系统中回写模式的缓存做对比,你会发现I、S和M状态已经有对应的概念:失效/未载入、干净以及脏的缓存段。所以这里的新知识只有E状态,代表独占式访问。这个状态解决了“在我们开始修改某块内存之前,我们需要告诉其他处理器”这一问题:只有当缓存段处于E或M状态时,处理器才能去写它,也就是说只有这两种状态下,处理器是独占这个缓存段的。当处理器想写某个缓存段时,如果它没有独占权,它必须先发送一条“我要独占权”的请求给总线,这会通知其他处理器,把它们拥有的同一缓存段的拷贝失效(如果它们有的话)。只有在获得独占权后,处理器才能开始修改数据——并且此时,这个处理器知道,这个缓存段只有一份拷贝,在我自己的缓存里,所以不会有任何冲突。

反之,如果有其他处理器想读取这个缓存段(我们马上能知道,因为我们一直在窥探总线),独占或已修改的缓存段必须先回到“共享”状态。如果是已修改的缓存段,那么还要先把内容回写到内存中。

MESI协议是一个合适的状态机,既能处理来自本地处理器的请求,也能把信息广播到总线上。我不打算讲更多关于状态图的细节以及不同的状态转换类型。如果你感兴趣的话,可以在关于硬件架构的书中找到更多的深度内容,但对于本文来说,讲这些东西有点过了。作为一个软件开发者,你只要理解以下两点,就大有可为:

第一,在多核系统中,读取某个缓存段,实际上会牵涉到和其他处理器的通讯,并且可能导致它们发生内存传输。写某个缓存段需要多个步骤:在你写任何东西之前,你首先要获得独占权,以及所请求的缓存段的当前内容的拷贝(所谓的“带权限获取的读(Read For Ownership)”请求)。

第二,尽管我们为了一致性问题做了额外的工作,但是最终结果还是非常有保证的。即它遵守以下定理,我称之为:

MESI定律:在所有的脏缓存段(M状态)被回写后,任意缓存级别的所有缓存段中的内容,和它们对应的内存中的内容一致。此外,在任意时刻,当某个位置的内存被一个处理器加载入独占缓存段时(E状态),那它就不会再出现在其他任何处理器的缓存中。

注意,这其实就是我们已经讲过的回写定律加上独占规则而已。我认为MESI协议或多核系统的存在根本没有弱化我们现有的内存模型。

好了,至此我们(粗略)讲了原生MESI协议(以及使用它的CPU,比如ARM)。其他处理器使用MESI扩展后的变种。常见的扩展包括“O”(Owned)状态,它和E状态类似,也是保证缓存间一致性的手段,但它直接共享脏段的内容,而不需要先把它们回写到内存中(“脏段共享”),由此产生了MOSEI协议。还有MERSI和MESIF,这两个名字代表同一种思想,即指定某个处理器专门处理针对某个缓存段的读操作。当多个处理器同时拥有某个S状态的缓存段的时候,只有被指定的那个处理器(对应的缓存段为R或F状态)才能对读操作做出回应,而不是每个处理器都能这么做。这种设计可以降低总线的数据流量。当然你可以同时加入R/F状态和O状态,或者更多的状态。这些都属于优化,没有一种会改变基本定律,也没有一种会改变MESI协议所确保的结果。

我不是这方面的专家,很有可能有系统在使用其他协议,这些协议并不能完全保证一致性,不过如果有,我没有注意到它们,或者没有看到有什么流行的处理器在使用它们。所以为了达到我们的目的,我们真的就可以假设一致性协议能保证缓存的一致性。不是基本一致,不是“写入一会儿后才能保持一致”——而是完全的一致。从这个层面上说,除非硬件有问题,内存的状态总是一致的。用技术术语来说,MESI以及它的衍生协议,至少在原理上,提供了完整的顺序一致性(sequential consistency),在C++ 11的内存模型中,这是最强的一种确保内存顺序的模型。这也引出了问题,为什么我们需要弱一点的内存模型,以及“什么时候会用到它们”?

内存模型
不同的体系结构提供不同的内存模型。到本文写作的时候为止,ARM和POWER体系结构的机器拥有相对较弱的内存模型:这类CPU在读写指令重排序(reordering)方面有相当大的自由度,这种重排序有可能会改变程序在多核环境下的语义。通过“内存屏障(memory barrier)”,程序可以对此加以限制:“重排序操作不允许越过这条边界”。相反,x86则拥有较强的内存模型。

我不打算在这里深入到内存模型的细节中,这很容易陷入堆砌技术术语中,而且也超出了本文的范围。但是我想说一点关于“他们如何发生”的内容——也就是,弱内存模型如何保证正确性(相比较于MESI协议给缓存带来的顺序一致性),以及为什么。当然,一切都归结于性能。

规则是这样的:如果满足下面的条件,你就可以得到完全的顺序一致性:第一,缓存一收到总线事件,就可以在当前指令周期中迅速做出响应。第二,处理器如实地按程序的顺序,把内存操作指令送到缓存,并且等前一条执行完后才能发送下一条。当然,实际上现代处理器一般都无法满足以上条件:

缓存不会及时响应总线事件。如果总线上发来一条消息,要使某个缓存段失效,但是如果此时缓存正在处理其他事情(比如和CPU传输数据),那这个消息可能无法在当前的指令周期中得到处理,而会进入所谓的“失效队列(invalidation queue)”,这个消息等在队列中直到缓存有空为止。
处理器一般不会严格按照程序的顺序向缓存发送内存操作指令。当然,有乱序执行(Out-of-Order execution)功能的处理器肯定是这样的。顺序执行(in-order execution)的处理器有时候也无法完全保证内存操作的顺序(比如想要的内存不在缓存中时,CPU就不能为了载入缓存而停止工作)。
写操作尤其特殊,因为它分为两阶段操作:在写之前我们先要得到缓存段的独占权。如果我们当前没有独占权,我们先要和其他处理器协商,这也需要一些时间。同理,在这种场景下让处理器闲着无所事事是一种资源浪费。实际上,写操作首先发起获得独占权的请求,然后就进入所谓的由“写缓冲(store buffer)”组成的队列(有些地方使用“写缓冲”指代整个队列,我这里使用它指代队列的一条入口)。写操作在队列中等待,直到缓存准备好处理它,此时写缓冲就被“清空(drained)”了,缓冲区被回收用于处理新的写操作。
这些特性意味着,默认情况下,读操作有可能会读到过时的数据(如果对应失效请求还等在队列中没执行),写操作真正完成的时间有可能比它们在代码中的位置晚,一旦牵涉到乱序执行,一切都变得模棱两可。回到内存模型,本质上只有两大阵营:

在弱内存模型的体系结构中,处理器为了开发者能写出正确的代码而做的工作是最小化的,指令重排序和各种缓冲的步骤都是被正式允许的,也就是说没有任何保证。如果你需要确保某种结果,你需要自己插入合适的内存屏障——它能防止重排序,并且等待队列中的操作全部完成。

使用强一点的内存模型的体系结构则会在内部做很多记录工作。比如,x86会跟踪所有在等待中的内存操作,这些操作都还没有完全完成(称为“退休(retired)”)。它会把它们的信息保存在芯片内部的MOB(“memory ordering buffer”,内存排序缓冲)。x86作为部分支持乱序执行的体系结构,在出问题的时候能把尚未“退休”的指令撤销掉——比如发生页错误(page fault),或者分支预测失败(branch mispredict)的时候。我已经在我以前的文章“好奇地说”中提到过一些细节,以及和内存子系统的一些交互。主旨是x86处理器会主动地监控外部事件(比如缓存失效),有些已经执行完的操作会因为这些事件而被撤销,但不算“退休”。这就是说,x86知道自己的内存模型应该是什么样子的,当发生了一件和这个模型冲突的事,处理器会回退到上一个与内存模型兼容的状态。这就是我在以前另一篇文章中提到的“清除内存排序机(memory ordering machine clear)”。最后的结果是,x86处理器为内存操作提供了很强的一致性保证——虽然没有达到完美的顺序一致性。

无论如何,一篇文章讲这么多已经够了。我把它放在我的博客上。我的想法是将来的文章只要引用它就行了。我们看效果吧。感谢阅读

314-554-6226

The Pthreads API

  • The original Pthreads API was defined in the ANSI/IEEE POSIX 1003.1 – 1995 standard. The POSIX standard has continued to evolve and undergo revisions, including the Pthreads specification.
  • Copies of the standard can be purchased from IEEE or downloaded for free from other sites online.
  • The subroutines which comprise the Pthreads API can be informally grouped into four major groups:
    1. Thread management: Routines that work directly on threads – creating, detaching, joining, etc. They also include functions to set/query thread attributes (joinable, scheduling etc.)
    2. Mutexes: Routines that deal with synchronization, called a “mutex”, which is an abbreviation for “mutual exclusion”. Mutex functions provide for creating, destroying, locking and unlocking mutexes. These are supplemented by mutex attribute functions that set or modify attributes associated with mutexes.
    3. Condition variables: Routines that address communications between threads that share a mutex. Based upon programmer specified conditions. This group includes functions to create, destroy, wait and signal based upon specified variable values. Functions to set/query condition variable attributes are also included.
    4. Synchronization: Routines that manage read/write locks and barriers.

Routine Prefix : Functional Group
pthread_ : Threads themselves and miscellaneous subroutines
pthread_attr_ : Thread attributes objects
pthread_mutex_ : Mutexes
pthread_mutexattr_ : Mutex attributes objects
pthread_cond_ : Condition variables
pthread_condattr_ : Condition attributes objects
pthread_key_ : Thread-specific data keys
pthread_rwlock_ : Read/write locks
pthread_barrier_ : Synchronization barriers
Thread Management
Creating and Terminating Threads
Routines:

pthread_create (thread, attr, start_routine, arg)
pthread_exit (status)
pthread_cancel (thread)
pthread_attr_init (attr)
pthread_attr_destroy (attr)

Creating Threads:

  • Initially, your main() program comprises a single, default thread. All other threads must be explicitly created by the programmer.
  • pthread_create creates a new thread and makes it executable. This routine can be called any number of times from anywhere within your code.
  • pthread_create arguments:
    • thread: An opaque, unique identifier for the new thread returned by the subroutine.
    • attr: An opaque attribute object that may be used to set thread attributes. You can specify a thread attributes object, or NULL for the default values.
    • start_routine: the C routine that the thread will execute once it is created.
    • arg: A single argument that may be passed to start_routine. It must be passed by reference as a pointer cast of type void. NULL may be used if no argument is to be passed.
  • The maximum number of threads that may be created by a process is implementation dependent. Programs that attempt to exceed the limit can fail or produce wrong results.
  • Querying and setting your implementation’s thread limit – Linux example shown. Demonstrates querying the default (soft) limits and then setting the maximum number of processes (including threads) to the hard limit. Then verifying that the limit has been overridden.
$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
 
$ ulimit -Hu
7168
 
$ ulimit -u 7168
 
$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7168
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Thread Attributes:

    • By default, a thread is created with certain attributes. Some of these attributes can be changed by the programmer via the thread attribute object.
    • pthread_attr_init

      and

      pthread_attr_destroy

      are used to initialize/destroy the thread attribute object.

      • Detached or joinable state
      • Scheduling inheritance
      • Scheduling policy
      • Scheduling parameters
      • Scheduling contention scope
      • Stack size
      • Stack address
      • Stack guard (overflow) size

Terminating Threads pthread_exit():

    There are several ways in which a therad may be terminated:

  • The thread returns normally from its starting routine. Its work is done.
  • The thread makes a call to the pthread_exit subroutine – whether its work is done or not.
  • The thread is canceled by another thread via the pthread_cancel routine.
  • The entire process is terminated due to making a call to either the exec() or exit()
  • If main() finishes first, without calling pthread_exit explicitly itself

The pthread_exit() routine allows the programmer to specify an optional termination status parameter. This optional parameter is typically returned to threads “joining” the terminated thread (covered later).
In subroutines that execute to completion normally, you can often dispense with calling pthread_exit() – unless, of course, you want to pass the optional status code back.
Cleanup: the pthread_exit() routine does not close files; any files opened inside the thread will remain open after the thread is terminated.
Discussion on calling pthread_exit() from main():

  • There is a definite problem if main() finishes before the threads it spawned if you don’t call pthread_exit() explicitly. All of the threads it created will terminate because main() is done and no longer exists to support the threads.
  • By having main() explicitly call pthread_exit() as the last thing it does, main() will block and be kept alive to support the threads it created until they are done.

Pthread Creationg and Termination Example

#include 
#include 
#define NUM_THREADS     5

 void *PrintHello(void *threadid)
 {
    long tid;
    tid = (long)threadid;
    printf("Hello World! It's me, thread #%ld!\n", tid);
    pthread_exit(NULL);
 }

 int main (int argc, char *argv[])
 {
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for(t=0; t < NUM_THREADS; t++){
       printf("In main: creating thread %ld\n", t);
       rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
       if (rc){
          printf("ERROR; return code from pthread_create() is %d\n", rc);
          exit(-1);
       }
    }

    /* Last thing that main() should do */
    pthread_exit(NULL);
 }


Thread Argument Passing(Incorrect)
This example performs argument passing incorrectly. It passes the address of variable t, which is shared memory space and visible to all threads. As the loop iterates, the value of this memory location changes, possibly before the created threads can access it.

int rc;
long t;

for(t=0; t < NUM_THREADS; t++) 
{
   printf("Creating thread %ld\n", t);
   rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t);
   ...
}

Thread Management

Routines:

pthread_join(threadid, status)
pthread_detach 9threadid)
pthread_attr_setdetachstate(attr, detachstate)
pthread_attr_getdetachstate9attr, detachstate)
  • Joining:
    "Joining" is one way to accomplish synchronization between threads.For example:
    Joining
  • The pthread_join() subroutine blocks the calling thread until the specified threadid thread terminates.
  • The programmer is able to obtain the target thread's termination return status if it was specified in the target thread's call to pthread_exit().
  • A joining thread can match one pthread_join() call. It is a logical error to attempt multiple joins on the same thread.
  • Two other synchronization methods, mutexes and condition variables, will be discussed later.

Joinable or Not?

  • When a thread is created, one of its attributes defines whether it is joinable or detached. Only threads that are created as joinable can be joined. If a thread is created as detached, it can never be joined.
  • The final draft of the POSIX standard specifies that threads should be created as joinable.
    1. To explicitly create a thread as joinable or detached, the attr argument in the pthread_create() routine is used. The typical 4 step process is:

    2. Declare a pthread attribute variable of the pthread_attr_t data type
    3. Initialize the attribute variable with pthread_attr_init()
    4. Set the attribute detached status with pthread_attr_setdetachstate()
    5. When done, free library resources used by the attribute with pthread_attr_destroy()

Detaching:

  • The pthread_detach() routine can be used to explicitly detach a thread even though it was created as joinable.
  • There is no converse routine.

Recommendations:

  • If a thread requires joining, consider explicitly creating it as joinable. This provides portability as not all implementations may create threads as joinable by default.
  • If you know in advance that a thread will never need to join with another thread, consider creating it in a detached state. Some system resources may be able to be freed.

Example:Pthread Joining

  • This example demonstrates how to "wait" for thread completions by using the Pthread join routine.
  • Since some implementations of Pthreads may not create threads in a joinable state, the threads in this example are explicitly created in a joinable state so that they can be joined later.
  • #include 
    #include 
    #include 
    #include 
    #define NUM_THREADS	4
    
     void *BusyWork(void *t)
     {
        int i;
        long tid;
        double result=0.0;
        tid = (long)t;
        printf("Thread %ld starting...\n",tid);
        for (i=0; i < 1000000; i++)
        {
           result = result + sin(i) * tan(i);
        }
        printf("Thread %ld done. Result = %e\n",tid, result);
        pthread_exit((void*) t);
     }
    
     int main (int argc, char *argv[])
     {
        pthread_t thread[NUM_THREADS];
        pthread_attr_t attr;
        int rc;
        long t;
        void *status;
    
        /* Initialize and set thread detached attribute */
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
        for(t=0; t < NUM_THREADS; t++) {
           printf("Main: creating thread %ld\n", t);
           rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);  
           if (rc) {
              printf("ERROR; return code from pthread_create() is %d\n", rc);
              exit(-1);
              }
           }
    
        /* Free attribute and wait for the other threads */
        pthread_attr_destroy(&attr);
        for(t=0; t < NUM_THREADS; t++) {
           rc = pthread_join(thread[t], &status);
           if (rc) {
              printf("ERROR; return code from pthread_join() is %d\n", rc);
              exit(-1);
              }
           printf("Main: completed join with thread %ld having a status   
                 of %ld\n",t,(long)status);
           }
     
     printf("Main: program completed. Exiting.\n");
     pthread_exit(NULL);
     }
    

Stack Management
Routines:

pthread_attr_getstacksize (attr, stacksize)
pthread_attr_setstacksize (attr, stacksize)
pthread_attr_getstackaddr (attr, stackaddr)
pthread_attr_setstackaddr (attr, stackaddr)

    Preventing Stack Problems:

  • The POSIX standard does not dictate the size of a thread's stack. This is implementation dependent and varies.
  • Exceeding the default stack limit is often very easy to do, with the usual results: program termination and/or corrupted data.
  • Safe and portable programs do not depend upon the default stack limit, but instead, explicitly allocate enough stack for each thread by using the pthread_attr_setstacksize routine.
  • The pthread_attr_getstackaddr and pthread_attr_setstackaddr routines can be used by applications in an environment where the stack for a thread must be placed in some particular region of memory.

Stack Management Example

#include 
 #include 
 #define NTHREADS 4
 #define N 1000
 #define MEGEXTRA 1000000
 
 pthread_attr_t attr;
 
 void *dowork(void *threadid)
 {
    double A[N][N];
    int i,j;
    long tid;
    size_t mystacksize;

    tid = (long)threadid;
    pthread_attr_getstacksize (&attr, &mystacksize);
    printf("Thread %ld: stack size = %li bytes \n", tid, mystacksize);
    for (i=0; i < N; i++)
      for (j=0; j < N; j++)
       A[i][j] = ((i*j)/3.452) + (N-i);
    pthread_exit(NULL);
 }
 
 int main(int argc, char *argv[])
 {
    pthread_t threads[NTHREADS];
    size_t stacksize;
    int rc;
    long t;
 
    pthread_attr_init(&attr);
    pthread_attr_getstacksize (&attr, &stacksize);
    printf("Default stack size = %li\n", stacksize);
    stacksize = sizeof(double)*N*N+MEGEXTRA;
    printf("Amount of stack needed per thread = %li\n",stacksize);
    pthread_attr_setstacksize (&attr, stacksize);
    printf("Creating threads with stack size = %li bytes\n",stacksize);
    for(t=0; t < NTHREADS; t++){
       rc = pthread_create(&threads[t], &attr, dowork, (void *)t);
       if (rc){
          printf("ERROR; return code from pthread_create() is %d\n", rc);
          exit(-1);
       }
    }
    printf("Created %ld threads.\n", t);
    pthread_exit(NULL);
 }

Thread Management
Miscellaneous Routines
pthread_self ()
pthread_equal (thread1,thread2)

  • pthread_self returns the unique, system assigned thread ID of the calling thread.
  • pthread_equal compares two thread IDs. If the two IDs are different 0 is returned, otherwise a non-zero value is returned.
  • Note that for both of these routines, the thread identifier objects are opaque and can not be easily inspected. Because thread IDs are opaque objects, the C language equivalence operator == should not be used to compare two thread IDs against each other, or to compare a single thread ID against another value.pthread_once (once_control, init_routine)
  • pthread_once executes the init_routine exactly once in a process. The first call to this routine by any thread in the process executes the given init_routine, without parameters. Any subsequent call will have no effect.
  • The init_routine routine is typically an initialization routine.
  • The once_control parameter is a synchronization control structure that requires initialization prior to calling pthread_once. For example:
    pthread_once_t once_control = PTHREAD_ONCE_INIT;

Mutex Variables
A typical sequence in the use of a mutex is as follows:

  • Create and initialize a mutex variable
  • Several threads attempt to lock the mutex
  • Only one succeeds and that thread owns the mutex
  • The owner thread performs some set of actions
  • The owner unlocks the mutex
  • Another thread acquires the mutex and repeats the process
  • Finally the mutex is destroyed

Routines:

pthread_mutex_init (mutex,attr)
pthread_mutex_destroy (mutex)
pthread_mutexattr_init (attr)
pthread_mutexattr_destroy (attr)

Usage:

  • Mutex variables must be declared with type pthread_mutex_t, and must be initialized before they can be used. There are two ways to initialize a mutex variable:
    1. Statically, when it is declared. For example:
      pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
    2. Dynamically, with the pthread_mutex_init() routine. This method permits setting mutex object attributes, attr.
  • he attr object is used to establish properties for the mutex object, and must be of type pthread_mutexattr_t if used (may be specified as NULL to accept defaults). The Pthreads standard defines three optional mutex attributes:
    1. Protocol: Specifies the protocol used to prevent priority inversions for a mutex.
    2. Prioceiling: Specifies the priority ceiling of a mutex.
    3. Process-shared: Specifies the process sharing of a mutex.
    4. Note that not all implementations may provide the three optional mutex attributes.

  • The pthread_mutexattr_init() and pthread_mutexattr_destroy() routines are used to create and destroy mutex attribute objects respectively.
  • pthread_mutex_destroy() should be used to free a mutex object which is no longer needed.

Locking and Unlocking Mutexes
Routings: pthread_mutex_lock (mutex)
pthread_mutex_trylock (mutex)

pthread_mutex_unlock (mutex)

  • The pthread_mutex_lock() routine is used by a thread to acquire a lock on the specified mutex variable. If the mutex is already locked by another thread, this call will block the calling thread until the mutex is unlocked.
  • pthread_mutex_trylock() will attempt to lock a mutex. However, if the mutex is already locked, the routine will return immediately with a "busy" error code. This routine may be useful in preventing deadlock conditions, as in a priority-inversion situation.
  • pthread_mutex_unlock() will unlock a mutex if called by the owning thread. Calling this routine is required after a thread has completed its use of protected data if other threads are to acquire the mutex for their work with the protected data. An error will be returned if:
    1. If the mutex was already unlocked
    2. If the mutex is owned by another thread

Example:

#include 
 #include 
 #include 

 /*   
 The following structure contains the necessary information  
 to allow the function "dotprod" to access its input data and 
 place its output into the structure.  
 */

 typedef struct 
  {
    double      *a;
    double      *b;
    double     sum; 
    int     veclen; 
  } DOTDATA;

 /* Define globally accessible variables and a mutex */

 #define NUMTHRDS 4
 #define VECLEN 100
    DOTDATA dotstr; 
    pthread_t callThd[NUMTHRDS];
    pthread_mutex_t mutexsum;

 /*
 The function dotprod is activated when the thread is created.
 All input to this routine is obtained from a structure 
 of type DOTDATA and all output from this function is written into
 this structure. The benefit of this approach is apparent for the 
 multi-threaded program: when a thread is created we pass a single
 argument to the activated function - typically this argument
 is a thread number. All  the other information required by the 
 function is accessed from the globally accessible structure. 
 */  

 void *dotprod(void *arg)
 {

    /* Define and use local variables for convenience */

    int i, start, end, len ;
    long offset;
    double mysum, *x, *y;
    offset = (long)arg;
     
    len = dotstr.veclen;
    start = offset*len;
    end   = start + len;
    x = dotstr.a;
    y = dotstr.b;

    /*
    Perform the dot product and assign result
    to the appropriate variable in the structure. 
    */

    mysum = 0;
    for (i=start; i < end ; i++) 
     {
       mysum += (x[i] * y[i]);
     }

    /*
    Lock a mutex prior to updating the value in the shared
    structure, and unlock it upon updating.
    */
    pthread_mutex_lock (&mutexsum);
    dotstr.sum += mysum;
    pthread_mutex_unlock (&mutexsum);

    pthread_exit((void*) 0);
 }

 /* 
 The main program creates threads which do all the work and then 
 print out result upon completion. Before creating the threads,
 the input data is created. Since all threads update a shared structure, 
 we need a mutex for mutual exclusion. The main thread needs to wait for
 all threads to complete, it waits for each one of the threads. We specify
 a thread attribute value that allow the main thread to join with the
 threads it creates. Note also that we free up handles when they are
 no longer needed.
 */

 int main (int argc, char *argv[])
 {
    long i;
    double *a, *b;
    void *status;
    pthread_attr_t attr;  

    /* Assign storage and initialize values */
    a = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
    b = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
   
    for (i=0; i < VECLEN*NUMTHRDS; i++)
      {
      a[i]=1.0;
      b[i]=a[i];
      }

    dotstr.veclen = VECLEN; 
    dotstr.a = a; 
    dotstr.b = b; 
    dotstr.sum=0;

    pthread_mutex_init(&mutexsum, NULL);
         
    /* Create threads to perform the dotproduct  */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for(i=0; i < NUMTHRDS; i++)
    {
    /* 
    Each thread works on a different set of data. The offset is specified 
    by 'i'. The size of the data for each thread is indicated by VECLEN.
    */
    pthread_create(&callThd[i], &attr, dotprod, (void *)i);
    }

    pthread_attr_destroy(&attr);

    /* Wait on the other threads */
    for(i=0; i < NUMTHRDS; i++)
       {
       pthread_join(callThd[i], &status);
       }

    /* After joining, print out the results and cleanup */
    printf ("Sum =  %f \n", dotstr.sum);
    free (a);
    free (b);
    pthread_mutex_destroy(&mutexsum);
    pthread_exit(NULL);
 }   

ConditionVariables
Routines:
pthread_cond_wait (condition,mutex)
pthread_cond_signal (condition)

pthread_cond_broadcast (condition)
Usage:

  • pthread_cond_wait() blocks the calling thread until the specified condition is signalled. This routine should be called while mutex is locked, and it will automatically release the mutex while it waits. After signal is received and thread is awakened, mutex will be automatically locked for use by the thread. The programmer is then responsible for unlocking mutex when the thread is finished with it.

    Recommendation: Using a WHILE loop instead of an IF statement (see watch_count routine in example below) to check the waited for condition can help deal with several potential problems, such as:

    1. If several threads are waiting for the same wake up signal, they will take turns acquiring the mutex, and any one of them can then modify the condition they all waited for.
    2. If the thread received the signal in error due to a program bug
    3. The Pthreads library is permitted to issue spurious wake ups to a waiting thread without violating the standard.
  • The pthread_cond_signal() routine is used to signal (or wake up) another thread which is waiting on the condition variable. It should be called after mutex is locked, and must unlock mutex in order for pthread_cond_wait() routine to complete.
  • The pthread_cond_broadcast() routine should be used instead of pthread_cond_signal() if more than one thread is in a blocking wait state.
  • It is a logical error to call pthread_cond_signal() before calling pthread_cond_wait().
  • Proper locking and unlocking of the associated mutex variable is essential when using these routines. For example:
    1. Failing to lock the mutex before calling pthread_cond_wait() may cause it NOT to block.
    2. Failing to unlock the mutex after calling pthread_cond_signal() may not allow a matching pthread_cond_wait() routine to complete (it will remain blocked).

Example:

#include 
 #include 
 #include 

 #define NUM_THREADS  3
 #define TCOUNT 10
 #define COUNT_LIMIT 12

 int     count = 0;
 int     thread_ids[3] = {0,1,2};
 pthread_mutex_t count_mutex;
 pthread_cond_t count_threshold_cv;

 void *inc_count(void *t) 
 {
   int i;
   long my_id = (long)t;

   for (i=0; i < TCOUNT; i++) {
     pthread_mutex_lock(&count_mutex);
     count++;

     /* 
     Check the value of count and signal waiting thread when condition is
     reached.  Note that this occurs while mutex is locked. 
     */
     if (count == COUNT_LIMIT) {
       pthread_cond_signal(&count_threshold_cv);
       printf("inc_count(): thread %ld, count = %d  Threshold reached.\n", 
              my_id, count);
       }
     printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", 
	    my_id, count);
     pthread_mutex_unlock(&count_mutex);

     /* Do some "work" so threads can alternate on mutex lock */
     sleep(1);
     }
   pthread_exit(NULL);
 }

 void *watch_count(void *t) 
 {
   long my_id = (long)t;

   printf("Starting watch_count(): thread %ld\n", my_id);

   /*
   Lock mutex and wait for signal.  Note that the pthread_cond_wait 
   routine will automatically and atomically unlock mutex while it waits. 
   Also, note that if COUNT_LIMIT is reached before this routine is run by
   the waiting thread, the loop will be skipped to prevent pthread_cond_wait
   from never returning. 
   */
   pthread_mutex_lock(&count_mutex);
   while (count < COUNT_LIMIT) {
     pthread_cond_wait(&count_threshold_cv, &count_mutex);
     printf("watch_count(): thread %ld Condition signal received.\n", my_id);
     }
     count += 125;
     printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
   pthread_mutex_unlock(&count_mutex);
   pthread_exit(NULL);
 }

 int main (int argc, char *argv[])
 {
   int i, rc;
   long t1=1, t2=2, t3=3;
   pthread_t threads[3];
   pthread_attr_t attr;

   /* Initialize mutex and condition variable objects */
   pthread_mutex_init(&count_mutex, NULL);
   pthread_cond_init (&count_threshold_cv, NULL);

   /* For portability, explicitly create threads in a joinable state */
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   pthread_create(&threads[0], &attr, watch_count, (void *)t1);
   pthread_create(&threads[1], &attr, inc_count, (void *)t2);
   pthread_create(&threads[2], &attr, inc_count, (void *)t3);

   /* Wait for all threads to complete */
   for (i=0; i < NUM_THREADS; i++) {
     pthread_join(threads[i], NULL);
   }
   printf ("Main(): Waited on %d  threads. Done.\n", NUM_THREADS);

   /* Clean up and exit */
   pthread_attr_destroy(&attr);
   pthread_mutex_destroy(&count_mutex);
   pthread_cond_destroy(&count_threshold_cv);
   pthread_exit(NULL);

 } 

From : /computing.llnl.gov/tutorials/pthreads/#Overview

(773) 781-3097

一、Redis基础部分:

1、redis介绍与安装比mysql快10倍以上

*****************redis适用场合****************

1.取最新N个数据的操作

2.排行榜应用,取TOP N 操作

3.需要精确设定过期时间的应用

4.计数器应用

5.Uniq操作,获取某段时间所有数据排重值

6.实时系统,反垃圾系统7.Pub/Sub构建实时消息系统

7.Pub/Sub构建实时消息系统8.构建队列系统

9.缓存

=============================================

SET操作每秒钟 110000 次,GET操作每秒钟 81000 次,服务器配置如下:

Linux 2.6, Xeon X3320 2.5Ghz.

stackoverflow 网站使用 Redis 做为缓存服务器。

同时也会将数据写到硬盘上。所以数据是安全的(除突然断电外,重启服务会写到dump.rdb文件中)

 

1.安装:

tar zxvf redis-2.6.9.tar.gz

cd redis-2.6.9

make

cd src && make install

2.移动配置文件位置(为了便于管理)

cd /usr/local/

mkdir -p /usr/local/redis/bin

mkdir -p /usr/local/redis/etc

mv /lamp/redis-2.6.9/redis.conf /usr/local/redis/etc

cd /lamp/redis-2.6.9/src

mv mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server /usr/local/redis/bin

3.修改配置文件

vi /usr/local/redis/etc/redis.conf

 

一、将daemonize no 中no改为yes[yes指后台运行]

4.启动/随机启动:

cd /usr/local/redis/bin

./redis-server /usr/local/redis/etc/redis.conf#启动redis并指定配置文件。

#vi /etc/rc.local #设置随机启动。

/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

5.查看是否启动成功

ps -ef | grep redis

netstat -tunpl | grep 6379#查看端口是否占用。

6.进入客户端/退出

cd /usr/local/redis/bin

./redis-cli#进入

quit#退出

7.关闭redis

pkill redis-server#关闭

./redis-cli shutdown#关闭

************************************Redis安全************************************

 

Redis的安全性???(由以下4种方式)

1.用ACL控制器安全性。

2.在redis.conf配置文件增加下面这一行配置,即可把redis绑定在单个接口上(但并不是只有接受这个网卡的数据)。

bind 127.0.0.1

3.给redis加上较长密码(无需要记住)

4.在redis.conf配置启用认证功能。

5.SSL代理

6.禁用指定命令。

************************************** Redis配置 **********************************************

daemonize    如果需要在后台运行,把该项改为yes

pidfile      配置多个pid的地址 默认在/var/run/redis.pid

bind 绑定ip,设置后只接受来自该ip的请求

port 监听端口,默认为6379

timeout      设置客户端连接时的超时时间,单位为秒

loglevel     分为4级,debug、verbose、notice、warning

logfile      配置log文件地址

databases    设置数据库的个数,默认使用的数据库为0

save         设置redis进行数据库镜像的频率

rdbcompression    在进行镜像备份时,是否进行压缩

Dbfilename        镜像备份文件的文件名

Dir   数据库镜像备份的文件放置路径

Slaveof     设置数据库为其他数据库的从数据库

Masterauth 主数据库连接需要的密码验证

Requirepass     设置登录时需要使用的密码

Maxclients 限制同时连接的客户数量

Maxmemory 设置redis能够使用的最大内存

Appendonly 开启append only模式

以下了解即可:

Appendfsync 设置对appendonly.aof文件同步的频率

vm-enabled 是否开启虚拟内存支持

vm-swap-file 设置虚拟内存的交换文件路径

vm-max-memory 设置redis使用的最大物理内存大小

vm-page-size 设置虚拟内存的页大小

vm-pages 设置交换文件的总的page数量

vm-max-threads 设置VM IO同时使用的线程数量

Glueoutputbuf 把小的输出缓存存放在一起

hash-max-zipmap-entries 设置hash的临界值

Activerehashing 重新hash

*******************************************************************

5种数据类型:字符串、哈希、链表、集合、有序集合。

支持:push/pop、add/remove 、取交集、并集、差集、排序。

redis<===同步====>mysql

同时也会将数据写到硬盘上。所以数据是安全的(除突然断电外,重启服务会写到dump.rdb文件中)

*******************************************************************

select num#选择库,默认在0库,共16个库

auth liweijie#授权用户所需密码(密码就是redis.conf中配置的密码)

flushdb#清空数据库。

String(字符串)类型:

set name lijie#设置键name的值为lijie

get name#获取name的值。

keys *#查询所有的键。

setnx name liweijie#如果键已存在则返回0,不更新,防止覆盖。

setex haircolor 10 red #设置键的值的有效期为10秒。

setrange email 6 lampbre.com#替换键的值从第6个字符开始换为lampbre.com

mset name1 李大伟 name2 李小伟#设置多个键的值。

msetnxname1 张三 name3 李四#判断键是否存在,不存在则设置,否则不设置返回0

mget name1 name2 name3#一次获取多个键的值。

getset name1 Tom#重新设置键的值,并返回旧的键值。

getrange email 6 18#截取email键的值,从第6-18位间的字符。

incr uid#每次自增1 (如果key中uid不存在,则设置并从0开始,下同)

incrby uid 5#每次自增5

incrby uid -5#每次自减5

decr uid #每次自减1

decrby uid 5#每次自减5

appendname1 @126.com#给name1的值,添加字符串@126.com

strlenname1#返回键name1的值的长度。

*************************************************************************

Hashes(哈希)类型:

hset user:001 name liweijie#哈希设置用户user:001的name键值为liweijie

hset user:001 age 21#同样,增加一个age键值为21

hsetnx user:001 age 22#同上,但检测键是否存在。若不存在创建。

hmset user:002 name liweijie2 age 26 sex 1#同时设置多个键的值。

hget user:001 name#哈希获取用户user:001的name键的值。

hget user:001 age #同上。

hmget user:001 name age sex#获取多个指定的键的值。

hgetall user:001#获取所有键的值。

hincrbyuser:001 age -8#在指定键上加上给定的值。

hexists user:001 sex#检测指定的键值是否存在。

hlen user:001#返回指定哈希的键个数/字段个数。

hdel user:001 sex#删除指定(user:001)哈希的指定字段或是键值。

hkeys user:003#返回哈希里所有字段或是键值。

*********************************************************************

Lists(链表)类型及操作(棧或队列):

lpush mylist “world”#从头部插入字符串

lpush mylist “hello”#同上

lrange mylist 0 -1#获取从0到最后一个如[1) “hello” 2) “world”]

rpush mylist “jiejie”#在尾部插入

linsert mylist before “hello” “this is linsert” #指定插入位置(在hello之前插入)。

lset mylist 0 “what”#设置修改指定下标的值。

lrem mylist 1 “hello”#删除(1个)一个值为hello的元素。(n<0从尾部删除,n=0全部删除)

ltrim mylist 1 2 #保留表中下标为1/2的元素。

lpop mylist#弹出开头元素并返回。

rpop mylist#弹出尾部元素并返回。

rpoplpush mylist mylist2 #从mylist尾部弹出插入到mylist2的头部。

lindex mylist 0#获取表下标为0的元素值。

llen mylist#返回表元素个数(相当于count($arr  ))。

*********************************************************************

sets(集合)类型及操作(好友推荐、blog、tag功能):

smembers myset#查看myset集合中所有元素值。

sadd myset “hello”#向mysets集合中添加一个值hello

srem myset “hello”#删除myset集合中名称为hello的元素。

spop myset #随机弹出并返回mysets中的一个元素。

sdiff myset2 myset3#返回myset2中的与myset3的差集(以myset2为准)。

sdiffstore myset4 myset2 myset3#返回myset2中的与myset3的差集,并存入myset4中去。

sinter myset2 myset3#返回myset2与myset3的交集。

sinterstore myset5 myset2 myset3#返回myset2与myset3的交集,并存入myset5中去。

sunion myset2 myset3#求并集(去重复)

sunionstore myset6 myset2 myset3#求并集,并存入myset6中去。

smove myset2 myset3 “three”#将myset2中的three移到myset3中去。

scard myset2#返回元素个数。

sismember myset2 “one”#判断元素one是不是myset2集合的(相当于is_array())。

srandmember myset2#随机返回myset2集合中的一个元素,但不删除(相当于array_rand())。

*********************************************************************

sorted sets(有序集合)类型及操作(以scores排序):

zadd myzset 1 “one”#向顺序1的添加元素one

zadd myzset 2 “two”#同上。

zadd myzset 3 “two”#相当于更新顺序为2的值

zrange myzset 0 -1 withscores#查看所有元素并带上排序(默认升序)。

zrem myzset “two”#删除two

zincrby myzset 2 “two”#将two的顺序值加上2

zrank myzset “two”#返回集合中元素的索引下标值。

zrevrank myzset two#元素反转并返回新下标值。

zrevrange myzset 0 -1 withscores#按顺序反转(相当于降序排序)

zrangebyscore myzset 1 10 withscores#返回顺序为1-10的元素(可做分页)。

zcount myzset 1 10 #返回顺序在1-10之间元素的个数。

zcard myzset#返回集合中所有元素的个数。

zremrangebyrank myzset 1 2#删除集合中下标为1到2的元素。

zremrangebyscore myzset 1 10#删除集合中顺序为1到10的元素。

Redis常用命令

键/值相关命令。

keys * #查询所有

keys user*#查询指定的

exists user:001#判断是否存在。

del name#删除指定的键。

expire addr 10#设置过期时间

ttl addr#查询过期时间

select 0 #选择数据库

move age 1#将age移到1数据库。

get age #获取

persist age#移除age的过期时间。

randomkey#随机返回一个key

rename name1 name2#重命名键

type myset#返回键的类型。

ping #测试redis连接是否存活。

echo lamp#输出一个lamp

select 10#选择数据库。

quit/exit/crtl+C#退出客户端

dbsize#返回库里的键的个数。

服务器相关命令:

info#显示redis服务器的相关信息。

config get */loglevel #返回所有/指定的配置信息。

flushdb#删除当前库中的所有键/表。

flushall#删除所有数据库中的所有键/表

 

二、Redis高级部分:

1、Redis安全性:

1.用ACL控制器安全性。

2.给redis加上较长密码

# requirepass foobared

requirepass beijing

3.在redis.conf配置启用认证功能。

方式一:Auth beijing

方式二:./redis-cli -a beijing

4.在redis.conf配置文件增加下面这一行配置,即可把redis绑定在单个接口上(但并不是只有接受这个网卡的数据)。

bind 127.0.0.1(单台机器的时候可以配置,分布式或主从复制时最好不要配置)

5.SSL代理

6.禁用指定命令。

2、Redis主从复制:

redis只需在从服务器(slave)上配置即可:

slaveof 211.122.11.11 6379 #指定master 的ip 和端口

masterauth beijing#这是master主机的密码

Info#查看主/从服务器的状态。

3、Redis事务处理:

Redis事务很不完善。

4、Redis持久化机制:

1.两种方式:一、备份数据到磁盘(快照)[ snapshotting(快照)也是默认方式]

二、记录操作命令[ Append-only file(缩写aof)的方式]

一、备份数据到磁盘(快照)[ snapshotting(快照)也是默认方式]

save 900 1 #900秒内如果超过1个key被修改,则发起快照保存

save 300 10 #300秒内容如超过10个key被修改,则发起快照保存

save 60 10000

二、记录操作命令[ Append-only file(缩写aof)的方式](较安全持久化)

appendonly yes #启用aof 持久化方式

# appendfsync always /收到写命令就立即写入磁盘,最慢,但是保证完全的持久化

appendfsync everysec /每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中

# appendfsync no /完全依赖os,性能最好,持久化没保证

MACOS Yosemite 10.10.5 install tenforflow 1.3.0

我是使用virtualenv安装的tensorflow。安装过程如:/www.tensorflow.org/install/install_mac中所述(需要科学上网)。

1.首先开启terminal。
2.然后输入:sudo easy_install pip
                 sudo pip install –upgrade virtualenv
3.然后再用户目录下创建一个环境。我是先生成了一个文件夹
mkdir ~/tensorflow
virtualenv –system-site-packages ~/tensorflow
4.启动创建的python环境
source ~/tensorflow/bin/activate
如果成功了,终端就会变成
(tensorflow)$
5.安装tensorflow
(tensorflow)$pip install –upgrade tensorflow
理论上,这样就可以了。
我直接在python中 import tensorflow的时候遇到ImportError: No module named protobuf
后来查了一下发现是protobuf版本的问题。
如果你也有这样的问题,可以试试:
(tensorflow)$pip install –upgrade protobuf==3.0.0b2
这样import tensorflow的可以过了

7744799820

1.stat的基本使用

头文件
int stat(const char * path, struct stat* buf)
struct stat {
    dev_t       st_dev;
    ino_t       st_ino;
    mode_t      st_mode;
    nlink_t     st_nlink;
    uid_t       st_uid;
    gid_t       st_gid;
    dev_t       st_rdev;
    off_t       st_size;
    blksize_t   st_blksize;
    blkcnt_t    st_blocks;
    time_t      st_atime;
    time_t      st_mtime;
    time_t      st_ctime;
};
st_ino:节点号
st_mode:文件类型和文件访问权限被编码在该字段中
st_nlink:硬连接数
st_uid:属主的用户ID
st_gid:所属组的组ID
st_rdev:设备文件的主、次设备号编码在该字段中
st_size:文件的大小
st_mtime:文件最后被修改时间 返回值:成功返回0;失败返回-1
st_mode中文件类型宏定义:
宏定义 文件类型
S_ISREG() 普通文件
S_ISDIR() 目录文件
S_ISCHR() 字符设备文件
S_ISBLK() 块设备文件
S_ISFIFO() 有名管道文件
S_ISLNK() 软连接(符号链接)文件
S_ISSOCK() 套接字文件
文件类型与许可设定被一起编码在st_mode字段中,同上面一样,我们也需要一组由系统提供的宏来完成解码。
宏定义 文件类型
S_ISUID 执行时,设置用户ID
S_ISGID 执行时,设置组ID
S_ISVTX 保存正文
S_IRWXU 拥有者的读、写和执行权限
S_IRUSR 拥有者的读权限
S_IWUSR 拥有者的写权限
S_IXUSR 拥有者的执行权限
S_IRWXG 用户组的读、写和执行权限
S_IRGRP 用户组的读权限
S_IWGRP 用户组的写权限
S_IXGRP 用户组的执行权限
S_IRWXO 其它读、写、执行权限
S_IROTH 其它读权限
S_IWOTH 其它写权限
S_IXOTH 其它执行权限
2.目录操作
头文件,
DIR* opendir(const char * name)
返回值: 成功返回目录流;失败返回NULL
3.读取目录
struct dirent * readdir(DIR * dir)
readdir()返回参数dir目录流的下一个子目录(子目录或子文件)
返回值: 成功返回结构体指向的指针,错误或以读完目录, 返回NULL
struct dirent {
 ino_t d_ino;
 off_t d_off;
 unsigned short d_reclen;
 unsigned char d_type;
 char d_name[256];
};
4.关闭目录
函数原型: int closedir(DIR * dir)
返回值:成功返回0; 失败返回-1, 错误原因在errno中

2626812298

report from : /blog.csdn.net/chen_jianjian/article/details/51136433

nginx、php,mysql我都是使用ubuntu的apt-get进行安装的,我主要参考了一下配置方面的设置。wordpress我是参考官方的安装文档进行的安装。配置过程虽然不太麻烦,但是还是有一些琐碎的。可以把这一部分docker化,为以后多服务器的配置减少了工作。

引言

工作中需要搭建一个web服务器用于验证谷歌最新的压缩算法Brotli,上网找到nginx有支持brotli算法的模块,于是动手搭建一个nginx + php 7.0 + mysql 5.6的WordPress环境,也可以通过这环境玩玩WordPress。
[root@test-server-2 conf]#yum install gcc automake autoconf libtool gcc-c++ cmake
环境及准备工作
环境为:linux centos 7.0,x86_64架构,具体为Linux test-server-2 3.10.0-327.10.1.el7.x86_64 #1 SMP Tue Feb 16 17:03:50 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
安装编译工具, 若源码安装mysql,需要安装cmake工具
[root@test-server-2 conf]#yum install gcc automake autoconf libtool gcc-c++ cmake

由于源码编译安装nginx、php需要依赖很多库,这边库就不直接源码安装,使用yum install安装,需要安装的库有

[root@test-server-2 conf]#yum install gd zlib zlib-devel openssl openssl-devel libxml2 libxml2-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libmcrypt libmcrypt-devel

下载相关源码,这边mysl不是源码安装,使用rpm+yum install安装

  1. wget /nginx.org/download/nginx-1.9.14.tar.gz
  2. wget /hk2.php.net/distributions/php-7.0.5.tar.gz
  3. wget /cn.wordpress.org/wordpress-4.4.2-zh_CN.tar.gz
  4. wget /repo.mysql.com/mysql-community-release-el7-5.noarch.rpm

安装

nginx安装

这边安装的nginx有支持谷歌最新的压缩算法brotli,故需要下载brotli源码及相关依赖源码,具体如下所示:
  1. git clone /github.com/google/ngx_brotli.git
  2. git clone /github.com/bagder/libbrotli
  3. cd libbrotli
  4. ./autogen.sh
  5.  ./configure –prefix=/usr
  6.  make && make install
  7.  ldconfig

一切准备就绪了,可以开始编译安装nginx了:

  1. ./configure –prefix=/usr/local/nginx-1.9 –user=www –group=www –with-threads  –add-module=/root/LNMP/ngx_brotli
  2. make && make install

安装过程中若出现找不到对应库的,就使用yum install进行安装。

安装php

由于php需要支持nginx和mysql,需要加上nginx和mysql的相关选项:
  1. ./configure –prefix=/usr/local/php7.0 –enable-fpm –with-fpm-user=www –with-fpm-group=www –enable-phpdbg –enable-phpdbg-webhelper –enable-mysqlnd –with-pcre-dir –enable-sockets –enable-sysvmsg –enable-sysvsem –enable-sysvshm –with-pdo-mysql=mysqlnd –enable-pcntl –enable-mysqlnd –enable-zip –with-mysqli=mysqlnd
  2. make && make install

安装mysql

mysql这边不使用源码安装,直接通过rpm+yum安装:
  1. rpm -ivh mysql-community-release-el7-5.noarch.rpm
  2. yum install mysql-community-server
  3. 若存在旧版本的mysql,需要完成删除mysql的话,使用如下命令:
  4. rpm -qa | grep mysql
  5. mysql57-community-release-el7-7.noarch
  6. mysql-community-common-5.7.11-1.el7.x86_64
  7. mysql-community-client-5.7.11-1.el7.x86_64
  8. mysql-community-libs-compat-5.7.11-1.el7.x86_64
  9. mysql-community-libs-5.7.11-1.el7.x86_64
  10. mysql-community-server-5.7.11-1.el7.x86_64
  11. yum remove mysql-community-release mysql-community-common mysql-community-client mysql-community-libs-compat mysql-community-libs mysql-community-server
  12. chkconfig –list | grep -i mysql
  13. chkconfig –del mysql
  14. whereis mysql

配置

nginx配置

根据nginx编译安装的配置,找到配置文件,我这边的环境主要是修改了nginx.conf文件,该文件内容如下所示:
  1. worker_processes  auto;
  2. error_log  logs/error.log;
  3. error_log  logs/error.log  notice;
  4. #error_log  logs/error.log  info;
  5. #pid        logs/nginx.pid;
  6. events {
  7.     worker_connections  1024;
  8. }
  9. http {
  10.     include       mime.types;
  11.     default_type  application/octet-stream;
  12.     log_format  main  ‘$remote_addr – $remote_user [$time_local] “$request” ‘
  13.                       ‘$status $body_bytes_sent “$http_referer” ‘
  14.                       ‘”$http_user_agent” “$http_x_forwarded_for”‘;
  15.     access_log  logs/access.log  main;
  16.     sendfile        on;
  17.     #tcp_nopush     on;
  18.     #keepalive_timeout  0;
  19.     keepalive_timeout  65;
  20.     brotli on;
  21.     brotli_static off;
  22.     brotli_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/javascript text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
  23.     brotli_buffers 4 16k;
  24.     brotli_comp_level 9;
  25.     brotli_window 512k;
  26.     brotli_min_length 512;
  27.     gzip  on;
  28.     server {
  29.         listen       80;
  30.         server_name  192.168.24.101;
  31.         #charset koi8-r;
  32.         gzip  on;
  33.         gzip_types  text/plain text/css application/xml application/javascript;
  34.         gzip_comp_level 6;
  35.         access_log  logs/host.access.log  main;
  36.         root  /home/wwwroot/wordpress;
  37.         index index.html index.htm index.php;
  38.         location / {
  39.             try_files $uri $uri/ /index.php?$args;
  40.         }
  41.         # Add trailing slash to */wp-admin requests.
  42.         rewrite /wp-admin$ $scheme:/$host$uri/ permanent;
  43.         #error_page  404              /404.html;
  44.         # redirect server error pages to the static page /50x.html
  45.         #
  46.         error_page   500 502 503 504  /50x.html;
  47.         location = /50x.html {
  48.             root   html;
  49.         }
  50.         # proxy the PHP scripts to Apache listening on 127.0.0.1:80
  51.         #
  52.         #location ~ \.php$ {
  53.         #    proxy_pass   /127.0.0.1;
  54.         #}
  55.         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  56.         #
  57.         location ~ \.php$ {
  58.             fastcgi_pass   127.0.0.1:9000;
  59.             fastcgi_index  index.php;
  60.             #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
  61.             include        fastcgi.conf;
  62.             include        fastcgi_params;
  63.             fastcgi_param  HTTPS              off;
  64.         }
  65.         # deny access to .htaccess files, if Apache’s document root
  66.         # concurs with nginx’s one
  67.         #
  68.         location ~ /\.ht {
  69.             deny  all;
  70.         }
  71.     }
  72.     # HTTPS server
  73.     #
  74.     server {
  75.         listen       443 ssl;
  76.         server_name  192.168.24.101;
  77.         ssl_certificate      server.crt;
  78.         ssl_certificate_key  server.key;
  79.         ssl_session_cache    shared:SSL:1m;
  80.         ssl_session_timeout  5m;
  81.         add_header           Front-End-Https on;
  82.         access_log  logs/ssl.access.log  main;
  83.         root  /home/wwwroot/wordpress;
  84.         index index.html index.htm index.php;
  85.         include wordpress.conf;
  86.     #    ssl_ciphers  HIGH:!aNULL:!MD5;
  87.     #    ssl_prefer_server_ciphers  on;
  88.          location ~ \.php$ {
  89.             fastcgi_pass   127.0.0.1:9000;
  90.             fastcgi_index  index.php;
  91.             #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
  92.             include        fastcgi.conf;
  93.             include        fastcgi_params;
  94.         }
  95.     #    location / {
  96.     #        root   html;
  97.     #        index  index.html index.htm;
  98.     #    }
  99.     }
  100. }

其中该配置文件支持https,对于https支持谷歌压缩算法brotli,配置文件里面有server.crt和server.key,这参考(639) 931-5214成SSL证书。

配置php

  1. cp /root/LNMP/php-7.0.5/php.ini-development /usr/local/php7.0/lib/php.ini
  2. cp /usr/local/php7.0/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
  3. cp /usr/local/php7.0/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf

这边未对php进行优化,需要根据具体的环境进行优化,至于怎么优化这边就不详述了。

配置mysql

本环境安装的mysql的配置文件为/etc/my.cnf,修改该配置文件,另外网上也有根据硬件环境生成优化的mysql配置文件,可以在该网站上(/tools.percona.com/dashboard)生成需要的mysql配置文件,我环境的配置文件为:
  1. # Generated by Percona Configuration Wizard (/tools.percona.com/) version REL5-20120208
  2. # Configuration name server generated for chen_jianjian@qq.com at 2016-03-21 08:31:54
  3. [mysql]
  4. # CLIENT #
  5. port                           = 3306
  6. socket                         = /var/lib/mysql/mysql.sock
  7. [mysqld]
  8. # GENERAL #
  9. user                           = mysql
  10. default-storage-engine         = InnoDB
  11. socket                         = /var/lib/mysql/mysql.sock
  12. pid-file                       = /var/lib/mysql/mysql.pid
  13. # MyISAM #
  14. key-buffer-size                = 32M
  15. myisam-recover                 = FORCE,BACKUP
  16. # SAFETY #
  17. max-allowed-packet             = 16M
  18. max-connect-errors             = 10000
  19. # DATA STORAGE #
  20. datadir                        = /var/lib/mysql/
  21. # BINARY LOGGING #
  22. log-bin                        = /var/lib/mysql/mysql-bin
  23. expire-logs-days               = 14
  24. sync-binlog                    = 1
  25. # CACHES AND LIMITS #
  26. tmp-table-size                 = 32M
  27. max-heap-table-size            = 32M
  28. query-cache-type               = 0
  29. query-cache-size               = 0
  30. max-connections                = 500
  31. thread-cache-size              = 50
  32. open-files-limit               = 65535
  33. table-definition-cache         = 1024
  34. table-open-cache               = 2048
  35. # INNODB #
  36. innodb-flush-method            = O_DIRECT
  37. innodb-log-files-in-group      = 2
  38. innodb-log-file-size           = 128M
  39. innodb-flush-log-at-trx-commit = 1
  40. innodb-file-per-table          = 1
  41. innodb-buffer-pool-size        = 2G
  42. # LOGGING #
  43. log-error                      = /var/lib/mysql/mysql-error.log
  44. log-queries-not-using-indexes  = 1
  45. slow-query-log                 = 1
  46. slow-query-log-file            = /var/lib/mysql/mysql-slow.log

ps:对于mysql 5.7为了安全,初次安装使用的时候会生成一个随机密码,可以通过如下方式修改mysql的密码:

service mysqld start
mysql为了安全设置了个随机密码,要手动查看,使用grep “password” /var/log/mysqld.log
修改密码mysql shell下:SET PASSWORD = PASSWORD(‘123456’);其中可能会出现密码不合法,只要修改配置文件,把validate-password=OFF即可修改

配置WordPress

把WordPress解压到nginx的配置目录,然后cp wp-config-sample.php wp-config.php,修改wp-config.php,下面是这个环境wp-config.php的配置内容:
  1. <?php
  2. /**
  3.  * WordPress基础配置文件。
  4.  *
  5.  * 这个文件被安装程序用于自动生成wp-config.php配置文件,
  6.  * 您可以不使用网站,您需要手动复制这个文件,
  7.  * 并重命名为“wp-config.php”,然后填入相关信息。
  8.  *
  9.  * 本文件包含以下配置选项:
  10.  *
  11.  * * MySQL设置
  12.  * * 密钥
  13.  * * 数据库表名前缀
  14.  * * ABSPATH
  15.  *
  16.  * @link /codex.wordpress.org/zh-cn:%E7%BC%96%E8%BE%91_wp-config.php
  17.  *
  18.  * @package WordPress
  19.  */
  20. $site_url = (isset($_SERVER[‘SERVER_PORT’]) && (‘443’ == $_SERVER[‘SERVER_PORT’]) ? “/” : “/”) . “192.168.24.101”; /这既支持http,也支持https,有域名,这IP改为域名
  21. define(‘WP_SITEURL’, $site_url);
  22. define(‘WP_HOME’, $site_url);
  23. define(“WP_CONTENT_URL”, $site_url . “/wp-content”);
  24. /*  强制使用SSL进行登录和后台管理  */
  25. define(‘FORCE_SSL_LOGIN’, false);
  26. define(‘FORCE_SSL_ADMIN’, true);
  27. / ** MySQL 设置 – 具体信息来自您正在使用的主机 ** /
  28. /** WordPress数据库的名称 */
  29. define(‘DB_NAME’, ‘wordpress’);
  30. /** MySQL数据库用户名 */
  31. define(‘DB_USER’, ‘root’);
  32. /** MySQL数据库密码 */
  33. define(‘DB_PASSWORD’, ‘123456’);
  34. /** MySQL主机 */
  35. define(‘DB_HOST’, ‘127.0.0.1’);
  36. /** 创建数据表时默认的文字编码 */
  37. define(‘DB_CHARSET’, ‘utf8’);
  38. /** 数据库整理类型。如不确定请勿更改 */
  39. define(‘DB_COLLATE’, );
  40. /**#@+
  41.  * 身份认证密钥与盐。
  42.  *
  43.  * 修改为任意独一无二的字串!
  44.  * 或者直接访问{@link /api.wordpress.org/secret-key/1.1/salt/
  45.  * WordPress.org密钥生成服务}
  46.  * 任何修改都会导致所有cookies失效,所有用户将必须重新登录。
  47.  *
  48.  * @since 2.6.0
  49.  */
  50. define(‘AUTH_KEY’,         ‘put your unique phrase here’);
  51. define(‘SECURE_AUTH_KEY’,  ‘put your unique phrase here’);
  52. define(‘LOGGED_IN_KEY’,    ‘put your unique phrase here’);
  53. define(‘NONCE_KEY’,        ‘put your unique phrase here’);
  54. define(‘AUTH_SALT’,        ‘put your unique phrase here’);
  55. define(‘SECURE_AUTH_SALT’, ‘put your unique phrase here’);
  56. define(‘LOGGED_IN_SALT’,   ‘put your unique phrase here’);
  57. define(‘NONCE_SALT’,       ‘put your unique phrase here’);
  58. /**#@-*/
  59. /**
  60.  * WordPress数据表前缀。
  61.  *
  62.  * 如果您有在同一数据库内安装多个WordPress的需求,请为每个WordPress设置
  63.  * 不同的数据表前缀。前缀名只能为数字、字母加下划线。
  64.  */
  65. $table_prefix  = ‘wp_’;
  66. /**
  67.  * 开发者专用:WordPress调试模式。
  68.  *
  69.  * 将这个值改为true,WordPress将显示所有用于开发的提示。
  70.  * 强烈建议插件开发者在开发环境中启用WP_DEBUG。
  71.  *
  72.  * 要获取其他能用于调试的信息,请访问Codex。
  73.  *
  74.  * @link /codex.wordpress.org/Debugging_in_WordPress
  75.  */
  76. define(‘WP_DEBUG’, false);
  77. /**
  78.  * zh_CN本地化设置:启用ICP备案号显示
  79.  *
  80.  * 可在设置→常规中修改。
  81.  * 如需禁用,请移除或注释掉本行。
  82.  */
  83. define(‘WP_ZH_CN_ICP_NUM’, true);
  84. /* 好了!请不要再继续编辑。请保存本文件。使用愉快! */
  85. /** WordPress目录的绝对路径。 */
  86. if ( !defined(‘ABSPATH’) )
  87.         define(‘ABSPATH’, dirname(__FILE__) . ‘/’);
  88. /** 设置WordPress变量和包含文件。 */
  89. require_once(ABSPATH . ‘wp-settings.php’);

启动运行

配置基本上已经完成了,下面就可以启动相关进程了,另外mysql还要创建一个wordpress数据库,具体怎么创建该数据库就不详述了。还有nginx和php没有搞开机启动脚本,需要开机启动,网上找一下开机启动脚本,这边nginx和php都要已www用户组运行,故需要创建www用户和用户组,创建www用户和用户组使用该命令:
  1. groupadd www
  2. useradd -g www www

启动mysql、nginx、php:

  1. service mysqld restart
  2. /usr/local/nginx-1.9/sbin/nginx
  3. /usr/local/php7.0/sbin/php-fpm

都启动好了,就可以在浏览器上安装WordPress了,具体为:/xx.xx.xx.xx/wp-admin/install.php进行安装配置。

大功告成!