软件课程设计报告

 

 

<!-- /* Font Definitions */ @font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0; mso-font-charset:2; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:0 268435456 0 0 -2147483648 0;} @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1107304683 0 0 159 0;} @font-face {font-family:Cambria; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1073741899 0 0 159 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpFirst {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpMiddle {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpLast {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h2 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 2 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:2; font-size:13.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h3 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 3 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:112%; mso-pagination:widow-orphan; mso-outline-level:3; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h4 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 4 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:4; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} h5 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 5 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:5; font-size:11.0pt; font-family:"Cambria","serif"; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h6 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 6 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:112%; mso-pagination:widow-orphan; mso-outline-level:6; font-size:11.0pt; font-family:"Cambria","serif"; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoHeading7, li.MsoHeading7, div.MsoHeading7 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 7 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:7; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoHeading8, li.MsoHeading8, div.MsoHeading8 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 8 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:8; font-size:10.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoHeading9, li.MsoHeading9, div.MsoHeading9 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 9 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:9; font-size:10.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoToc1, li.MsoToc1, div.MsoToc1 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoToc2, li.MsoToc2, div.MsoToc2 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:11.0pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoToc3, li.MsoToc3, div.MsoToc3 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:22.0pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoHeader, li.MsoHeader, div.MsoHeader {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"页眉 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; text-align:center; line-height:115%; mso-pagination:widow-orphan; tab-stops:center 207.65pt right 415.3pt; layout-grid-mode:char; border:none; mso-border-bottom-alt:solid windowtext .75pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoFooter, li.MsoFooter, div.MsoFooter {mso-style-priority:99; mso-style-link:"页脚 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; tab-stops:center 207.65pt right 415.3pt; layout-grid-mode:char; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitle, li.MsoTitle, div.MsoTitle {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpFirst, li.MsoTitleCxSpFirst, div.MsoTitleCxSpFirst {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpMiddle, li.MsoTitleCxSpMiddle, div.MsoTitleCxSpMiddle {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpLast, li.MsoTitleCxSpLast, div.MsoTitleCxSpLast {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoSubtitle, li.MsoSubtitle, div.MsoSubtitle {mso-style-priority:11; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"副标题 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:30.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.65pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoDate, li.MsoDate, div.MsoDate {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"日期 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:5.0pt; mso-para-margin-top:0in; mso-para-margin-right:0in; mso-para-margin-bottom:10.0pt; mso-para-margin-left:25.0gd; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} a:link, span.MsoHyperlink {mso-style-priority:99; color:#0066FF; text-decoration:underline; text-underline:single;} a:visited, span.MsoHyperlinkFollowed {mso-style-noshow:yes; mso-style-priority:99; color:purple; mso-themecolor:followedhyperlink; text-decoration:underline; text-underline:single;} em {mso-style-priority:20; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; letter-spacing:.5pt; border:none; font-weight:bold;} pre {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"HTML 预设格式 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:12.0pt; font-family:宋体; mso-bidi-font-family:宋体; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoAcetate, li.MsoAcetate, div.MsoAcetate {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"批注框文本 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoNoSpacing, li.MsoNoSpacing, div.MsoNoSpacing {mso-style-priority:1; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"无间隔 Char"; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:.5in; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpFirst, li.MsoListParagraphCxSpFirst, div.MsoListParagraphCxSpFirst {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:0in; margin-left:.5in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpMiddle, li.MsoListParagraphCxSpMiddle, div.MsoListParagraphCxSpMiddle {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:0in; margin-left:.5in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpLast, li.MsoListParagraphCxSpLast, div.MsoListParagraphCxSpLast {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:.5in; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoQuote, li.MsoQuote, div.MsoQuote {mso-style-priority:29; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"引用 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:.25in; margin-bottom:0in; margin-left:.25in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoIntenseQuote, li.MsoIntenseQuote, div.MsoIntenseQuote {mso-style-priority:30; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"明显引用 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:.8in; margin-bottom:14.0pt; margin-left:.7in; text-align:justify; text-justify:inter-ideograph; line-height:115%; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.MsoSubtleEmphasis {mso-style-priority:19; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-style:italic;} span.MsoIntenseEmphasis {mso-style-priority:21; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-weight:bold;} span.MsoSubtleReference {mso-style-priority:31; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps;} span.MsoIntenseReference {mso-style-priority:32; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps; letter-spacing:.25pt; text-decoration:underline; text-underline:single;} span.MsoBookTitle {mso-style-priority:33; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps; letter-spacing:.25pt; font-style:italic;} p.MsoTocHeading, li.MsoTocHeading, div.MsoTocHeading {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpFirst, li.MsoTocHeadingCxSpFirst, div.MsoTocHeadingCxSpFirst {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpMiddle, li.MsoTocHeadingCxSpMiddle, div.MsoTocHeadingCxSpMiddle {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpLast, li.MsoTocHeadingCxSpLast, div.MsoTocHeadingCxSpLast {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.1Char {mso-style-name:"标题 1 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 1"; mso-ansi-font-size:14.0pt; mso-bidi-font-size:14.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.2Char {mso-style-name:"标题 2 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 2"; mso-ansi-font-size:13.0pt; mso-bidi-font-size:13.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.3Char {mso-style-name:"标题 3 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 3"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.4Char {mso-style-name:"标题 4 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 4"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.5Char {mso-style-name:"标题 5 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 5"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.6Char {mso-style-name:"标题 6 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 6"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.7Char {mso-style-name:"标题 7 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 7"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.8Char {mso-style-name:"标题 8 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 8"; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.9Char {mso-style-name:"标题 9 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 9"; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char {mso-style-name:"无间隔 Char"; mso-style-priority:1; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:无间隔; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char0 {mso-style-name:"批注框文本 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:批注框文本; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char1 {mso-style-name:"日期 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:日期; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char2 {mso-style-name:"页眉 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:页眉; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char3 {mso-style-name:"页脚 Char"; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:页脚; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.HTMLChar {mso-style-name:"HTML 预设格式 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"HTML 预设格式"; mso-ansi-font-size:12.0pt; mso-bidi-font-size:12.0pt; font-family:宋体; mso-ascii-font-family:宋体; mso-hansi-font-family:宋体; mso-bidi-font-family:宋体; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char4 {mso-style-name:"标题 Char"; mso-style-priority:10; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:标题; mso-ansi-font-size:26.0pt; mso-bidi-font-size:26.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char5 {mso-style-name:"副标题 Char"; mso-style-priority:11; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:副标题; mso-ansi-font-size:12.0pt; mso-bidi-font-size:12.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.65pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char6 {mso-style-name:"引用 Char"; mso-style-priority:29; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:引用; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char7 {mso-style-name:"明显引用 Char"; mso-style-priority:30; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:明显引用; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; mso-ascii-font-family:Calibri; mso-fareast-font-family:宋体; mso-hansi-font-family:Calibri;} @page Section1 {size:595.3pt 841.9pt; margin:1.0in .75in 1.0in .75in; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {mso-list-id:19477224; mso-list-type:hybrid; mso-list-template-ids:-2016751348 957919660 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l0:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l1 {mso-list-id:35618804; mso-list-type:hybrid; mso-list-template-ids:-292357040 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l1:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l2 {mso-list-id:37945843; mso-list-type:hybrid; mso-list-template-ids:-557152656 -1167306530 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l2:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:39.0pt; text-indent:-.25in;} @list l3 {mso-list-id:173346659; mso-list-type:hybrid; mso-list-template-ids:-188204378 -1208171458 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l3:level1 {mso-level-text:%1.; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:39.0pt; text-indent:-.25in;} @list l4 {mso-list-id:504593466; mso-list-type:hybrid; mso-list-template-ids:-1887250706 -315474174 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l4:level1 {mso-level-tab-stop:none; mso-level-number-position:left; margin-left:60.0pt; text-indent:-.25in;} @list l5 {mso-list-id:564534336; mso-list-type:hybrid; mso-list-template-ids:-1575576948 1988901484 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l5:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l6 {mso-list-id:654336836; mso-list-type:hybrid; mso-list-template-ids:-1864493826 -586515714 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l6:level1 {mso-level-number-format:japanese-counting; mso-level-text:%1、; mso-level-tab-stop:21.0pt; mso-level-number-position:left; margin-left:21.0pt; text-indent:-21.0pt; mso-ansi-language:EN-US;} @list l6:level2 {mso-level-number-format:alpha-lower; mso-level-text:"%2/)"; mso-level-tab-stop:42.0pt; mso-level-number-position:left; margin-left:42.0pt; text-indent:-21.0pt;} @list l6:level3 {mso-level-number-format:roman-lower; mso-level-tab-stop:63.0pt; mso-level-number-position:right; margin-left:63.0pt; text-indent:-21.0pt;} @list l7 {mso-list-id:687023684; mso-list-type:hybrid; mso-list-template-ids:733275990 2022061108 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l7:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l8 {mso-list-id:1233127868; mso-list-type:hybrid; mso-list-template-ids:527230024 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l8:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l9 {mso-list-id:1412118487; mso-list-type:hybrid; mso-list-template-ids:64003442 -1791034928 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l9:level1 {mso-level-text:%1、; mso-level-tab-stop:11.35pt; mso-level-number-position:left; margin-left:11.35pt; text-indent:-11.35pt; mso-ascii-font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-hansi-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman";} @list l10 {mso-list-id:1485200162; mso-list-type:hybrid; mso-list-template-ids:-686660436 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l10:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l11 {mso-list-id:1756776902; mso-list-type:hybrid; mso-list-template-ids:-732680122 -1352092336 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l11:level1 {mso-level-text:"/[%1/]"; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:42.0pt; text-indent:-21.0pt;} @list l12 {mso-list-id:1763601758; mso-list-type:hybrid; mso-list-template-ids:-914301096 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l12:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l13 {mso-list-id:1910336498; mso-list-type:hybrid; mso-list-template-ids:-1182349410 -1791034928 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l13:level1 {mso-level-text:%1、; mso-level-tab-stop:11.35pt; mso-level-number-position:left; margin-left:11.35pt; text-indent:-11.35pt; mso-ascii-font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-hansi-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman";} @list l14 {mso-list-id:2031452156; mso-list-type:hybrid; mso-list-template-ids:1846201960 -136021808 1394390288 -686804554 1760874092 -1771302544 1952606742 1350839770 1571857246 -398039114;} @list l14:level1 {mso-level-number-format:bullet; mso-level-text:; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; font-family:Wingdings;} @list l15 {mso-list-id:2035185183; mso-list-type:hybrid; mso-list-template-ids:-1612952014 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l15:level1 {mso-level-tab-stop:none; mso-level-number-position:left; margin-left:101.95pt; text-indent:-21.0pt;} ol {margin-bottom:0in;} ul {margin-bottom:0in;} -->

任务1 命令解释器的实现

实践目标:

学会利用Windows API创建进程

实践内容:

batch文件中所列的应用程序启动运行(批处理)

设计思路:

只要从文件中读出进程名,调用CreateProcess创建进程就可以了。

主要代码结构

  1. 从程序参数中得到存放进程名的文件名
  2. 打开文件
  3. 读出每个进程的名称,用CreateProcess调用
  4. CloseHandle关句柄,减少计数

主要代码分析:

 

//1.打开文件,这样做其实并不是很好,详见体会

#ifdef UNICODE//make it work whether UNICODE defined.

            wifstream inFile(argv[1]);

#else

            ifstream inFile(argv[1]);

#endif

            int i=0;

//2.得到进程名

            while (inFile>>PTSTRCmdLine){    

                        ++i;

//3.创建进程

                        if(!CreateProcess(NULL,       PTSTRCmdLine,NULL,        NULL,            FALSE, CREATE_NEW_CONSOLE|NORMAL_PRIORITY_CLASS,   NULL,  NULL,           &StartInfo&ProcessInfo) ) {

                                                fprintf(stderr,"Create Process failed (%d)/n",GetLastError());

                        }else{

                                    fprintf(stdout,"The Child Process: %s's PID: %d./n", PTSTRCmdLine,ProcessInfo.dwProcessId);

                        }

//.创建完成后及时关闭句柄(并不结束程序)

                        CloseHandle(ProcessInfo.hProcess);//close handle to descrease the count, we call it explicitly for good reasons.

                        CloseHandle(ProcessInfo.hThread);

            };

            fprintf(stdout,"The Parent Process have to terminate./n");

            return (0);

}

实践结果:

基本数据:

源程序代码行数:52

完成实践投入的时间(小时数):0.5

资料查阅时间:0.1

编程调试时间:0.4

测试数据设计:

输入:

calc

notepad

mspaint

it_seems_there_is_no_such_process

 

输出:

命令行如下,同时启动了计算器等三个程序。

测试结果分析:

3正常启动;最后一个无此进程,创建失败。

实践体会:

实践过程中遇到的问题及解决过程

关于字符编码:batch文件如果说明是宽字节或单字节(内容、文件名)就容易了,我的处理方法并不是十分正确的(如要考虑所有情况将十分复杂,这里也只是表达一下想法而已)。

实践体会和收获

应该尽量写通用的代码,注重国际化,提供使用Unicode.

参考文献

[1] Jeffrey Richter,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000

 

 


任务2:用软件方法解决兄弟问题

实践目标:

学会使用软件方法解决临界区问题

实践内容:

Peterson算法或Dekker 算法解决兄弟问题

实践主要内容:

设计思路:

兄弟进程都要从一个账号取钱存到另一账号,这段的实现代码是临界区。

Peterson算法或Dekker 算法都可以软件的方法实现临界区的保护。

正常情况下两账号金额之和为定值。

主要数据结构:

//线程参数

typedef struct BrotherInfo {

            int          iSerial;                       //用户定义的线程号

            DWORD dwDelay;                //线程睡眠的时间

} BROTHERINFO,*PBROTHERINFO;

 

//目前哪个线程被允许进入临界区

int turn = 0;

//线程提出进入临界区请求的标志位

BOOL flag[MAX_THREADS] ;

主要代码结构

  1. 主线程启动多个(两个)兄弟线程,并等待他们全部结束
  2. 兄弟线程中的关键部分是从一账号取款,存入另一账号,用Dekker 算法保护

主要代码分析:

volatile  int iAccnt1 = 0; /*  Ensure variable in the memory */

volatile  int iAccnt2 = 0;

volatile BOOL flag[2];

volatile int turn;

 

/*Banker函数负责启动多个Brother线程

*并等待它们结束

*/

void Banker( LPCTSTR file){

            int iThreadCounter = 0;    // Thread Counter             

            DWORD   dwThreadIdArray[MAX_THREADS];                          // Thread ID Array

            HANDLE  hThreadArray[MAX_THREADS];                                             // Thread Object HANDLE Array

            PBROTHERINFO pThreadInfoArray[MAX_THREADS];     // Custom data structure for threads to use.           

            flag[0] = flag[1] = false;

            turn = 0;

           

            ifstream  inFile;

            inFile.open(file);                      //open file

            fprintf(stdout, "Now, We begin to Read thread Information and create every WorkerThread /n/n" );

           

            while ( inFile ){

                        ……

                        //得到线程的相关信息后,一一创建之

                        // Create the thread to begin execution on its own.

                        hThreadArray[iThreadCounter] = CreateThread(

                                    NULL,                                                                                    // default security attributes

                                    0,                                                                                             // use default stack size 

                                    (LPTHREAD_START_ROUTINE)(Brother),  // thread function name

                                    pThreadInfoArray[iThreadCounter],  // argument to thread function

                                    0,                                                                                 // use default creation flags

                                    &dwThreadIdArray[iThreadCounter]);          // returns the thread identifier

……//容错处理

            }         

            WaitForMultipleObjects(iThreadCounter, hThreadArray, TRUE, INFINITE);

            ……//清理工作

}

 

/*Brother函数是重点,主要处理临界区

*/

DWORD WINAPI Brother(LPVOID lpParam){

            int iLoan, iAccnt,iCounter = 0;

……   

            do {

                        ……

                        int i = pThreadInfo->iSerial-1;

                        int j = 1-i;

 

//Dekker 算法,只允许一个线程进入下面的临界区

                        flag [i] = TRUE;

                        while (flag[j]){

                                    if (turn == j){

                                                flag[i] = FALSE;

                                                while(turn==j);

                                                flag[i] = TRUE;

                                    }

                        }

                       

                        //临界区部分开始

                        iAccnt1 = iAccnt1 - iLoan;

                        Sleep(pThreadInfo-> dwDelay*INTE_PER_SEC);

                        iAccnt2 = iAccnt2 + iLoan;

                        iAccnt = iAccnt1 + iAccnt2;

                        //临界区部分结束

                       

                        turn = j; //此线程已退出临界区

                        flag[i] = FALSE;

                        iCounter++;                

            } while ( (iAccnt == 0) && (iCounter<10));

            ……

} //End of Brother

基本数据:

源程序代码行数:186

完成实践投入的时间(小时数):1

资料查阅时间:0.5

编程调试时间:0.5

测试数据设计

输入:

1 3

2 1

输出:

每次结果可能不一样,下面是一次的结果

但多次测试,iAccnt1 + iAccnt2 均为0

测试结果分析:

多次测试,每次Account1Account2之和均为0,表示两线程没有同时进入临界区,Dekker算法正确地解决了临界区互斥的问题。

实践体会:

实践体会和收获

复习了操作系统概念中与同步有关的内容,有了进一步理解。

参考文献:

[1] Jeffrey Richter,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000

[2] Abraham Silberschatz ,Peter Baer Galvin  and Greg Gagne. 郑扣根等译.操作系统概念 第六版,翻译版. 北京:高等教育出版社. 2004

 


任务3:使用Win32所提供的同步对象解决兄弟问题

实践目标:

学会使用Win32提供的同步对象解决临界区问题

实践内容:

使用Win32所提供的同步对象——互斥体对象——解决兄弟问题

设计思路:

  1. Windows提供了多种同步对象,如Mutex, Event, Semaphore等,还有用户态下的Critical Section
  2. Critical Section使用方便,速度快,对此问题很适用。
  3. 在任务二的基础上,将软件方法实现的临界区保护改为Critical Section即可。

主要数据结构:

//线程参数

typedef struct BrotherInfo {

            int          iSerial;                      

            DWORD dwDelay;               

} BROTHERINFO,*PBROTHERINFO;

 

CRITICAL_SECTION g_cs;//全局

void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection);

void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection);

主要代码分析:

Banke中初始化临界区对象:InitializeCriticalSection(&cs);

Banke中删除临界区对象:        DeleteCriticalSection(&cs);

/*Brother函数仍然是关键

*/

DWORD WINAPI Brother(LPVOID lpParam){

            int iLoan, iAccnt,iCounter = 0;

            ……   

            do {

                                    iLoan = rand();                       

                        EnterCriticalSection(&cs);//进入临近区

                        iAccnt1 = iAccnt1 - iLoan;

                        iAccnt2 = iAccnt2 + iLoan;

                        iAccnt = iAccnt1 + iAccnt2;

                        LeaveCriticalSection(&cs);//离开临近区

                        iCounter++;                

            } while ( (iAccnt == 0) && (iCounter<10));

            ……

} //End of Brother

基本数据:

源程序代码行数:172

完成实践投入的时间(小时数):1

资料查阅时间: 0.5

编程调试时间:0.5

测试数据设计:

输入:

1 1

2 3

输出:

每次结果可能不一样,下面是一次的结果

但多次测试,iAccnt1 + iAccnt2 均为0

测试结果分析:

多次测试,每次Account1Account2之和均为0说明Critical Section可以解决临界区问题。

实践体会:

实践体会和收获

Windows提供的同步对象有了更清晰的认识,体会到其方便。

参考文献:

[1] Jeffrey Richter,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000


任务4:用Win32所提供的同步对象解决有限缓冲区问题

实践目标:

学会用Win32所提供的同步对象解决有限缓冲区问题

实践内容:

写一个多线程实现C/C++语言程序:一些线程负责找出某个数据范围的素数,并放到一个数组中,另一些线程负责将数组中的素数按次序取出,显示出来。

要求定义一个全局变量的数组:int prime[9] 用于存放找到的待显示的素数,要理解成环形缓冲区

设计思路:

分析:

  1. 生产者之间互斥;
  2. 消费者间互斥;
  3. 生产者、消费者间同步。
  4. 生产者全部退出要通知消费者;反之亦然。

解决:

1. 1Critical Section可解决

2. 2,同上

3. 3,一对同步信号量:hFull(初始0),hEmpty(初始缓冲区大小)

4. 4,两个手动的Event,可通知所以等待线程

5. 另外,WaitForMultiObjects可以做使用

主要数据结构:

typedef struct{//定义缓冲区

            INT q[QLEN];

            int front,rear;

}ProdQueue,*PProdQueue;

// 清空缓冲区

inline VOID ClearQueue(PProdQueue pQ){

            pQ->front = pQ->rear = 0;

}

//是否缓冲区满

inline BOOL IsFullProductQueue(PProdQueue pQ){

            return (pQ->rear+1)%QLEN == pQ->front;

}

//是否缓冲区空

inline BOOL IsEmptyProuctQueue(PProdQueue pQ){

            return pQ->front == pQ->rear;

}

//放一个素数

inline VOID PutProduct(PProdQueue pQ,INT num){

            pQ->q[pQ->rear] = num;

            pQ->rear = (pQ->rear+1)%QLEN;

}

//取一个素数

inline INT TakeProduct(PProdQueue pQ){

            INT ret = pQ->q[pQ->front];

            pQ->front = (pQ->front+1)%QLEN;

            return ret;

}

ProdQueue prodQ;//定义缓冲区

typedef struct { //线程参数

            int iThreadId;  //用户定义的线程ID

            char cTag;        //标识该线程是放素数线程,还是取素数线程

            int iA;//若为放素数线程,表示搜索的起始数字;若为取素数线程,表示取素数的个数

            int iB;//若为放素数线程,表示素数个数;若为取素数线程,该数无意义

} INFO, *PINFO;

INT nMaxConsumer//消费者线程的数目

INT nMaxProducer; //生产者线程的数目

HANDLE hEmpty, hFull;//一对同步信号量的句柄,分析中对3的解决

HANDLE hEventAllConsumerDeadhEventAllProducerDead;//分析中对4的解决,两个事件的句柄

主要代码结构

对生产者:

/*控制当所有消费者退出时,生产者也应退出(装满缓冲区或已产生足够多的素数)

*/

DWORD WINAPI ControlProducer(LPVOID lpParam){

            ……

            WaitForMultipleObjects(nMaxConsumer,hConsumer,TRUE,INFINITE);//等所有消费者死

SetEvent(hEventAllConsumerDead);//使所有消费者死的消息事件有信号

……

}

 

DWORD WINAPI SingleProducer(LPVOID lpParam){

            ……   

            while(1){

        ……//产生素数                     

        WaitForMultipleObjects(“有空信号量有信号||“所有消费者死事件有信号);

If(“所有消费者死事件有信号){

……//填充一个素数后继续循环,为的是给其它线程一个机会,注意保护临界区

}else{

……//放素数,释放一个满的信号量,注意保护临界区

}

}

}

至于消费者,类似。

主要代码分析:

//函数ReaderPriority(总控)主要部分

//部分初始化

     while ( inFile>>thread_info[n_thread].serial) {

                 inFile>>thread_info[n_thread].entity;                       

                 inFile>>thread_info[n_thread].beg;

                 inFile>>thread_info[n_thread].len;

//创建后,线程处于阻塞状态

                 if (thread_info[n_thread].entity == 'D'){

                             hConsumer[nMaxConsumer++] =                                          CreateThread(NULL,0,SingleConsumer,&thread_info[n_thread],CREATE_SUSPENDED,&thread_ID);

                 }

                 else{

                             hProducer[nMaxProducer++] =                                              CreateThread(NULL,0,SingleProducer,&thread_info[n_thread],CREATE_SUSPENDED,&thread_ID);

                 }

                 ++n_thread;

                 inFile.get();

     }

//一对同步信号量,控制生产者、消费者同步

hEmpty = CreateSemaphore(NULL,QLEN-1,QLEN-1,"empty");

            hFull = CreateSemaphore(NULL,0,QLEN-1,"full");

            //两个控制线程,其中等待所有生产者、消费者,并使相关事件有信号

            hCtrlConsumer = CreateThread(NULL,0,ControlConsumer,NULL,0,&thread_ID);

            hCtrlProducer = CreateThread(NULL,0,ControlProducer,NULL,0,&thread_ID);

            //创建这两个全死的事件

            hEventAllConsumerDead = CreateEvent(NULL,TRUE,FALSE,"all_consumer_dead");

            hEventAllProducerDead = CreateEvent(NULL,TRUE,FALSE,"all_producer_dead");

            //全生产者、消费者可调度

            for (i=0; i<nMaxConsumer;++i){

                        ResumeThread(hConsumer[i]);

            }

            for (i=0; i<nMaxProducer;++i){

                        ResumeThread(hProducer[i]);

            }

            WaitForSingleObject(hCtrlConsumer,INFINITE);

            WaitForSingleObject(hCtrlProducer,INFINITE);    

……//一系列清理工作

 

DWORD WINAPI ControlProducer(LPVOID lpParam){

     HANDLE hEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");

     HANDLE hEventAllConsumerDead = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_consumer_dead");

     if (nMaxConsumer > 0)

                 WaitForMultipleObjects(nMaxConsumer,hConsumer,TRUE,INFINITE);//等所有消费者退出

     printf("all consumer threads end/n");

 

     SetEvent(hEventAllConsumerDead); //当所有消费者退出时,使相关事件有信号

     return 1;

}

 

DWORD WINAPI SingleProducer(LPVOID lpParam){

     HANDLE hEmptyOrConsumerDead[2];//[0]有空信号量的句柄,[]所有消费者退出事件有效

     hEmptyOrConsumerDead[0] = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");

     hEmptyOrConsumerDead[1] = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_consumer_dead");

     HANDLE hFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");

     INT nPrimeProduced = 0;

     INT iPrimeFound;

     BOOL qFull=FALSE;

     PThreadInfo pThreadInfo = (PThreadInfo)lpParam;

    

     while(1){

//下面产生一个素数,如果总数已经够规定数目就退出

                 if (nPrimeProduced == pThreadInfo->len)

                             break;

                 while(isPrime[pThreadInfo->beg]==FALSE)

                             ++pThreadInfo->beg;

                 iPrimeFound = pThreadInfo->beg;

                 ++pThreadInfo->beg;

                 ++nPrimeProduced;   

                

                 DWORD idx = WaitForMultipleObjects(2,hEmptyOrConsumerDead,FALSE,INFINITE)-WAIT_OBJECT_0;//无效等待,直到信号量或事件有信号,并得到具体是哪个

                 if (idx == 1){//说明所有消费都退出,同步信号量也就没什么意义了,放一素数到缓冲区后,继续循环,等下一次调度

                             EnterCriticalSection(&csProducer);

                             if (IsFullProductQueue(&prodQ))

                                         qFull = TRUE;

                             else{

                                         PutProduct(&prodQ,iPrimeFound);

                                         printf("Thread %2d produced %5d ,and puts into queue/n",pThreadInfo->serial,iPrimeFound);

                             }

                             LeaveCriticalSection(&csProducer);

                             if (qFull){

                                         break;

                             }

                             else{

                                         continue;

                             }

                 }else{//正常的生产者、消费者解决方式

                             EnterCriticalSection(&csProducer);

                             PutProduct(&prodQ,iPrimeFound);

                            printf("Thread %2d produced %5d ,and puts into queue/n",pThreadInfo->serial,iPrimeFound);

                             LeaveCriticalSection(&csProducer);

                             ReleaseSemaphore(hFull,1,NULL);

                 }

     }

     printf("Thread %d exits/n",pThreadInfo->serial);

     return EXIT_SUCCESS;

}

 

对消费者类似的,不再详细注释

DWORD WINAPI ControlConsumer(LPVOID lpParam){

            HANDLE hFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,_T("full"));

            HANDLE hEventAllProducerDead = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_producer_dead");

            if (nMaxProducer > 0)

                        WaitForMultipleObjects(nMaxProducer,hProducer,TRUE,INFINITE);

            printf("all producer threads end/n");  

            if(SetEvent(hEventAllProducerDead)==0){

                        printf("set event failed/n");

                        printf("error code :%d/n",GetLastError());

            }

            return 1;

}

DWORD WINAPI SingleConsumer(LPVOID lpParam){

            PThreadInfo pThreadInfo = (PThreadInfo)lpParam;

            HANDLE hFullOrProducerDead[2];

            HANDLE hEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");

            hFullOrProducerDead[0] = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");

            hFullOrProducerDead[1] = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_producer_dead");

            BOOL isEmpty = FALSE;

            int iConsumed = 0;

           

            while(1){

                        if(iConsumed == pThreadInfo->beg)

                                    break;             

                        ++iConsumed;

                        DWORD idx = WaitForMultipleObjects(2,hFullOrProducerDead,FALSE,INFINITE)-WAIT_OBJECT_0;

                       

                        if (idx == 1){

                                    EnterCriticalSection(&csConsumer);

                                    if (IsEmptyProuctQueue(&prodQ))

                                                isEmpty = TRUE;

                                    else

                                                printf("Thread %2d take %5d from queue/n",pThreadInfo->serial,TakeProduct(&prodQ));

                                    LeaveCriticalSection(&csConsumer);

                                    if(isEmpty){

                                                break;

                                    }

                                    else{

                                                continue;

                                    }

                        }

                        EnterCriticalSection(&csConsumer);

                        printf("Thread %2d take %5d from queue/n",pThreadInfo->serial,TakeProduct(&prodQ));

                        LeaveCriticalSection(&csConsumer);

                        ReleaseSemaphore(hEmpty,1,NULL);

            }

            printf("Thread %d exits/n",pThreadInfo->serial);

            return EXIT_SUCCESS;

}

基本数据:

源程序代码行数:274

完成实践投入的时间(小时数):2.5

设计时间: 1

资料查阅时间:0.5

编程调试时间:1

测试数据设计:

测试数据1://只有生产者

输入:

1 W 10 10

2 W 1 10

输出:

测试数据2://只有消费者

输入:

1 D 3 0

2 D 3 0

输出:

测试数据3://生产者产生素数个数少于消费者需求数(供不应求)

输入:

1 W 10 2

2 W 1 3

3 D 4 0

4 D 3 0

输出:

测试数据4://供大于求,且最终放满缓冲区

输入:

1 W 10 10

2 W 1 10

3 D 3 0

4 D 3 0

输出:

测试数据5://供大于求,且最终放不满缓冲区

输入:

1 W 10 5

2 W 1 5

4 W 3 2

5 D 2 0

6 D 3 0

输出:

测试数据6://供求恰相等

输入:

1 W 10 2

2 W 5 3

3 W 4 2

4 D 5 0

5 D 2 0

输出:

测试结果分析:

生产者之间、消费者之间自由竞争缓冲区,生产者与消费者做到了同步(没有缓冲区无产品仍有消费者取走产品或产品多于缓冲区大小的情况)。

当只有生产者时,尽量放满缓冲区后,能够正常结束;只有消费者时,取走所需产品或取空缓冲区后,能够正常结束。

结果与设计相符,与要求相符,与手动分析相符。

实践体会:

考虑真实的情况,正确的逻辑(设计的基础)是关键,编码只是思路的表达。

Windows提供的常用同步对象的综合应用更加熟练。

参考文献:

[1] Jeffrey Richter,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000


任务5:用Win32同步对象实现PV操作,解决兄弟问题

实践目标:

学会用Win32所提供的同步对象实现你自己的PV操作,并使用它们并解决兄弟问题

实践内容:

利用Win32所提供的时间对象实现自己的PV 操作,并解决兄弟问题

设计思路:

从二值的P、V到一般的P、V操作可由Binary Semaphores算法实现; 而二值的PV操作可由事件对象(Event)实现。

主要数据结构及主要代码分析:

这也是本任务的关键,我将信号量用类实现.

对于如何实现P、V,Binary Semaphores算法已解决这个问题。

根据此,有如下实现

class MSemaphore{

            LONG C;

            HANDLE m_hS1,m_hS2;

public:

            MSemaphore(LONG nSem=1,PTSTR pSemId=NULL){

                        C = nSem;

                        m_hS1 = CreateEvent(NULL,FALSE,TRUE,pSemId);

                        m_hS2 = CreateEvent(NULL,FALSE,FALSE,pSemId);

            }

            ~MSemaphore(){

                        CloseHandle(m_hS1);

                        CloseHandle(m_hS2);

            }         

            void P(){

                        WaitForSingleObject(m_hS1,INFINITE);

                        InterlockedDecrement(&C);

                        if (C < 0) {

                                    SetEvent(m_hS1);

                                    WaitForSingleObject(m_hS2,INFINITE);

                        }

                        SetEvent(m_hS1);

            }

           

            void V(){

                        WaitForSingleObject(m_hS1,INFINITE);

                        InterlockedIncrement(&C);

                        if (C <= 0)

                                    SetEvent(m_hS2);

                        else

                                    SetEvent(m_hS1);

            }

};

用来解决兄弟问题的关键部分:

MSemaphore mSem(1,_T("com.gene"));//定义一个实现的信号量的对象

            mSem.P();//临界区前的P操作

                        iAccnt1 = iAccnt1 - iLoan;    

                        iAccnt2 = iAccnt2 + iLoan;

                        iAccnt = iAccnt1 + iAccnt2;

            mSem.V();//临界区后的V操作

基本数据:

源程序代码行数:199

完成实践投入的时间(小时数):1

资料查阅时间:0.5

编程调试时间:0.5

测试数据设计:

输入:

1 1

2 3

输出:

测试结果分析:

兄弟线程之间自由竞争iAccnt1iAccnt2资源,能够正确的处理临界区问题。                                                                                       

实践体会:

事实上,如果像模仿Critical Section的实现可以做的更好。

参考文献:

[1] Abraham Silberschatz ,Peter Baer Galvin  and Greg Gagne. 郑扣根等译.操作系统概念 第六版,翻译版. 北京:高等教育出版社. 2004

 


任务6:采用动态优先数,编写一进程调度程序模拟程序

实践目标:

理解采用动态优先数的进程调度算法,并复习链表的操作。

实践内容:

采用动态优先数,编写一进程调度程序模拟程序。

设计思路:

需要几个地方模拟:

1.  调度任务产生的模拟,应注意时间上的先后

2.  调度队列的实现

3.  任务完成后,为方便信息输出,可暂存于别处。

还有一个要注意的地方是,动态优先级的问题,应该设置一个范围,比如Linuxnice值为:-20 ~ +19,本例中不妨设最低0(最高是在产生任务时确定,具体实现可以通过取模)。

主要数据结构:

class CPCB  {

public:

            int ID;//标识,系统分配

            int createTime;//创建时间

            int runTime;//要求时间

            int priority;//动态优先数

            int status;//状态――就绪、结束

            int waittime;//等待时间

            int turnovertime;//周转时间

            int remaintime;//剩余运行时间

            CPCB *next;//下一PCB指针 

            CPCB();

            virtual ~CPCB();

};

            CPCB *mhead,//所有的任务按时间排于此

                        *task_over_list,//为保存结果,调度结束后存放于此

                        *head;//所有在排队的任务按优先级排于此队

主要代码分析:

CPCB *mhead,//模拟作业调度的结果

                        *task_over_list;//为保存结果,调度结束后存放于此

            CPCB *p,*next,*pre;

            mhead = NULL;//初始时,没有任务

            for(int i=0;i<m_processNum;i++)       {//模拟现实,产生一些任务,最终按时间排列

                        CPCB *p=InitOnePro();//随机产生一个任务,不对时间作假设

                        p->status=0;

//下面将产生的任务,插入有序链表

                        if(mhead==NULL){

                                    mhead=p;

                                    mhead->next=NULL;

                        }else{

                                    if(mhead->createTime>p->createTime){

                                                p->next = mhead;

                                                mhead = p;

                                    }else{

                                                pre = mhead;

                                                next = pre->next;

                                                while(next){

                                                            if(p->createTime<=next->createTime)

                                                                        break;

                                                            pre = next;

                                                            next = next->next;

                                                }

                                                pre->next = p;

                                                p->next = next;

                                    }

                        }

            }

            ……

            //调度,关键部分

            int cpuCurrTime=mhead->createTime;//假设前面已经没有需要调度的,it's a new start.

            head = NULL;//CPU队列为空

            task_over_list = NULL;//结束任务也为空

            while (1){

                        if (mhead == NULL && head == NULL)break;//说明所有任务都处理完,且无新任务,此时CPU实际在运行Idle进程,此处简单假设

                        while (mhead && mhead->createTime == cpuCurrTime){//下面这一段,将这一时刻到达的新任务按优先数挂在就绪队列上

                                    p = mhead;

                                    mhead = mhead->next;

 

                                    if (head == NULL){

                                                head = p;

                                                head->next = NULL;

                                    }else{

                                                if (p->priority >= head->priority){

                                                            p->next = head;

                                                            head = p;

                                                }else{//==,则任务同时到达、同优先数,不作任何假设

                                                            pre = head;

                                                            next = pre->next;

                                                            while (next){

                                                                        if (p->priority>next->priority)

                                                                                    break;

                                                                        pre = next;

                                                                        next = next->next;

                                                            }

                                                            pre->next = p;

                                                            p->next = next;

                                                }

                                    }

                        }                     

                        if (head == NULL){//就绪队列为空,此时CPU空闲(Idle)         

                                    ++cpuCurrTime;

                                    continue;

                        }

                        head->remaintime -=1;

            //下面这句表现CPU工作过程(实际是划一条线在图形界面上)            DrawLine(500+cpuCurrTime*14,60+(head->ID-1)*30,500+(cpuCurrTime+1)*14,60+(head->ID-1)*30);

                        ++cpuCurrTime;//假设一个时间片长度

                        if(head->remaintime==0){//此任务处理完毕,将移至完成队列(为方便表示)

                                    head->status=1;//设标志

                                    head->waittime=(cpuCurrTime-head->createTime)-head->runTime;

                                    head->turnovertime=cpuCurrTime-head->createTime;

 

                                    CPCB *tmp = head;

                                    head = head->next;

                                    //delete tmp;

                                    tmp->next = task_over_list;

                                    task_over_list = tmp;

                        }else{{//进程运行一次后,若要求运行时间>0,则再将它加入队列(以新的优先级)

                                    p = head;//先删去此任务

                                    head = head->next;

                                    p->priority -= 1;

                                    if (p->priority < 0)?

                                                p->priority = 0;

 

                                    //insert into queue again

                                    if (head == NULL){

                                                head = p;

                                                head->next = NULL;

                                    }else

                                                if (p->priority > head->priority){

                                                            p->next = head;

                                                            head = p;

                                                }else{//==,则任务同时到达、同优先数,不作任何假设

                                                            pre = head;

                                                            next = pre->next;

                                                            while (next){

                                                                        if (p->priority>next->priority)

                                                                                    break;

                                                                        pre = next;

                                                                        next = next->next;

                                                            }

                                                            pre->next = p;

                                                            p->next = next;

                                                }

                                    }

                        }         

            }

……//还有一些相关计算及界面上的表示

}

基本数据:

源程序代码行数:210左右

完成实践投入的时间(小时数):3

资料查阅时间:0.5

编程调试时间:2.5

测试数据设计:

随机产生,以下是一次运行时产生的数据及结果

测试结果分析:

以上面测试为例,手动的、按时间一步步分析,结果不会出现低优先数进程得到调度的情况。

实践体会:

现在多核CPU已经很常见了,如果在模拟时加上对CPU亲和性的考虑等会更有趣.

参考文献:

[1] Jeffrey Richter,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
一、课程设计题目 ① 基本要求题目:矩阵乘法。 ② 综合训练:学生成绩管理系统 二、设计要求 矩阵乘法:编写一个函数实现矩阵A(2行3列)与矩阵B 相乘(3行2列),乘积放在C数组中。在主函数中输入相乘的两数组,并输出结果。 学生成绩管理:(结构体数组、函数、指针、算法、流程结构及文件等的综合应用) 程序说明:有N个学生,每个学生的数据包含学号(不重复)、姓名、三门课的成绩及平均成绩,试设计一学生成绩管理系统,使之能提供以下功能: (1)主菜单 (2)各菜单项功能 ① 成绩录入:输入学生的学号、姓名及三门课的成绩; ② 成绩查询:(至少一种查询方式)。  按学号查询学生记录。  查询不及格学生的记录。 ③成绩统计:  计算学生的平均分;  根据学生的平均分高低,对学生的数据进行排序后输出;  对学生单科成绩排序,输出学生姓名与该科成绩; ④退出系统:退出整个系统(即主菜单)。 (3)结构体数组: #define N 30struct student {int num; /* 定义学号*/ char name[20]; /* 定义姓名*/ float score[3]; /* 定义存贮三门课成绩的数组*/ float average; /* 定义平均成绩*/ };struct student stu[N];  /* 定义结构体数组,存贮多个学生的记录*/ 三、数据结构设计描述 系统功能描述 1能够输入学生的学号、姓名、三科成绩并且计算出平均成绩。 2能够以表格的形式输出学生记录 3能够按照学生三科的平均成绩进行排序 4能够按照学生的单科成绩进行排序 5能够按照学号查询学生记录 6往表中插入学生记录 7从表中删除学生记录 8存储记录到文件中 9从文件中读取记录 10退出 数据字典 1数据流条目 数据流名称:全部记录 别名:无 简述:最新更新后所有关于学生成绩的记录 来源:数据库 去向:加工“记录筛选” 数据流量:不限 组成:学号+姓名+SC1+SC2+SC3+平均成绩 数据存储条 数据存储名称:学生成绩记录 别名:无 简述:存放学生所有可供查询的信息 组成:学号+姓名+SC1+SC2+SC3+平均成绩 组织方式:索引文件,以学学号为关键字 查询要求:要求能立即查询 2数据项条目 数据项名称:学号 别名:无 简述:所有学校学生的学号 类型:字符串 3加工条目 加工名:更改的记录 激发条件:学生成绩记录被改动 优先级:普通 输入:新记录 输出:更新数据、数据未改动 加工逻辑:根据现有学生成绩记录 if 新记录旧记录 then 更新数据 else 数据未改动 endif 设计测试流程 1、进入界面 2、输入选项0,回车; 按提示输入数据 3、回到主菜单; 输入选项8,回车; 输入文件名:data,回车; 出现成功提示,则读入文件操作成功。 4、回到主菜单,输入1,回车 每10个暂停显示数据 5、回到主菜单,输入2,回车 出现排序成功信息。 6、回到主菜单,输入3,回车 出现排序成功信息。 7、回到主菜单,输入5,回车 按提示插入一组数据 8、回到主菜单,输入6,回车 按提示输入姓名,删除数据 出现删除成功的信息 9、回到主菜单,输入4,回车 输入学号进行查询 10、回到主菜单,输入1,回车 出现统计信息 11、回到主菜单,输入7,回车 输入result,回车 出现成功写入文件的信息 12、回到主菜单,输入9,回车退出系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值