Amazon MWS商品APIの実行結果例

前回の記事ではGetLowestOfferListingsForASINのレスポンス例を紹介しました。
ここでは、他の商品APIの実行結果を紹介します。

GetCompetitivePricingForASIN


出品数とランクが分かります。

<?xml version="1.0"?>
<GetCompetitivePricingForASINResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetCompetitivePricingForASINResult ASIN="4906638015" status="Success">
  <Product xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" 
           xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
    <Identifiers>
      <MarketplaceASIN>
        <MarketplaceId>A1VC38T7YXB528</MarketplaceId>
        <ASIN>4906638015</ASIN>
      </MarketplaceASIN>
    </Identifiers>
    <CompetitivePricing>
      <CompetitivePrices>
        <CompetitivePrice belongsToRequester="false" condition="New" subcondition="New">
          <CompetitivePriceId>1</CompetitivePriceId>
          <Price>
            <LandedPrice>
              <CurrencyCode>JPY</CurrencyCode>
              <Amount>2039.00</Amount>
            </LandedPrice>
            <ListingPrice>
              <CurrencyCode>JPY</CurrencyCode>
              <Amount>2039.00</Amount>
            </ListingPrice>
            <Shipping>
              <CurrencyCode>JPY</CurrencyCode>
              <Amount>0.00</Amount>
            </Shipping>
          </Price>
        </CompetitivePrice>
      </CompetitivePrices>
      <NumberOfOfferListings>
        <OfferListingCount condition="Any">119</OfferListingCount>
        <OfferListingCount condition="Used">104</OfferListingCount>
        <OfferListingCount condition="New">1</OfferListingCount>
        <OfferListingCount condition="Collectible">14</OfferListingCount>
      </NumberOfOfferListings>
    </CompetitivePricing>
    <SalesRankings>
      <SalesRank>
        <ProductCategoryId>book_display_on_website</ProductCategoryId>
        <Rank>46</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>502840</ProductCategoryId>
        <Rank>2</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>562858</ProductCategoryId>
        <Rank>4</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>562850</ProductCategoryId>
        <Rank>4</Rank>
      </SalesRank>
    </SalesRankings>
  </Product>
</GetCompetitivePricingForASINResult>
<ResponseMetadata>
  <RequestId>xxxx</RequestId>
</ResponseMetadata>
</GetCompetitivePricingForASINResponse>





GetMatchingProduct


商品の情報が分かります

<?xml version="1.0"?>
<GetMatchingProductResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetMatchingProductResult ASIN="4906638015" status="Success">
  <Product xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" 
           xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
    <Identifiers>
      <MarketplaceASIN>
        <MarketplaceId>A1VC38T7YXB528</MarketplaceId>
        <ASIN>4906638015</ASIN>
      </MarketplaceASIN>
    </Identifiers>
    <AttributeSets>
      <ns2:ItemAttributes xml:lang="ja-JP">
        <ns2:Binding>単行本</ns2:Binding>
        <ns2:Creator Role="著">スティーブン・R. コヴィー</ns2:Creator>
        <ns2:Creator Role="原著">Stephen R. Covey</ns2:Creator>
        <ns2:Creator Role="翻訳">ジェームス スキナー</ns2:Creator>
        <ns2:Creator Role="翻訳">川西 茂</ns2:Creator>
        <ns2:IsAdultProduct>false</ns2:IsAdultProduct>
        <ns2:Label>キングベアー出版</ns2:Label>
        <ns2:ListPrice>
          <ns2:Amount>2039.00</ns2:Amount>
          <ns2:CurrencyCode>JPY</ns2:CurrencyCode>
        </ns2:ListPrice>
        <ns2:Manufacturer>キングベアー出版</ns2:Manufacturer>
        <ns2:NumberOfPages>492</ns2:NumberOfPages>
        <ns2:PackageDimensions>
          <ns2:Height Units="inches">1.02</ns2:Height>
          <ns2:Length Units="inches">7.87</ns2:Length>
          <ns2:Width Units="inches">5.75</ns2:Width>
          <ns2:Weight Units="pounds">1.41</ns2:Weight>
        </ns2:PackageDimensions>
        <ns2:PackageQuantity>1</ns2:PackageQuantity>
        <ns2:ProductGroup>Book</ns2:ProductGroup>
        <ns2:ProductTypeName>ABIS_BOOK</ns2:ProductTypeName>
        <ns2:PublicationDate>1996-12-01</ns2:PublicationDate>
        <ns2:Publisher>キングベアー出版</ns2:Publisher>
        <ns2:SmallImage>
          <ns2:URL>http://ec2.images-amazon.com/images/I/51JHD9GEK0L._SL75_.jpg</ns2:URL>
          <ns2:Height Units="pixels">75</ns2:Height>
          <ns2:Width Units="pixels">51</ns2:Width>
        </ns2:SmallImage>
        <ns2:Studio>キングベアー出版</ns2:Studio>
        <ns2:Title>7つの習慣―成功には原則があった!</ns2:Title>
      </ns2:ItemAttributes>
    </AttributeSets>
    <Relationships/>
    <SalesRankings>
      <SalesRank>
        <ProductCategoryId>book_display_on_website</ProductCategoryId>
        <Rank>46</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>502840</ProductCategoryId>
        <Rank>2</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>562858</ProductCategoryId>
        <Rank>4</Rank>
      </SalesRank>
      <SalesRank>
        <ProductCategoryId>562850</ProductCategoryId>
        <Rank>4</Rank>
      </SalesRank>
    </SalesRankings>
  </Product>
</GetMatchingProductResult>
<ResponseMetadata>
  <RequestId>xxxx</RequestId>
</ResponseMetadata>
</GetMatchingProductResponse>



GetProductCategoriesForASIN


ブラウズノードが分かります

<?xml version="1.0"?>
<GetProductCategoriesForASINResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetProductCategoriesForASINResult>
  <Self>
    <ProductCategoryId>562850</ProductCategoryId>
    <ProductCategoryName>倫理学入門</ProductCategoryName>
    <Parent>
      <ProductCategoryId>500102</ProductCategoryId>
      <ProductCategoryName>倫理学・道徳</ProductCategoryName>
      <Parent>
        <ProductCategoryId>571582</ProductCategoryId>
        <ProductCategoryName>人文・思想</ProductCategoryName>
        <Parent>
          <ProductCategoryId>465610</ProductCategoryId>
          <ProductCategoryName>ジャンル別</ProductCategoryName>
          <Parent>
            <ProductCategoryId>465392</ProductCategoryId>
            <ProductCategoryName>ジャンル別</ProductCategoryName>
          </Parent>
        </Parent>
      </Parent>
    </Parent>
  </Self>
  <Self>
    <ProductCategoryId>562858</ProductCategoryId>
    <ProductCategoryName>人生論・教訓</ProductCategoryName>
    <Parent>
      <ProductCategoryId>500102</ProductCategoryId>
      <ProductCategoryName>倫理学・道徳</ProductCategoryName>
      <Parent>
        <ProductCategoryId>571582</ProductCategoryId>
        <ProductCategoryName>人文・思想</ProductCategoryName>
        <Parent>
          <ProductCategoryId>465610</ProductCategoryId>
          <ProductCategoryName>ジャンル別</ProductCategoryName>
          <Parent>
            <ProductCategoryId>465392</ProductCategoryId>
            <ProductCategoryName>ジャンル別</ProductCategoryName>
          </Parent>
        </Parent>
      </Parent>
    </Parent>
  </Self>
  <Self>
    <ProductCategoryId>466282</ProductCategoryId>
    <ProductCategoryName>ビジネス・経済</ProductCategoryName>
    <Parent>
      <ProductCategoryId>465610</ProductCategoryId>
      <ProductCategoryName>ジャンル別</ProductCategoryName>
      <Parent>
        <ProductCategoryId>465392</ProductCategoryId>
        <ProductCategoryName>ジャンル別</ProductCategoryName>
      </Parent>
    </Parent>
  </Self>
  <Self>
    <ProductCategoryId>502840</ProductCategoryId>
    <ProductCategoryName>能力発見・自己改革</ProductCategoryName>
    <Parent>
      <ProductCategoryId>492216</ProductCategoryId>
      <ProductCategoryName>自己啓発</ProductCategoryName>
      <Parent>
        <ProductCategoryId>466292</ProductCategoryId>
        <ProductCategoryName>実用・ホビー</ProductCategoryName>
        <Parent>
          <ProductCategoryId>465610</ProductCategoryId>
          <ProductCategoryName>ジャンル別</ProductCategoryName>
          <Parent>
            <ProductCategoryId>465392</ProductCategoryId>
            <ProductCategoryName>ジャンル別</ProductCategoryName>
          </Parent>
        </Parent>
      </Parent>
    </Parent>
  </Self>
</GetProductCategoriesForASINResult>
<ResponseMetadata>
  <RequestId>xxxx</RequestId>
</ResponseMetadata>
</GetProductCategoriesForASINResponse>

Amazon MWSで商品の情報を取得する

今までAmazonでは、Amazon Product Advertising API(PA-API)を使用して商品情報が取得できていたのですが、このAPIは2012/8末で廃止になります。
代わりに用意されたAmazon MWSの商品APIを使用して商品情報を取得する方法を説明します。

Amazon MWSとは?

Amazon MWSは、Amazon Marketplace Web Serviceの略で、アマゾンのマーケットプレースに出品している人が、WebAPIベースで作業を行うためのサービスですこれを使用する事によって、Amazonのサイトにブラウザでアクセスしなくても、出品・出荷・価格改定etcの作業を行う事が可能です。

また、Amazon MWSは小口の出品者は使用することが出来ません。4,900円/月の利用料を払っている大口出品者のみが利用可能です。


商品APIとは?


Amazon MWS 商品APIは、出品者が出品している商品に対して、競合他社の出品状況を取得する事が可能です。これにより、出品予定の商品やの情報や、価格改定時の情報が取得可能になります。商品APIを使用する事によって、商品属性や現在の価格等の情報を入手できます。


商品APIには、以下のWebAPIが存在します(詳細は後述)。

ListMatchingProducts
GetMatchingProduct
GetMatchingProductForId
GetCompetitivePricingForSKU
GetLowestOfferListingsForSKU
GetLowestOfferListingsForASIN
GetMyPriceForSKU
GetMyPriceForASIN
GetServiceStatus




APIによっては、複数のASINを指定できます。複数指定可能なのは以下の5つとなります。

GetMatchingProduct
GetCompetitivePricingForSKU
GetCompetitivePricingForASIN
GetLowestOfferListingsForSKU
GetLowestOfferListingsForASIN



複数指定するにはパラメータの末尾に連番を付けて、以下のような感じでクエリを投げます。

https://mws.amazonservices.com/Products/2011-10-01?AWSAccessKeyId=...
&ASINList.ASIN.1=4900000001
&ASINList.ASIN.2=4900000002
&ASINList.ASIN.3=4900000003




API一覧

Amazon 商品APIで用意されている各APIの説明です。

ListMatchingProducts

Description
    商品検索のAPI
    条件を指定して、一致する商品と属性のリストを関連性の高い順に取得する
    最大10アイテムの情報が返る。
 
Args
    MarketplaceId
    Query
    QueryContextId
 
Rerurn
    Product
    Identifiers
    AttributeSets
    Relationships
    SalesRankings





GetMatchingProduct

Description
    ASIN指定(複数可)で、商品の情報を最高で10件取得する
 
Args
    MarketplaceId
    ASINList
 
Rerurn
    ListMatchingProductsと同じ





GetMatchingProductForId

Description
    商品ID(ASIN,SellerSKU,UPC,EAN,ISBN,JAN)指定で、商品の情報を取得する
 
Args
    MarketplaceId
    IdType
    IdList
 
Rerurn
    ListMatchingProductsと同じ






GetCompetitivePricingForSKU

Description
    SellerSKUと、MarketplaceIdを指定して、同一商品を出品している競合他社の価格情報を取得する
    MarketplaceIdは、クエリを投げた自分のIDのみが指定可能(?)
 
Args
    MarketplaceId
    SellerSKUList
 
Rerurn
    Product
    Identifiers
    CompetitivePricing
    SalesRankings






GetLowestOfferListingsForSKU

Description
    SellerSKU指定で商品のコンディションごとの最低価格情報を取得する
 
Args
    MarketplaceId
    SellerSKUList
    ItemCondition
    ExcludeMe
 
Rerurn
    AllOfferListingsConsidered
    Product
    Identifiers






GetLowestOfferListingsForASIN

Description
    ASIN指定で商品のコンディションごとの最低価格情報を取得する
 
Args
    MarketplaceId
    ASINList
    ItemCondition
 
Rerurn
    GetLowestOfferListingsForSKUと同じ






GetMyPriceForSKU

Description
    SellerSKU指定で商品を自分が何円で出品しているかを取得する
 
Args
    MarketplaceId
    SellerSKUList
    ItemCondition
 
Rerurn
    Identifiers
    BuyingPrice
    RegularPrice
    FulfillmentChannel
    ItemCondition
    ItemSubCondition
    SellerId
    SellerSKU






GetMyPriceForASIN

Description
    ASIN指定で商品を自分が何円で出品しているかを取得する
 
Args
    MarketplaceId
    ASINList
    ItemCondition
 
Rerurn
    GetMyPriceForSKUと同じ





GetServiceStatus

Description
    MWS APIのサービス提供状況を取得する
 
Args
    なし
 
Rerurn
    省略







API実行例

以下は、GetLowestOfferListingsForASINのAPI実行結果例です。

リクエストURL

mws.amazonservices.jp/Products/2011-10-01
  ?AWSAccessKeyId  =xxxx
  &Action          =GetLowestOfferListingsForASIN
  &SellerId        =xxxx
  &SignatureVersion=2
  &Timestamp       =2012-08-19T07%3A57%3A14Z
  &Version         =2011-10-01
  &Signature       =xxxx
  &SignatureMethod =HmacSHA256
  &MarketplaceId   =xxxx
  &ASINList.ASIN.1 =4906638015



レスポンス

<?xml version="1.0"?>
<GetLowestOfferListingsForASINResponse xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<GetLowestOfferListingsForASINResult ASIN="4906638015" status="Success">
  <AllOfferListingsConsidered>false</AllOfferListingsConsidered>
  <Product xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01" 
            xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd">
    <Identifiers>
      <MarketplaceASIN>
        <MarketplaceId>A1VC38T7YXB528</MarketplaceId>
        <ASIN>4906638015</ASIN>
      </MarketplaceASIN>
    </Identifiers>
    <LowestOfferListings>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Good</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>98-100%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>3</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>497</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1448.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1198.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Good</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>95-97%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>8</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>4411</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1449.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1199.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Acceptable</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>95-97%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>1</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>190</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1450.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1200.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Good</ItemSubcondition>
          <FulfillmentChannel>Amazon</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>90-94%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>1</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>163</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1550.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1550.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>0.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>VeryGood</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>Less than 70%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>1</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>1</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1550.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1300.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>VeryGood</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>95-97%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>2</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>8242</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1573.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1323.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>True</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Good</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>Unknown</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>98-100%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>2</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>1891</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1590.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1340.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>Acceptable</ItemSubcondition>
          <FulfillmentChannel>Merchant</FulfillmentChannel>
          <ShipsDomestically>Unknown</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>98-100%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>1</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>1855</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1597.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1347.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>250.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
      <LowestOfferListing>
        <Qualifiers>
          <ItemCondition>Used</ItemCondition>
          <ItemSubcondition>VeryGood</ItemSubcondition>
          <FulfillmentChannel>Amazon</FulfillmentChannel>
          <ShipsDomestically>True</ShipsDomestically>
          <ShippingTime>
            <Max>0-2 days</Max>
          </ShippingTime>
          <SellerPositiveFeedbackRating>95-97%</SellerPositiveFeedbackRating>
        </Qualifiers>
        <NumberOfOfferListingsConsidered>1</NumberOfOfferListingsConsidered>
        <SellerFeedbackCount>126</SellerFeedbackCount>
        <Price>
          <LandedPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1647.00</Amount>
          </LandedPrice>
          <ListingPrice>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>1647.00</Amount>
          </ListingPrice>
          <Shipping>
            <CurrencyCode>JPY</CurrencyCode>
            <Amount>0.00</Amount>
          </Shipping>
        </Price>
        <MultipleOffersAtLowestPrice>False</MultipleOffersAtLowestPrice>
      </LowestOfferListing>
    </LowestOfferListings>
  </Product>
</GetLowestOfferListingsForASINResult>
<ResponseMetadata>
  <RequestId>xxxx</RequestId>
</ResponseMetadata>
</GetLowestOfferListingsForASINResponse>




レスポンスが長いので見づらいですが、良く確認してみるとProduct.LowestOfferListings.LowestOfferListingノード以下に、以下のような出品情報がセットされていることが分かります。

LowestOfferListing.Qualifiers.ItemCondition         Used
LowestOfferListing.Qualifiers.ItemSubcondition      Good
LowestOfferListing.Qualifiers.FulfillmentChannel    Merchant
LowestOfferListing.Qualifiers.ShipsDomestically     True
LowestOfferListing.Qualifiers.ShippingTime.Max      0-2 days
LowestOfferListing.NumberOfOfferListingsConsidered  3
LowestOfferListing.SellerFeedbackCount              497
LowestOfferListing.Price.LandedPrice.CurrencyCode   JPY
LowestOfferListing.Price.LandedPrice.Amount         1448.00
LowestOfferListing.Price.ListingPrice.CurrencyCode  JPY
LowestOfferListing.Price.ListingPrice.Amount        1198.00
LowestOfferListing.Price.Shipping.CurrencyCode      JPY
LowestOfferListing.Price.Shipping.Amount            250.00
LowestOfferListing.MultipleOffersAtLowestPrice      False



他のAPI呼び出し結果は、こちらの記事を参照してください。
Amazon MWS商品APIの実行結果例

サンプルプログラム

本APIを使用した、C#とPHPのサンプルプログラムを作成しました。
実際にプログラムを書いてみたい人は、参考にしてください。
C#でAmazonのMWSを使用して,マケプレの出品情報を取得する
PHPでAmazonのMWSを使用して,マケプレの出品情報を取得する

C#の記事のほうではDLLも用意しているので、DLLの読み込みが出来れば他の言語でも同様に作業できるかと思います。

リンク集

Amazonから提供されているドキュメントです。

Amazon マーケットプレイスWeb サービス 商品API セクションリファレンス
Amazon MWS Scratchpad
APIリファレンス
Product Advertising APIから商品 APIセクションへの移行ガイド

[C言語]リンクリストを使用したプログラムの基礎

構造体とmallocによるメモリの確保を利用した、リンクリストのサンプルプログラムです。

//----------------------------------------------------------------------
// File        : linklist.c
// Description : リンクリストを使用して、入力された点数の平均点を求める
//----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
 
struct t_scoreNode {
    int                Score; // 点数
    struct t_scoreNode *Next; // 次のデータへのポインタ
};
typedef struct t_scoreNode ScoreNode;
 
 
int main()
{
    ScoreNode *PtrRootNode = NULL; // 最初の要素
    ScoreNode *PtrCurNode  = NULL; 
    int status = 100;
    int score = 0;  // 入力された点数
    int total = 0;  // 合計点
    int count = 0;  // 入力人数
 
    while( 1 ) {
        //------------------------------------
        // 点数の入力を求める
        // (面倒なのでscanfで手抜きしてます) 
        //------------------------------------
        printf( "点数を入力してください :" );
        scanf( "%d", &score ); 
 
        //---------------------------------
        // 入力チェック
        //---------------------------------
        if ( score < 0 ) { 
            // 負の数が入力されたら処理終了とみなす
            printf( "終了します\n" );
            status = 0;
            break;
        }
 
        //---------------------------------
        // 入力された値をリンクリストに追加
        //---------------------------------
        if ( PtrRootNode == NULL ) {
            // 最初の要素の場合
            PtrRootNode = (ScoreNode *)malloc( sizeof( ScoreNode ) );
            if ( PtrRootNode == NULL ) {
                printf( "メモリの確保に失敗しました\n" );
                status = 1;
                break;
            }
 
            PtrRootNode->Score = score;
            PtrRootNode->Next  = NULL;
        } else {
            // 2つめ以降の要素の場合
 
            // 最後のノードを見つける
            PtrCurNode = PtrRootNode;
            while( PtrCurNode->Next != NULL ) {
                PtrCurNode = PtrCurNode->Next;
            }
 
            // 新しいノードを作成し、入力値をセット
            PtrCurNode->Next = (ScoreNode *)malloc( sizeof( ScoreNode ) );
            if ( PtrCurNode->Next == NULL ) {
                printf( "メモリの確保に失敗しました\n" );
                status = 1;
                break;
            }
 
            PtrCurNode->Next->Score = score;
            PtrCurNode->Next->Next  = NULL;
        }
 
        //------------------
        // 平均点を求める
        //------------------
        total = 0;
        count = 0;
        PtrCurNode = PtrRootNode;
        while( PtrCurNode != NULL ) {
            total += PtrCurNode->Score;
            count++;
            PtrCurNode = PtrCurNode->Next;
        }
 
        printf( "現在の入力数:%d人, 平均点:%.1lf\n", count, (double)total/count );
    }
 
    //-------------------------
    // 確保したメモリを開放する
    //-------------------------
    PtrCurNode = PtrRootNode;
    while( PtrCurNode != NULL ) {
        ScoreNode *PtrNextNode = PtrCurNode->Next;
        free( PtrCurNode );
 
        PtrCurNode = PtrNextNode;
    }
 
    return status;
}

「CPUの創りかた」は、パタヘネ本の次に必読の一冊

4839909865
CPUの創りかた

CPU設計の超入門書として良書とという評判だったので、読んでみました。
表紙の絵は、書店で買うにはちょっと恥ずかしい感じですが、内容は至って真面目です。


説明の順序が良くて文章も平易で非常に読みやすかったので3時間程度での読了でした。すんなり内容が入ってきたのは、本書の読みやすさももちろんですが、先にパタヘネ本を読んでCPUの概要を把握していたのも大きかったです。

文章がどれくらい読み易いかは、以下のサンプルページを見てもらうと一目瞭然です。
専門的な内容にも関わらず、いわゆる教科書っぽい堅苦しさは一切有りません。
4839909865

4839909865

4839909865

この本を読む上での前提知識は、2進数、論理式(AND,OR等)が分かるレベルがあれば十分です。回路やプログラムの知識、CPUの仕組みについては、本文中で丁寧に説明してくれているので不要です。

本書の購入理由は、CPU自体を基板上で創りたいというのは目的ではなく、”論理回路の組み合わせでCPUが実装できるか?”の実例が見たかったので、その疑問に答えてくれる丁度良い本でした。この本のおかげで、自分の中での論理回路->CPUのミッシングリンクが幾分解消した気がします。

CPU内部の仕組みについては、パタヘネ本でもある程度踏み込んだ内容が書かれているのですが、この本を追加で読んでおく事で具体的な回路構成がイメージ出来る様になります。ここでいう”具体的な”は、論理回路の組み合わせや、周辺のアナログ回路とのI/Fの等を指しています。


パタヘネ本では省略されていた(そもそもCPU外の話なので、パタヘネ本の対象外なのですが)、外部入力のプルアップやリセット回路なども、仕組みを含めて詳細に説明されています。




余談ですが、書籍の中で作成したCPUであるTD4は非常にシンプルなアーキテクチャなので、アセンブラやシミュレータも簡単に作れそうです。PICのシミュレータ作りがひと段落したら、こっちも作ってみたいところです(というか、PICのシミュレータを中断してこちらを先に作ったほうが速いかも…)。構成のシンプルさのおかげで、ひょっとしたら論理回路レベルのエミュレーションまで作れるかもしれません。




とりあえずメモっておきたかった事を羅列しておきます。
全部読んでからのメモなので、本文の説明順では有りません。

TD4のスペック


4bitマイコン
 
命令長            : 8bit
 
プログラムメモリ  : 16word
 
レジスタ          : Aレジスタ、Bレジスタ(共に4bit)の2つ
                    OUT命令だけ、命令に対してレジスタが直交して無い
 
ステータスレジスタ: キャリーフラグ1bitのみ
                    (条件付ジャンプでのみ使用)
 
I/O               : 入力,出力ともに1ポート(共に4bit)
 
トランジスタ数    : 推定1500個程度らしい




CPU構成要素の実装方法


クロックジェネレータ
    RC発信とシュミットトリガIC(74HC14)で生成
 
レジスタ
    4bitカウンタ(74HC161)のカウンタ機能を殺して、4bitのFFとして使用
 
ソースレジスタの決定回路
    2ビットのセレクタ(74HC163)を使用して、決定している
    (74HC163は4ビットのセレクタが2個あるので、ICが2つ必要)
 
デスティネーションレジスタの決定回路
    レジスタに使った7HC161のLOADピンをAssertにすることで決定している
 
プログラムカウンタ
    4bitカウンタ(74HC161)のカウンタ機能を使用
 
I/O
    レジスタを1つ殺してI/Oを作る
    Input は、ALU->レジスタの出力を、外部ピンに変更する。
    Outputは、レジスタ->ALUの入力を、外部ピンに変更する。
 
 
プログラムメモリ
    ディップスイッチで代用!!
    (ROMだとライタが必要になるのを回避するため)
 
プログラムレジスタのアドレス指定
    74HC154のデコーダで,制御ユニットの4bit入力から読み出す番地を決定している
 
ALU
    4ビットの加算器(74HC283)を使用
 
ステータスレジスタ(C)
    ALUで使用している加算器のキャリーピンをFF(74HC74)に保持
 
命令のデコーダ
    これが、このCPUで一番複雑なところ
    真理値表を書いて、ロジックICでがんばる
    opcodeを工夫しているので、ORが4個とNANDが3個で実装できる
    74HC32,74HC10が各1個




命令のフォーマット

全命令共に、上位4bitがオペコードで、下位4bitがデータとなる。
対象レジスタを指定するビットは無い(命令毎に対象レジスタが固定になっている)

命令一覧

opcodeをどうやって決めたかは、本文中に説明無し。

命令          処理                  opcode
----------    --------------------  -------
ADD A, Im     A += Im               0000
ADD B, Im     B += Im               0101
MOV A, Im     A = Im                0011
MOV B, Im     B = Im                0111
MOV A, B      A = B                 0001
MOV B, A      B = A                 0100
JMP Im        pc = Im               1111
JNC Im        if(C==0) { pc = Im }  1110
IN  A         A = InPort            0010
IN  B         B = InPort            0110
OUT B         OutPort = B           1001
OUT Im        OutPort = Im          1011





CPUのステージ分け(Fetch/Decode/Execute/WriteBack)

ステージ分けはせず、非同期で処理している。
レジスタへの書き込みが終わるまでの遅延に対して、クロックが十分低い(1Hz or 10Hz)ので問題が起きないようになっている。

パイプライン処理は、当然行われていない。


その他メモ


CPUにとっては、MOV命令とADD命令は同じもの


ALUがどんな演算をしたかが違うだけ、と捕らえれば分かりやすい。

MOV命令はALUが何もせずに、デスティネーションレジスタに値を格納している。
ADD命令はALUが加算をして、デスティネーションレジスタに値を格納している。



前者の”ALUが何もせずに”を”ALUが0を加算して”に読み返ると同じというイメージがさらに伝わりやすいかもしれない。


エッジトリガの作り方


“セレクタとNOT回路*2″を2セット用意して直列につなげば良い。
仮に、直列につないだ2つを前段、後段と呼ぶ事にする。

以下の仕組みで、立ち上がりエッジの情報を保持する事が可能となる
(クロック信号を元にセレクタを切り替える)

クロックがOFFの時:
    前段側は、入力データを常に受け取れるようにする、
    後段側は、記憶回路の構成にして出力を安定させる。
 
クロックがONの時:
    前段側は、記憶回路の構成にして出力を安定させる。
    後段側は、入力データを常に受け取れるようにする。


これでクロックがONになった瞬間の入力値を、出力側は安定して出す事が出来る。

今まで、”論理回路の話”と、”クロック信号のエッジトリガによる同期処理”がつながってなかったのでこの説明は有りがたかった。
(書籍では、ちゃんと回路図も明示してもっと分かりやすく説明してくれてます)



というわけで、CPU周りの実装に興味がある人には、絶対にお勧めな一冊です。

4839909865
CPUの創りかた

ITmediaのTechTargetで”続きを読むには会員登録…”のページを、会員登録せずに読む方法

テクノロジー系のニュースサイトとして有名なITmediaが運営していているサイトに、TechTargetというものが有ります。

TechTargetでは、IT製品や、サービスの導入・購買について有用な情報を提供してくれています。
利用は無料なのですが、一部会員登録しないと読めない記事もあります。


TechTargetはこんなページのサイトです。



で、非会員の立場で記事を読んでいると、具体的には以下のような感じで、記事の途中までしか読めなくなっているという仕組みです(記事によっては最後まで読めるものも有ります)


続きを読むには会員登録(無料)が必要です。
TechTargetジャパンは、企業情報システム担当者やエンジニアのための無料の会員制サイトです。
会員登録することで、IT関連の製品・システム導入や運用管理に関する技術資料やリポート、
そのほかの会員限定コンテンツをすべて無料で閲覧できます。
アイティメディアIDを持っている方は次のページでIDパスワードを入力してください。
登録されていない方は新規登録ボタンをクリックしてください。




今まで、この表示が出ていたらそれ以上先はあきらめていたのですが、よく見てみると、その先の記事内容が、背景に薄く表示されている事に気づきました(非常に薄いのですが、目を凝らしてみると文章が有るのが分かります)。



始めは、画像かな?と思ったのですが、試しに範囲選択をしてコピペすると、メモ帳にテキストの貼り付けも出来てしまいます。


…という事は、cssで見辛くしてるだけに違いない!!と思い、firebugを使ってソースを調べてみるとその通りでした。


以下、調査の結果です。
まず、薄く表示されている記事本文のエリアですが、firebugで見てみると記事本体はclass=”.CmsMembersControlIn”というdivタグに入っており…


こいつのcss定義を見るとopacity=0.05(0.5%表示)という、超薄文字での表示にされています。




ちなみに、”会員登録(無料)が必要です。”の表示は、”.CmsMembersControl”なdivタグに入っている”.memberboxout”クラスのタグでした。




そこまで分かれば、cssをいじってあげる事で、もう少し見やすく出来そうです。
この邪魔者を排除するために、firebugで以下のcss定義をしてみました。
記事の文字を100%表示にして、会員登録の枠は非表示にしてしまいます。

.CmsMembersControlIn
	opacity: 1;
 
.CmsMembersControl .memberboxout
	display: none;




その結果は以下の通り。
思ったとおり、記事が読めるようになりました。





手動で見られるようになったら、次はこれを自動化したくなってきます。
firefoxユーザなら、こんな時のために最適のプラグインが有ります。

それはStylishというもので、これは指定したサイトに来ると、事前に定義されたスタイルシートを自動で適用してくれるという便利プラグインです。



プラグインをインストール後、新しいスタイルを追加し、以下の定義を追加します。


@-moz-document domain("techtarget.itmedia.co.jp") {
    .CmsMembersControlIn {
    	opacity: 1 !important;
    }
    .CmsMembersControl .memberboxout {
    	display: none !important;
    }
}



説明するまでも無いような定義ですが、これはブラウザで”techtarget.itmedia.co.jp”ドメインのページを開いたら、カッコ内にあるcssを適用してしろ。という内容です。




定義を保存後、TechTargetのサイトを再度表示させてみると…
自動で続き部分の記事が読みやすくなりました。


わかるFirefox―人気ブラウザの「基本操作」から「アドオン」まで

Bluetoothイヤホンに関する規格まとめ(EDR,HSP,A2DP,AVRCPなど)

AndroidやiPhone等のスマホで、音楽を聴く用のBluetoothのイヤホンを買う際の判断基準のメモです。


Bluetoothのバージョン

まずは、バージョンのチェックです。
とりあえず、Ver.2.0 + EDRならOK。

Ver.2.0 + EDR
	EDRは、Enhanced Data Rateで、高音質になります。
 
Ver.2.1 + EDR
	バージョンが2.1だとペアリング時の暗証番号入力が不要になります。
	但し、ペアリングは最初に行うだけなので、ver2.1か2.0かはあまり重要では無いです。
 
EDR無し
	音声通話用なので、オーディオ目的ではないです
	転送速度が低いので、音質が悪くなります




プロファイル

次はプロファイル。
音楽を聴くなら、A2DPとAVRCPの対応である事をチェックしておきます。

HSP(Headset Profile)
	モノラルでの音声送受信機能
 
	通話用なので、音楽再生には関係ないです。
	ハンズフリーで通話の着信をしたい場合は欲しいかも。
 
HFP (Hands-Free Profile)
	電話の発信・着信操作の機能。
	これも音楽再生には関係ないです。
 
A2DP (Advanced Audio Distribution Profile)
	ステレオ再生可能
	高音質
	※音楽を聴く時には実質必須のプロファイルです。
 
 
AVRCP (Audio Video Remote Control Profile)
	いわゆるリモコン機能の対応です。
	このプロファイルに対応していれば、レシーバ側から再生・停止・早送りetc
	の操作が出来る様になります。
	これも必須の機能です。



バージョン、プロファイルはスマホ側の対応も見る必要があります。
あと、SCMS-T対応のものが有りますが、これ著作権保護対応で、これが無いとワンセグ視聴時に音声が飛んでこない等の問題がある事も発生します。


レシーバの形状

レシーバは、イヤホンが一体になっているものと、レシーバとイヤホンがバラ(はずせる)モノが有ります。
総額では一体型のほうが安いですが、イヤホンが断線した時には、レシーバごと交換が必要になります。

個人的には分離型がお勧めです。


クラス

BluetoothのクラスにはClass1とClass2が有ります。
Class1は理論値で100m届くので離れたところで使用する場合は、Class1が必須です。
出先で、スマホをカバンに入れてワイヤレス再生したいといった場合はClass2で十分です。


充電コネクタ

USB充電が多いですが、miniBとmicroBの形状のものが有ります。
AndroidのスマホはmicroBのコネクタが多いので、microB対応のものにしておけば、持ち運ぶケーブルが減るので便利です。


再生時間

Bluetoothのイヤホンは小型なので、10時間ぐらい持てば長時間な方になるかと思います。
安物は再生時間が極端に短いものも有ったりするので注意が必要です。


複数台のペアリング

iPhone+Androidや、ノートPCでの使用など、複数台のBluetoothデバイスを持っている場合は、複数台のペアリングが出来ると便利です。マルチペアリングとか書かれてたりもします。

[パタヘネ:読書メモ]付録A グラフィックスとGPU コンピューティング

この章の筆者はNVIDIAの人なので、内容にバイアスが入っている可能性があるかも….

A.1 はじめに

GPUはグラフィックプロセッサだけではなく、複数コアを持つ並列数値演算の側面も持ってきている。最近は汎用プログラムを実行するための仕組み(ハードのサポートや開発環境)が整備されてきている。

CUDAというプラットフォームでは、C,C++で開発が出来て、メニーコア環境での並列処理が行える。


A.2 GPUシステムのアーキテクチャ


昔のVGAカードは、ノースブリッジとサウスブリッジをつないでいるPCIバスに接続されていた。

一方今のGPUは、メモリと同列レベルに格上げされており、ノースブリッジ配下になっている。また、GPUとCPUは互いに相手のメモリにアクセスできるようになっている。

ローエンドのGPUはGPU専用のメモリを使用せずにCPUの主記憶をGPU用に共用している。これはユニファイド・メモリ・アーキテクチャ(UMA)と呼ばれている。



A.3 GPUのプログラミング

GPUをグラフィックス向けではなく、数値計算目的で使用するんいは、以下のようなインターフェースがある。

CUDA
	NVIDIAが開発した言語
	C,C++を機能拡張している
 
Brook
	GPUに適合化したストリーミング言語らしい??
 
CAL(Compute Abstraction Layer)
	AMDによるGPU向けのアセンブラ



CUDAでは3つの抽象化を行っている。

スレッドグループ階層
共有メモリ
バリア同期



これらを使用することで、多数のデータを分割して複数スレッドで実行し、全スレッドが終わるのを待つ(同期を取る)といった事が簡単に出来る。
C言語の拡張は、このスレッド呼び出しの記法とかが対象になってるっぽい。


A.4以降

興味が無いので、以下の節は省略(読んでない)

A.4マルチスレッド型マルチプロセッサのアーキテクチャ A.5 並列記憶システム
A.6 浮動小数点算術演算
A.7 実例:NVIDIA GeForce 8800
A.8 実例:GPUへのアプリケーションの適用
A.9 誤信と落とし穴
A.10 おわりに
A.11 歴史展望と参考文献(◎CDコンテンツ)


[gcc]CPUID命令を使用して、CPUの情報を取得する

IntelのCPUでは、CPUIDというアセンブラの命令を使用することで、CPUの情報を入手する事が出来ます。
ここでいうCPU情報というのは、CPUのシリーズや、対応している命令セット、キャッシュ容量、プロセッサシリアルNoなどが含まれます。

このCPUID命令、C言語で普通にプログラムしていてはコールする事が出来ませんが、インラインアセンブラを使用することで利用する事が可能です。

今回は、gccのインラインアセンブラである”__asm__”命令を使用して、CPUID情報を取得する方法を説明します。


C言語からCPUID命令をコールする


まずは、CPUID取得のベースになるインラインアセンブラ処理です。

void getCpuId( int param,
                unsigned int *eax,
                unsigned int *ebx,
                unsigned int *ecx,
                unsigned int *edx )
{
    /* cpuid命令はeax(param)で指定した値に応じた情報を返す */
    __asm__( "cpuid"
             : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
             : "0" (param) );
}



__asm__を使用して、cpuid命令をコールしています。
cpuid命令は、eaxレジスタにパラメータを渡すと、そのパラメータに応じた情報をeax~edxレジスタにセットしてくれるという仕組みです。

今回は、C言語の関数であるgetCpuId()関数の第1引数にパラメータをセットすると、第2~5引数にレジスタ値が返されるようにしました。

ちなみにこのコード、gccによるアセンブラ出力は以下のようになりました。
※アセンブラ出力はgccに-Sオプションを指定する事で確認できます。
インラインアセンブラの記述は、ちゃんと効いているみたいですね。

.globl _getCpuId
    .def    _getCpuId;  .scl    2;  .type   32; .endef
_getCpuId:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %esi
    pushl   %ebx
    movl    8(%ebp), %eax
/APP
 # 74 "cpuid.c" 1
    cpuid
 # 0 "" 2
/NO_APP
    movl    12(%ebp), %esi
    movl    %eax, (%esi)
    movl    16(%ebp), %eax
    movl    %ebx, (%eax)
    movl    20(%ebp), %eax
    movl    %ecx, (%eax)
    movl    24(%ebp), %eax
    movl    %edx, (%eax)
    popl    %ebx
    popl    %esi
    popl    %ebp
    ret



はじめて読むPentium マシン語入門編


CPUID命令に渡すパラメータ


次は、CPUID命令がサポートしているパラメータの情報のセットについてです。

これは、Intelのサイトで公開されている、アセンブラの命令セット・リファレンスに載っています。
IA-32 インテルR アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 A: 命令セット・リファレンス A-M



以下にドキュメントよりCPUIDのパラメータ情報を抜粋したものを引用します(pdfのp170辺りです)
※画像はクリックで拡大します






表の左端にある値がパラメータです。



CPUID命令を呼び出してみる


沢山の情報が入手できますが、今回は比較的確認が容易な”プロセッサブランドストリング”を取得してみる事にします。
プロセッサブランドストリングは、eaxレジスタに,80000001H~80000004Hの値を順に設定していく事で取得可能です。但し、全てのプロセッサでこの情報が取得可能とは限らないので、始めに80000000Hパラメータで、対応状況を見る必要があります。

この処理を、先ほど作った関数を利用してC言語でコーディングすると、以下のようになります。

void getProsessorBrandStr( char *outStr ) {
    unsigned int eax;
    unsigned int ebx;
    unsigned int ecx;
    unsigned int edx;
 
    char buff[64];
    memset( buff,   0x00, sizeof( buff   ) );
 
    getCpuId( 0x80000000, &eax, &ebx, &ecx, &edx );
    if ( eax < 0x80000004 ) {
        // cpuidによるプロセッサ名の取得に未対応
        *outStr = '\0';
        return;
    }
 
    getCpuId( 0x80000002, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
 
 
    getCpuId( 0x80000003, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
 
    getCpuId( 0x80000004, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
}



この関数の呼び元は、以下のような感じになります。

int main()
{
    char outStr[64];
 
    // initialize
    memset( outStr, 0x00, sizeof( outStr ) );
 
    // CPUIDでCPUのブランド文字列を取得する
    getProsessorBrandStr( outStr );
    printf( "-- brand string --\n" );
    printf( "%s\n", outStr );
 
}




手元の環境で実行してみたところ、以下のような出力になりました。
どうやら、上手くいったようです。

-- brand string --
Intel(R) Core(TM) i7 CPU       L 640  @ 2.13 GHz






以下が、今回作成したプログラムの全文です。

#include <stdio.h>
#include <string.h>
 
 
void getCpuId( int param,
                unsigned int *eax,
                unsigned int *ebx,
                unsigned int *ecx,
                unsigned int *edx );
 
void getProsessorBrandStr( char *outStr );
 
int main()
{
    char outStr[64];
    unsigned int eax;
    unsigned int ebx;
    unsigned int ecx;
    unsigned int edx;
 
    // initialize
    memset( outStr, 0x00, sizeof( outStr ) );
 
    // CPUIDのサポートレベルを取得する
    getCpuId( 0x80000000, &eax, &ebx, &ecx, &edx );
    printf( "cpuidサポートレベル: eax=%x\n", eax );
 
 
    // CPUIDでCPUのブランド文字列を取得する
    getProsessorBrandStr( outStr );
    printf( "-- brand string --\n" );
    printf( "%s\n", outStr );
 
}
 
 
void getProsessorBrandStr( char *outStr ) {
    unsigned int eax;
    unsigned int ebx;
    unsigned int ecx;
    unsigned int edx;
 
    char buff[64];
    memset( buff,   0x00, sizeof( buff   ) );
 
    getCpuId( 0x80000000, &eax, &ebx, &ecx, &edx );
    if ( eax < 0x80000004 ) {
        // cpuidによるプロセッサ名の取得に未対応
        *outStr = '\0';
        return;
    }
 
    getCpuId( 0x80000002, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
 
 
    getCpuId( 0x80000003, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
 
    getCpuId( 0x80000004, &eax, &ebx, &ecx, &edx );
    sprintf( buff, "%4.4s%4.4s%4.4s%4.4s", &eax, &ebx, &ecx, &edx );
    strcat( outStr, buff );
 
}
 
void getCpuId( int param,
                unsigned int *eax,
                unsigned int *ebx,
                unsigned int *ecx,
                unsigned int *edx )
{
    /* cpuid命令はeax(param)で指定した値に応じた情報を返す */
    __asm__( "cpuid"
             : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
             : "0" (param) );
}





今回は、gccでインラインアセンブラを使用する事により、CPUID命令を発行する事で、プロセッサの情報を取得する方ほうを説明しました。例で取得したのはプロセッサのブランド名称だけでしたが、他にも命令セットの情報や、自分のPCがx64に対応しているか、プロセッサのシリアル値など様々な情報が入手可能です。

恋するCUPID

[パタヘネ:読書メモ]第7章 マルチコアとマルチプロセッサとクラスタ

7.1 はじめに


単一のCPUの能力を向上させるには、密度、発熱量的に限界があるので、可能であれば複数のプロセッサ(マルチプロセッサ)を使用してプログラミングを行いたい。”能力を向上…”の能力には色々な意味があるけど、ここでは単位時間当たりの処理量であるスループットを対象とする。マルチプロセッサで、スループットをあげるには全てのプロセッサに別の仕事(ジョブ)を割り当てればよい。

独立したジョブが沢山ある場合の特性を、ジョブレベル並列性(あるいはプロセスレベル並列性)と呼ぶ。
一方、単一のプログラムのコード片を複数のプロセッサで分散処理する場合は、並列処理プログラムと呼ぶ(マルチスレッド的な意味も含む?)。


後者の場合、仮にハードウェアが複数プロセッサ対応できたとしても、ソフトが並列処理可能な形になっていなければ意味が無い。なので、並列処理プログラムをいかに容易に作成できるかの仕組み(言語、ライブラリ)も重要となってくる。

並列処理関係で使われる用語は、一部誤用(というか混乱)があるので、一旦整理しておく

直列 vs 並列

直列(serial)
	あるプロセスを1つのプロセッサで処理する
 
並列(parallel)
	あるプロセスを複数のプロセッサで分散処理する
	(マルチコア的な意味)



逐次 vs 同時並行

逐次(sequential)
	逐次は1つ目のジョブが終わるまで、2つ目のジョブを開始しない
 
同時並行(concurrent)
	逐次は複数のジョブを時間分割しながら同時並行で作業する
	(WindowsやUNIXのマルチタスク的な意味)



上記2つの概念は直交するので、直列で同時並行の仕組みや、並列で逐次のモノも存在する。
以降の説明では、parallelなハードウェア上で動くプログラムを”並列処理プログラム”と表記している。



7.2 並列処理プログラム作成の困難さ


並列処理で難しいのはハードではなく、ソフトウェア側になる。
これは、ソフトウェアはマルチスレッド等で複数の処理を行う場合、処理の分割や、各処理での同期、負荷の平準化の考慮と、それらのオーバーヘッドの考慮が必要となるから。


並列処理で能力を向上させる(スケールさせる)場合の前提条件には以下の2つがある。

強いスケーリング
	問題の規模(データ量etc)はそのままで、プロセッサを増やした時に
	どの程度能力が向上するか
 
弱いスケーリング
	問題の規模と、プロセッサを共に増やした時にどの程度能力が向上するか




7.3 共有記憶型マルチプロセッサ


共有記憶型マルチプロセッサ(SMP)というのは、複数のCPUが単一の主記憶(物理メモリ)に、均等にアクセスできるハード構成を言う。この構成だと、プログラマはマルチスレッドのような形で処理の並列化だけを気にすればよくなるので、ソフト側の負荷が少ない。

SMPアーキテクチャは、メモリアクセスの方法の違いによって2つに分割される。

UMA:均等メモリアクセス
	全てのプロセッサはどのメモリアドレスにも等しい時間でアクセスが出来る。
 
NUMA:非均等メモリアクセス
	全てのプロセッサはメモリアドレスによってアクセス時間が異なる。



ソフト側にとってはUMAのほうがプログラミングしやすい。一方ハード側にとってはNUMAのほうがスケールさせやすいハード構成が作れる。


並列処理プログラムでは、処理対象データを共有する事が一般的なので、各処理(スレッド)間での同期処理が必要となる。同期を取るためには、排他(ロック)処理が必要となるが、これは2章で説明している。


7.4 クラスタおよびその他のメッセージ交換型マルチプロセッサ


先ほどの7.3節では全CPUが単一のメモリ空間にアクセスできるパターンを考えたが、本節では各CPUが専用のメモリを持つアーキテクチャを考える。このような仕組みの場合、プロセッサ間(スレッド間)でデータをやり取りするために、メッセージ交換処理が必要となる。

但し、たとえばWebサーバのように各処理間でデータの共有が無い(orほとんど無い)場合もあり、このような際にはメッセージ交換の仕組みは不要となる。


メッセージ交換式の仕組みでスケールさせやすい仕組みの1つが、クラスタ構成になる。

キャッシュ設計者からすると、SMPよりクラスタの方が、キャッシュ整合性の問題がなくなるので、設計が簡単になる。一方ソフト屋からすると、メッセージ交換には非常にコストが掛かる(高レイテンシ)ので、コーディングが難しくなる。



7.5 ハードウエア・マルチスレッディング


本節では、マルチスレッド処理をソフトウェア的なコンテキスト切り替えではなく、ハードで実装するアーキテクチャを考える。
このような場合、CPUはスレッドの数分のレジスタやプログラムカウンタの記憶域を用意する必要がある。

ハードウエア・マルチスレッディングの実装は難しいが、並列化の恩恵だけでなく、CPUがI/O待ち(キャッシュミス等)になった時に替わりに実行できるほかの処理を持つことも出来るので、ハードウェア資源の効率化が出来る。


7.6 SISD,MIMD,SIMD,SPMD,ベクトル


本章のこれまでの話は、ハード構成(マルチコア/クラスタetc)の話だったけど、本節は単一CPU内の命令セットの話になる。

今までは1つの命令で単一データ(レジスタ)の値を演算していたが、もし1命令で複数レジスタの計算を同時に行えれば、能力を向上させる事が可能となる。

このあたりの用語を分類すると、以下のようになる。

SISD
	単一命令で単一データを処理する
	※SISDのIはInstruction(命令)で、Dはデータを意味している。
 
SIMD
	単一命令で複数データを一気に処理する
	例えばIntelのMMX命令のようなもの
 
MIMD
	複数の命令ストリームで、複数データを処理する
	(いわゆるマルチプロセッサ)
 
SPMD
	単一のプログラムが全てのプロセッサで、複数データを処理する
	一般的名MIMDのモデルらしい??
	※PはProgramの略



SIMDが威力を発揮するのは、forループのように大量の配列データに同じ処理を行うような場合になる。
逆にifやswitchなどの分岐が有る処理は苦手になる。


7.7 グラフィックス処理ユニットの概要

7.8 マルチプロセッサ・ネットワーク・トポロジの概要

7.9 マルチプロセッサのベンチマーク

7.10 ルーフライン:単純な性能モデル

7.11 実例:ルーフライン・モデルを用いた4つのマルチコアのベンチマーキング


省略(興味が無いので流し読みしただけ)


7.12 誤信と落とし穴


誤信:Amdahlの法則は並列コンピューティングに適用できない

ソフトを並列化に対応させても、並列処理できない部分が残るので、仮に無数のCPUを用意できたとしても、非並列化部分がボトルネックになる。

ただ、データを増やせばスケールさせることは可能となる。ここから得られる知見は、データ量に応じてアルゴリズムの再考慮が必要となるかも。という点になる。


誤信:ピーク性能は現実の性能を反映する


このネタは、前の章でも出た気が…
ピーク性能は理論値だし、他にボトルネックが出来るとピーク値は出せないので余り意味が無い。


落とし穴:マルチプロセッサアーキテクチャを十分に活用した,あるいはそれに最適化したソフトウェアを開発しない事


CPUが並列化を用意しても、例えばOS等のレイヤで処理の直列化がされていたら、ユーザプログラムを工夫しても並列化のメリットを享受できない。


7.13 おわりに

省略

7.14 歴史展望と参考文献(◎CDコンテンツ)

省略

7.15 演習問題

省略


[C言語入門]共用体でメモリを有効活用

今回は共用体です。
共用体の定義は以下のように行います。

union タグ名 {
    メンバ1;
    メンバ2;
    ...
};




一見すると構造体っぽい定義方法で、同じように見えるかもしれませんが、構造体と、共用体ではメモリ確保のされ方が異なります。

このことを確認するために、サンプルを1つ実行してみます。

#include <stdio.h>
 
struct t_CHARACTER {
    char c0;
    char c1;
    char c2;
    char c3;
};
 
union u_CHARACTER {
    char c0;
    char c1;
    char c2;
    char c3;
};
 
 
int main()
{
    struct t_CHARACTER data1;
    union u_CHARACTER  data2;
 
    data1.c0 = 'a';
    data1.c1 = 'b';
    data1.c2 = 'c';
    data1.c3 = 'd';
 
    data2.c0 = 'e';
    data2.c1 = 'f';
    data2.c2 = 'g';
    data2.c3 = 'h';
 
 
    printf( "\ndata1 --\n" );
    printf( "%c\n", data1.c0 );
    printf( "%c\n", data1.c1 );
    printf( "%c\n", data1.c2 );
    printf( "%c\n", data1.c3 );
 
    printf( "\ndata2 --\n" );
    printf( "%c\n", data2.c0 );
    printf( "%c\n", data2.c1 );
    printf( "%c\n", data2.c2 );
    printf( "%c\n", data2.c3 );
 
 
    printf( "\n" );
    printf( "\ndata1 address --\n" );
    printf( "%p\n", &data1.c0 );
    printf( "%p\n", &data1.c1 );
    printf( "%p\n", &data1.c2 );
    printf( "%p\n", &data1.c3 );
 
    printf( "\ndata2 address --\n" );
    printf( "%p\n", &data2.c0 );
    printf( "%p\n", &data2.c1 );
    printf( "%p\n", &data2.c2 );
    printf( "%p\n", &data2.c3 );
 
    return 0;
}



実行結果

data1 --
a
b
c
d
 
data2 --
h
h
h
h
 
 
data1 address --
0x28cc8c
0x28cc8d
0x28cc8e
0x28cc8f
 
data2 address --
0x28cc8b
0x28cc8b
0x28cc8b
0x28cc8b




構造体の場合は、各メンバ変数が異なるメモリ領域に割り当てられますが、共用体の場合は同じアドレスが使用されます(場所が”共用”されます)。上記サンプルのdata2ではc0~c3へ順に値をセットしていますが、これは同一番地への書き込みとなり、最終的にc3の値が有効になります。



上記の例だと、共用体のメリットがわからないかと思いますので、もう少し役に立ちそうな別の例を2つほど出してみます。

#include <stdio.h>
 
union u_test {
        int idata;
        unsigned char cdata[4];
};
 
int main()
{
        union u_test data2;
 
        data2.idata = 10;
 
 
        printf( "\ndata2(idata) --\n" );
        printf( "%p %d\n", &data2.idata, data2.idata );
 
        printf( "\ndata2(cdata) --\n" );
        printf( "%p %04x\n", &data2.cdata[0], data2.cdata[0] );
        printf( "%p %04x\n", &data2.cdata[1], data2.cdata[1] );
        printf( "%p %04x\n", &data2.cdata[2], data2.cdata[2] );
        printf( "%p %04x\n", &data2.cdata[3], data2.cdata[3] );
 
        return 0;
}



実行結果

data2(cdata) --
0x28cc8c 10
 
data2(cdata) --
0x28cc8c 000a
0x28cc8d 0000
0x28cc8e 0000
0x28cc8f 0000



このプログラムでは、4byteのint変数に整数値をセットしたとき、その値が内部的にどのように格納されているかを確認することができます。今回はintelのCPU上でコンパイルしたのですが、若い番地のアドレスに下位ビットの情報が入っていることがわかりました。このようなメモリ確保の方法を、小さい番地から使っているのでリトルエンディアン(little endian)と呼びます。

プログラムを実行しているCPUによっては、以下の出力になる場合もあります。
これは、データを大きい番地から使っているのでビッグエンディアン(big endian)と呼びます。

data2(cdata) --
0x28cc8c 0000
0x28cc8d 0000
0x28cc8e 0000
0x28cc8f 000a





2番目の例です
この例は、セットされたデータを複数の方法でアクセスする事ができるというサンプルです。このような書き方をすることは多くないですが、共用体の各メンバが指す先頭アドレスが同一だという事がわかればOKです。

#include <stdio.h>
#include <string.h>
 
struct DETAIL {
        char c0;
        char c1;
        char c2;
        char c3;
};
 
union DATA {
        char str_array[4];
        struct DETAIL d;
};
 
 
int main()
{
        union DATA c;
 
 
        strcpy( c.str_array, "abc" );
 
        printf( "\ndata2(idata) --\n" );
        printf( "%p %s\n", &c.str_array, c.str_array );
 
        printf( "\nc(cdata) --\n" );
        printf( "%p %c\n", &c.d.c0, c.d.c0 );
        printf( "%p %c\n", &c.d.c1, c.d.c1 );
        printf( "%p %c\n", &c.d.c2, c.d.c2 );
        printf( "%p %c\n", &c.d.c3, c.d.c3 );
 
        return 0;
}



実行結果

data2(idata) --
0x28cc8c abc
 
c(cdata) --
0x28cc8c a
0x28cc8d b
0x28cc8e c
0x28cc8f




次の例は、unionが実際にlinux等のOSで使用されている例です(説明しやすいように一部改変してあります)
ちょっと難しいので、よく意味が分からない方は読み飛ばしていただいても結構です。

以下のファイルは/usr/include/ext2fs/ext2fs.hから抜粋したものです。

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    unsigned short int   i_mode;     /* File mode */
    unsigned short int   i_uid;      /* Low 16 bits of Owner Uid */
    unsigned int         i_size;     /* Size in bytes */
    unsigned int         i_atime;    /* Access time */
    unsigned int         i_ctime;    /* Inode change time */
    unsigned int         i_mtime;    /* Modification time */
    unsigned int         i_dtime;    /* Deletion Time */
    unsigned short int   i_gid;      /* Low 16 bits of Group Id */
    unsigned short int   i_links_count;  /* Links count */
    unsigned int         i_blocks;   /* Blocks count */
    unsigned int         i_flags;    /* File flags */
    union {
        struct {
            unsigned int         l_i_version; /* was l_i_reserved1 */
        } linux1;
        struct {
            unsigned int        h_i_translator;
        } hurd1;
    } osd1;             /* OS dependent 1 */
    unsigned int         i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
    unsigned int         i_generation;   /* File version (for NFS) */
    unsigned int         i_file_acl; /* File ACL */
    unsigned int         i_dir_acl;  /* Directory ACL */
    unsigned int         i_faddr;    /* Fragment address */
    union {
        struct {
            unsigned short int   l_i_blocks_hi;
            unsigned short int   l_i_file_acl_high;
            unsigned short int   l_i_uid_high;   /* these 2 fields    */
            unsigned short int   l_i_gid_high;   /* were reserved2[0] */
            unsigned int         l_i_reserved2;
        } linux2;
        struct {
            unsigned char        h_i_frag;   /* Fragment number */
            unsigned char        h_i_fsize;  /* Fragment size */
            unsigned short int   h_i_mode_high;
            unsigned short int   h_i_uid_high;
            unsigned short int   h_i_gid_high;
            unsigned int         h_i_author;
        } hurd2;
    } osd2;             /* OS dependent 2 */
};



大きなくくりでみると、structの中に2つunionがあります。

これはHDDのファイルをOSが内部管理するための構造体です。
この構造体はlinuxやhurdという複数のOSが利用しており、両OSでデータ構造は、ほぼ同じなのですが一部OSによって依存(dependent)している部分があります。このOS依存の部分をunionとして定義することで、同一の構造体を利用しつつOS依存でフォーマットが異なる部分を吸収しています。

[gcc]C言語のプログラムからレジスタの内容をダンプする

C言語のコードをgccで開発している場合、インラインアセンブラを使う事でレジスタの内容をダンプさせることが出来ます。

以下のdefine文を定義しておくと、1命令書くだけで、全レジスタをダンプしてくれるので便利です。

#define DUMP_REGISTER() {                                   \
                          register int   eax asm( "%eax" ); \
                          register int   ebx asm( "%ebx" ); \
                          register int   ecx asm( "%ecx" ); \
                          register int   edx asm( "%edx" ); \
                          register void *esp asm( "%esp" ); \
                          register void *ebp asm( "%ebp" ); \
                          register void *esi asm( "%esi" ); \
                          register void *edi asm( "%edi" ); \
                          printf( "eax:%-10d 0x%x\n"        \
                                  "ebx:%-10d 0x%x\n"        \
                                  "ecx:%-10d 0x%x\n"        \
                                  "edx:%-10d 0x%x\n"        \
                                  "esp:%p\n"                \
                                  "ebp:%p\n"                \
                                  "esi:%p\n"                \
                                  "edi:%p\n",               \
                                  eax, eax, ebx, ebx,       \
                                  ecx, ecx, edx, edx,       \
                                  esp, ebp, esi, edi        \
                                );                          \
                        }



呼び出し側はこんな感じ。

int main()
{
    DUMP_REGISTER();
}




手元の環境で実行したところ、以下のような感じで出力されました。
実行はレジスタ構成に依存するので、当たり前ですがintelのCPUの環境下で実行する必要があります。

$ ./a.out
eax:1          0x1
ebx:0          0x0
ecx:0          0x0
edx:0          0x0
esp:0x22ac20
ebp:0x22ac98
esi:0x6127cd70
edi:0x0


[パタヘネ:読書メモ]第6章 ストレージおよびその他の入出力の話題 その2

6.7 入出力性能の測定法:ディスクおよびファイル・システムを例にして

省略

6.8 入出力システムの設計


入出力システムを設計する上で、要求される指標値には、バスのバンド幅と応答の遅延(レイテンシ)がある。

無負荷時のレイテンシ試算は簡単だけど、負荷がある場合は設計が難しい。
そのあたりの設計は、待ち行列理論などでシミュレーションさせたりする必要がある。



6.9 並列処理と入出力:RAID

基本的なRAIDの仕組みは常識レベルの話なので省略。



RAID構成の問題点として1台のディスクに故障が出ると、リビルド時に既存ディスクに負荷が高くなる問題がある。既存ディスクも同じ環境に晒されているため、1台のディスクに障害が発生した時は、連鎖して不具合が出やすい傾向がある。この為、予備ディスクを2台設けたRAID6が有効になってくる。


ディスク故障時に、替わりのディスクが届くまでの時間を削減するため、スタンバイスペアのディスクを用意する場合もある。


RAIDが有効なのは、基本的に”複数のディスクが同時に壊れない”というのが前提となっているけど、実態はそれ程甘くは無い。例えばサーバルームの空調が壊れたといった場合、全てのディスクが高温下にさらされる為、全ディスクがほぼ同時に壊れてしまうというリスクはある。また、RAIDを組む時は、同一メーカー・同一型番のディスクを複数用意する事が多いが、ロットが同じものをセットで使用すると、製造上の問題で不備があった場合に同時故障に繋がる。



ディスクの転送速度は遅いので、リビルドに時間が掛かるというのも問題。
1TBのデータをリビルドさせると、無負荷の時でも3時間ぐらい掛かってしまう。

RAID構成をとっているサーバの場合、無停止で運用を継続させたい場合も多い。稼動させながら(ディスクのI/Oがある状態)でのリビルドはさらに時間が掛かる。



6.10 実例:Sun Fire x4150サーバー

省略。



6.11高度な話題:ネットワーク(◎CDコンテンツ)

省略。



6.12 誤信と落とし穴


誤信:あるディスクの平均故障寿命(MTTF)が規格上は1,200,000時間である。これは役140年に相当する。したがってディスクが故障する事は事実上ありえない


故障までの時間は幅が有るので平均値を見てもあまり意味が無い。
また、メーカーは一定時間、多数のディスクを稼動させ、何台のディスクが壊れたかでMTTFを算出している。この算出方法だと、当たり前の事だけど実際に1,200,000時間稼動しているわけでは無いので、劣化に伴う故障のリスクは過小評価される事になる。

MTTFだと誤解してしまうので、替わりに年間故障率(AFR:annual failure rate)という指標値を使ったほうが直感に沿ったものになる。AFRは、沢山のディスクを1年使用した場合、何パーセントのディスクが壊れるかを意味している。



誤信:現場環境でのディスク故障率は仕様に合致している


過去に行われた実験によると、仕様値より5~6倍以上の故障率が有ったらしい。



誤信:1Gバイト/秒の相互連絡路は,1秒間に1Gバイトのデータを転送できる


これは、実際に開発、運用している側からしてみたら、直感的におかしい事が分かる。(実測値は仕様値より明らかに遅いので)理由としては、バスの占有待ち、その他コマンド送信に伴うオーバーヘッド等がある。

他には、バスの幅は1000を基準に単位を変えているけど、データ(ディスク容量等を含む)は1024を基準にしているので、実は単位が違うという所から、見かけ上の能力が落ちて見える。



落とし穴:エンド・ツー・エンドで実現すべき機能を、ネットワークの中だけで実現しようとする事


例えば、アプリケーションレイヤで行うべきデータのチェックサム計算を、下のレイヤで計算させたとする。この場合プログラマの負荷は低くなるが、通信レイヤ~ソフトウェア間での異常(例えばメモリの故障など)が検出できなくなる。



落とし穴:性能向上をもくろんで、十分な解析をせずにCPUから入出力プロセッサに機能を移すこと


普通はCPUより、入出力プロセッサのが能力が低いので、同じ仕事をさせるなら、CPUの方が効率が良い。
なので、非同期で複数の処理を行うメリットが、速度差のデメリットを超えない限り性能は上がらない。



落とし穴:ディスクのバックアップに磁気テープを使用すること


昔はテープのほうが、容量あたりのコストメリットがあったけど、今ではHDDのほうが単価が安くメリットが薄い。



誤信:オペレーティング・システムは、ディスクのアクセスをスケジューリングする最善の場所である。


ストレージの要件はストレージが一番知っているので、デバイス側に任せたほうがよいこともある。
例えばHDD上でどこにデータがあるかについて、OSはLBAによる論理位置しか知らないけど、HDDはディスクのどこのシリンダ、セクタにあるかという物理位置を知っている。
HDDにデータのアクセス順を(ある程度)任せてあげれば、ディスクの回転待ちが最小になるように最適化させることも可能となる。(例えばS-ATAではNCQ機能でこれを実装している)



落とし穴:入出力システム内の一部のピーク転送速度を使用して、性能の予測や比較を行うこと。


ピーク値というものは、そもそも出ない数字(理論上の最大値)なので、性能比較にはあまり意味が無い。大事なのは実効値。あと、Amdahlの法則より、ピーク値がよくても、ボトルネックが他にあれば、全体のスループットはそこに引っ張られるというのも重要なポイントになる。



6.13 おわりに

省略


6.14 歴史展望と参考文献(◎CDコンテンツ)

省略

6.15 演習問題

省略


[パタヘネ:読書メモ]第6章 ストレージおよびその他の入出力の話題

6.1 はじめに

6.2 信頼性と信頼度とアベイラビリティ

6.3 ディスク・ストレージ

6.4 フラッシュ・ストレージ

6.1~6.4の内容は基本的な事項なので省略
(応用情報処理の試験とかのレベル)


6.5 プロセッサ、メモリ、入出力装置間の接続


プロセッサ、メモリ、入出力装置間はバスで接続されている。
バスのメリットは、汎用的で低コストということ。

バスは大きく3つに分かれる。

プロセッサ-主記憶間バス
    最も距離が短く、最も速くする必要がある。
 
入出力バス
    距離が長く、複数のデバイスが接続される。
    I/Oが直接メモリに接続されるわけではなく、他のバスを経由して接続される。
 
バックプレーンバス
    単一のバスに、メモリ、I/Oを両方接続できるようにしたもの



バスは、信号線の物理的な距離や、最大接続数の制限が掛かる場合がある。


昔のバスは、クロックに同期してデバイスが動作していた。同期式メリット、デメリットには以下の点があげられる。

メリット
    ハードの設計が簡単
    高速に動作できる
デメリット
    バス内の各デバイスが同じクロックになること
    信号線が長くなるとクロックの遅延(クロックスキュー)が発生する



プロセッサが高速になるとデメリットが問題になってくるため、近年では非同期式のインターフェースが使用される事が多い。USB2.0,S-ATA,SAS,PCI Expressなどは全て非同期式になっている。

最近のPCで使用されているIntelやAMDのCPUでは、バスが上流(CPU寄り)と、下流で2つに別れている。
上流側のバスを管理しているのがノースブリッジというチップセット(LSI)で、メモリやPCI Expressのバスを管理している。こちらは短距離な配線で小数のデバイスが高速に動作している。

下流側は、サウスブリッジで、ディスクや、キーボード、マウス、USBなど低速なデバイスが多数接続される。


6.6 プロセッサ、主記憶、OSと入出力装置のインタフェース


というわけで、バスを通して入出力装置(I/O)が接続されることとなった。
これでCPUはI/Oとデータの受け渡しが可能になるけど、以下の考慮点がある。

1. メモリとI/O間でのデータ転送はどのように実装されるのか?
2. I/Oが要求するデータ送受信コマンドを誰が生成するか?
3. I/O処理において、OSは何をしているのか?



OSはI/Oとのやり取りに対して、以下の前提を置いている。

I/Oは複数のプログラムで共有される。
    -> OSはプログラムからのI/Oアクセスをコントロールしている
 
I/Oはデータの送受信に関して、割り込みを使ってCPUにトリガを発している。
    (割り込みは、一般にユーザモードではなくシステムモードで動作するので、OSやVMMの範疇となる)
    -> I/O割り込みはOSがトラップする
 
I/Oの制御は、時に複数のデバイスを同時に処理する必要があり、
またタイミングの問題もある為、管理が煩雑になる。
    -> デバイスやプロセスのスケジューリングはOSの主要な役割の1つ




I/Oへのコマンド送信

外部デバイスとのやり取りを行うためには、デバイスへのコマンド(データ)送受信が必要となる。
このコマンド送受信には2つの方式がある。

メモリマップドI/O
    メモリ空間上にI/Oデバイスを割り当てる
    (CPUから見ると特定アドレスにメモリ書き込み命令を行うと、バス経由でI/OがコマンドをReadする)
 
入出力用特殊命令
    I/O処理用を行う専用の機械語命令をCPUに持たせる




入出力用特殊命令の例としては、例えばIntelの8080(8086より前にでたCPU)では、IN命令/OUT命令というオペコードが存在している。


メモリマップドI/OのほうがCPU設計的にはシンプルになる。一方、入出力用の特殊命令があるとメモリ空間とI/O空間が分離できるので、メモリをフルに認識できる。
※例えば、Windows XPや7の32bit版では、最大搭載メモリが4GBなのに、認識可能なメモリサイズが3GB程度という制限が有った。もし入出力用の特殊命令を使用していたら4GB全てを認識できたはず。


I/Oとの通信には、以下の2つの方式がある。

ポーリング方式
割り込み方式



ポーリング方式は、CPU側がI/Oの状態を一定周期で監視する方法となる。
これは、擬似コードで書くと以下のようなイメージになる。

while( 1 ) {
    I/Oの状態をread
    if ( I/Oの状態を変わったら ) {
        何かをする
    }
 
    ...他の仕事を行う...
}



この方法は簡単だけどCPUサイクルの無駄遣いに繋がる。I/OはCPUに比べてとても遅いので、状態チェックをすべき状況に至ってから、期待する状態変化が起こるまで非常に長い時間が掛かるのが無駄が出る原因となっている。

なので、最近のCPUは割り込み方式でI/O制御を行う事が多い。
これは、I/Oの状態が変化したタイミングで、CPUに割り込みをかける。ポーリングに比べて割り込みのほうが、CPUはI/O側の処理が完了するまで非同期で他の仕事を行えるため、効率が良い。



割り込み処理を採用する場合、以下の点に注意が必要となる。

1.基本的に割り込みは非同期処理になる。
(CPUサイクル的に見て)割り込みが掛かった瞬間に、ソフトウェアはそのことを認識できるわけではなく、
I/Oが割り込みを立ててから、CPUが割り込み線を見るまでの遅延は存在する。
※もちろんこの遅延が発生したところで、ポーリング処理にに比べれば、断然CPU処理の無駄や
レイテンシは少ない
 
2.割り込みは複数発生するし、同時に発生する事がある。
CPU/ソフトウェアは優先順を決めて処理を行っていく必要がある。
また、CPUは何の割り込みが掛かったかを識別できる必要がある。
(MIPSではCAUSEレジスタで割り込み理由が識別できる)
 
 
3.割り込み中に割り込みが掛かる可能性もある
"割り込み中の割り込み"を処理すべきかどうかの配慮が必要。
これを認めないと処理は簡単になるけど、大事な処理が遅延するリスクがある。
逆に認めると、ソフトウェア的な制御が難しくなる。
(通常は、今処理している割り込みより優先度が高い割り込みだけ、多重割り込みを認める)





割り込み処理の実装

割り込み処理の実装は当然ソフトウェアによって異なるが、UNIXの場合、優先度が4~6レベルに分類されており、以下の特性を持つ。

入出力例外より、内部例外のほうが優先度が低い
入出力例外では、遅いデバイスのほうが優先度が低い



割り込み優先度の管理を行うためのサポートをMIPSでは行っている。
この為に、指定以上のレベルの割り込みのみを受け付けたり、割り込み理由の通知を行ってくれるレジスタを用意している。



ポーリングや割り込みは、どちらもCPUとI/O間の通信方法の仕組みだけど、都度CPUが関与する必要がある為、負荷が高い(=低速デバイス向けとなる)

一方でディスク-メモリ間のデータ転送などの場合は、kbyte単位のオーダーで転送が掛かる場合もある為、いちいちCPUが関わっていては遅くなってしまう。
この為、DMA(Direct Memory Access)を使って、ディスクとメモリ間のデータ転送をCPUが関与せずに行う仕組みをとる。

この場合、データ転送中は当然ながらCPUがバスを使用することが出来なくなる為、バスの占有権の管理が必要となってくる。
DMA転送を行う場合、DMAコントローラーというものを追加でバスに設ける。CPUやDMAコントローラーなどのバスを主体的に使う側のコントローラーを、バスマスタと呼ぶ。

DMA方式でデータの転送を行う場合、CPUとDMAコントローラは、以下の手順でやり取りを行う。

CPUはDMAコントローラに,データ転送要求を行う。
要求には通常、以下のような情報が含まれる
    入出力装置のデバイスNo
    read/writeの区分
    データ転送元アドレス
    データ転送先アドレス
    データ転送サイズ
 
DMAコントローラは、バスに存在する他のバスマスタに、バスの占有権を主張する。
 
DMAコントローラは、(Readの場合は)入出力装置からデータをReadし、メモリの指定された位置にWriteする。
(Writeの場合も方向が逆になるだけで同様)
 
 
DMAコントローラは、データ転送が完了するとCPUに外部割込みをかける。
 
CPUは、データ転送が終わった事を認識し、後続の処理を行う。



バスアーキテクチャによっては、DMAコントローラーは1つだけとは限らず、複数の場合もありえる。


もちろんDMA転送中にCPUがメモリアクセスが必要な場合はDMA待ちになってしまうが、キャッシュが効いていれば、ある程度はCPU内だけで他の処理を続ける事も可能となる。(というか、キャッシュのおかげでCPUが非同期処理できるので、DMAの価値が出てくる)


DMAコントローラは単なるメモリ操作を行うものだが、もう少し多機能なものもあり、これは”入出力プロセッサ”と呼ばれる場合もある。


超!A&G+の録音用URLが変更になりました

最近、”超!A&G+ 録音”あたりのキーワードでのアクセスが多いのでなぜだろう…? と思ってたのですが、どうやらA&G+側でradikaやGetASFStreamで予約録音するためのURLが変更になったようです。


というわけで、変更後のURLを調べる方法を説明しておきます。
firefoxで調べるほうが簡単なのですが、今回は誰でも利用可能なInternet Explorerを使って確認する方法を紹介します。確認はIE9で行っています。


ブラウザを立ち上げたら、まずはツール->開発者ツールをクリックします。


追加でウィンドウが開くので、ネットワークタブを選択し、キャプチャの開始ボタンをクリックします。


次に、IEのURL欄に以下のアドレスを入力しEnterを押します。
(このタイミングでメディアプレイヤーが表示されるかもしれませんが、そのまま放置して置いてください)

http://uniqueradio.jp/agplayer/ag.asx





すると、先ほど開いたウィンドウにアクセスしたURLが1行追加されるはずです。
この行を選択し、詳細ビューに移動をクリックします。


応答本文のタブをクリックすると”mms://1134mb-live2.daijinakoto.com/uniqueradio/2ch…”というようなURLが表示されるので、メモ帳にコピーします。
非常に長いURLでおそらく200文字以上有りますが「””」記号で囲まれた中を全部コピーしてください。



コピーしたURLの先頭がmmsになっているのでこれをrtspに書き換えます。
これで、新しい録音用のURLが入手できました。



このURLを元に、以前書いた記事の手順で設定を行えば録音可能になります。
radikaで「超A&G+」の予約録音を行う方法
超!A&G+ の番組を,GetASFStreamで予約録音する



B0084FSJEM
喜多村英梨 RE;STORY(初回限定盤)

gccのアセンブル出力を解析する時は-gオプションが便利

アセンブラの勉強のために、gccで-Sオプションを使用してC言語のコードからアセンブラのコードを出力していたのですが、ソースコード中の何行目がどの出力に対応するのか分かり辛く、苦労していました。

もちろん注意深く読めば対応は分かるのですが、本質的でないところに時間が取られてしまい、本来の解析が出来なくなってしまうので非効率です。

と思ってたのですが、コンパイル時に-gオプションをつけ、デバッグ情報を付与させると、ソースとアセンブリの対応が出力される事に最近気づきました。

以下、試してみた結果です。


こちらがテストに使ったコード

#include<stdio.h>
 
int main()
{
    int a;
    int b;
    int c;
 
    a = 2;
    b = 5;
    c = a + b;
    printf( "%d\n", c );
 
 
}



普通に “gcc -S test10.c”でコンパイル(アセンブリの出力)を行った場合

    .file   "test10.c"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $2, 20(%esp)
    movl    $5, 24(%esp)
    movl    24(%esp), %eax
    movl    20(%esp), %edx
    addl    %edx, %eax
    movl    %eax, 28(%esp)
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"



“gcc -S -g test10.c”でコンパイルした場合
(非常に長いので、ざっと読み流してください)

    .file   "test10.c"
    .text
.Ltext0:
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .file 1 "test10.c"
    .loc 1 4 0
    .cfi_startproc
    pushl   %ebp
.LCFI0:
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 9 0
    movl    $2, 20(%esp)
    .loc 1 10 0
    movl    $5, 24(%esp)
    .loc 1 11 0
    movl    24(%esp), %eax
    movl    20(%esp), %edx
    addl    %edx, %eax
    movl    %eax, 28(%esp)
    .loc 1 12 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    .loc 1 15 0
    leave
    .cfi_restore 5
.LCFI2:
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
.Letext0:
    .section    .debug_info,"",@progbits
.Ldebug_info0:
    .long   0xac
    .value  0x2
    .long   .Ldebug_abbrev0
    .byte   0x4
    .uleb128 0x1
    .long   .LASF10
    .byte   0x1
    .long   .LASF11
    .long   .LASF12
    .long   .Ltext0
    .long   .Letext0
    .long   .Ldebug_line0
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .long   .LASF0
    .uleb128 0x2
    .byte   0x1
    .byte   0x8
    .long   .LASF1
    .uleb128 0x2
    .byte   0x2
    .byte   0x7
    .long   .LASF2
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .long   .LASF3
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .long   .LASF4
    .uleb128 0x2
    .byte   0x2
    .byte   0x5
    .long   .LASF5
    .uleb128 0x3
    .byte   0x4
    .byte   0x5
    .string "int"
    .uleb128 0x2
    .byte   0x8
    .byte   0x5
    .long   .LASF6
    .uleb128 0x2
    .byte   0x8
    .byte   0x7
    .long   .LASF7
    .uleb128 0x2
    .byte   0x4
    .byte   0x5
    .long   .LASF8
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .long   .LASF9
    .uleb128 0x4
    .byte   0x1
    .long   .LASF13
    .byte   0x1
    .byte   0x3
    .long   0x4f
    .long   .LFB0
    .long   .LFE0
    .long   .LLST0
    .uleb128 0x5
    .string "a"
    .byte   0x1
    .byte   0x5
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 20
    .uleb128 0x5
    .string "b"
    .byte   0x1
    .byte   0x6
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 24
    .uleb128 0x5
    .string "c"
    .byte   0x1
    .byte   0x7
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 28
    .byte   0
    .byte   0
    .section    .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
    .uleb128 0x1
    .uleb128 0x11
    .byte   0x1
    .uleb128 0x25
    .uleb128 0xe
    .uleb128 0x13
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0xe
    .uleb128 0x1b
    .uleb128 0xe
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x10
    .uleb128 0x6
    .byte   0
    .byte   0
    .uleb128 0x2
    .uleb128 0x24
    .byte   0
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0xe
    .byte   0
    .byte   0
    .uleb128 0x3
    .uleb128 0x24
    .byte   0
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0x8
    .byte   0
    .byte   0
    .uleb128 0x4
    .uleb128 0x2e
    .byte   0x1
    .uleb128 0x3f
    .uleb128 0xc
    .uleb128 0x3
    .uleb128 0xe
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x40
    .uleb128 0x6
    .byte   0
    .byte   0
    .uleb128 0x5
    .uleb128 0x34
    .byte   0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2
    .uleb128 0xa
    .byte   0
    .byte   0
    .byte   0
    .section    .debug_loc,"",@progbits
.Ldebug_loc0:
.LLST0:
    .long   .LFB0-.Ltext0
    .long   .LCFI0-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 4
    .long   .LCFI0-.Ltext0
    .long   .LCFI1-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 8
    .long   .LCFI1-.Ltext0
    .long   .LCFI2-.Ltext0
    .value  0x2
    .byte   0x75
    .sleb128 8
    .long   .LCFI2-.Ltext0
    .long   .LFE0-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 4
    .long   0
    .long   0
    .section    .debug_aranges,"",@progbits
    .long   0x1c
    .value  0x2
    .long   .Ldebug_info0
    .byte   0x4
    .byte   0
    .value  0
    .value  0
    .long   .Ltext0
    .long   .Letext0-.Ltext0
    .long   0
    .long   0
    .section    .debug_line,"",@progbits
.Ldebug_line0:
    .section    .debug_str,"MS",@progbits,1
.LASF6:
    .string "long long int"
.LASF2:
    .string "short unsigned int"
.LASF0:
    .string "unsigned int"
.LASF11:
    .string "test10.c"
.LASF3:
    .string "long unsigned int"
.LASF7:
    .string "long long unsigned int"
.LASF1:
    .string "unsigned char"
.LASF13:
    .string "main"
.LASF8:
    .string "long int"
.LASF10:
    .string "GNU C 4.6.1"
.LASF4:
    .string "signed char"
.LASF5:
    .string "short int"
.LASF12:
    .string "/home/..."
.LASF9:
    .string "char"
    .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
    .section    .note.GNU-stack,"",@progbits




上記のように、-gオプションつきでコンパイルすると、出力が長くなりすぎるので敬遠してたのですが、コードが出力されている箇所を見ると、 “.loc”の行で.cファイルのソースコードが表示してくれています。

先ほどの出力結果から、該当箇所を抜粋すると…

    ...
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 9 0                     <- 1 9 0の"9"が行番号をあらわしている!!
    movl    $2, 20(%esp)
    .loc 1 10 0
    movl    $5, 24(%esp)



といった感じになり、.cファイルでの9行目が”movl $2, 20(%esp)”に変換された事が分かります。

ちなみに.locディレクティブ自体の書式は、以下の仕様だそうです。

.loc ファイル番号 行番号 [カラム位置] [オプション]





また、.cファイル上の1行が、アセンブラで複数個所に分かれる場合は、以下の出力になります。
※for文は通常、初期条件、終了条件、繰り返し処理の3つに分かれます。

ソース

#include<stdio.h>
 
int main()
{
    int i;
    for ( i = 0; i < 10; i++ ) {
        printf( "%d\n", i );
    }
}



gcc -S -g test10.cの出力(main関数のところだけ抜粋)

main:
.LFB0:
    .file 1 "test10.c"
    .loc 1 4 0
    .cfi_startproc
    pushl   %ebp
.LCFI0:
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 6 0
    movl    $0, 28(%esp)
    jmp .L2
.L3:
    .loc 1 7 0 discriminator 2
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    .loc 1 6 0 discriminator 2
    addl    $1, 28(%esp)
.L2:
    .loc 1 6 0 is_stmt 0 discriminator 1
    cmpl    $9, 28(%esp)
    jle .L3
    .loc 1 9 0 is_stmt 1
    leave
    .cfi_restore 5
.LCFI2:
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc




“.loc x 6″の記述が3箇所に分かれているのが確認できます。

i = 0; の部分

    .loc 1 6 0
    movl    $0, 28(%esp)
    jmp .L2



i++ の部分

    .loc 1 6 0 discriminator 2
    addl    $1, 28(%esp)



i < 10; の部分

    .loc 1 6 0 is_stmt 0 discriminator 1
    cmpl    $9, 28(%esp)
    jle .L3



複数個所に分かれる場合は、.locにdiscriminatorオプションが付くようです。

情報処理試験の過去問をblogで引用してもよいか?

IPAで春と秋に年二回開催されている情報処理技術者試験ですが、過去問の解き方や解答についてblogで言及したい場合、問題文を全文掲載できるのか気になってました。
試験問題も当然ながら著作権の対象になるので、勝手にコピペするとトラブルの元になります。


メールで直接問い合わせようと思っていたのですが、試験のWebサイトを見てたら、そのものズバリのQAが掲載されてました。

以下、引用です。

よくある質問
6.その他
試験の過去問題の使用方法(申請方法、使用料等)について教えてください。
 
 試験センターで公表している過去の試験問題の使用に関し、許諾や使用料は必要ありません。
(ダウンロードでのご利用も特に問題ございません。)
 ただし、以下の留意点を必ず確認の上、ご使用ください。
 
【留意点】
・著作権は放棄していません。
・教育目的など、情報処理技術者試験制度の意義に反しない限り、公表されている過去問題を
問題集やテキストに使用される際、許諾および使用料の必要はありません。
・出典は明記してください。
 [例]「出典:平成○年度 ○期 ○○試験区分 午前 問1 設問2」
 また、問題の一部を改変している場合、その旨も明記してください。
・公表しているPDF以外の電子データの提供はできません。
 (PDFセキュリティの解除方法は提供できません。)




というわけで、情報処理試験の過去問に関しては、出典さえ明記しておけば、いちいち連絡を取らなくても無償で使用することは可能でした。

試験対策や過去問解説のサイトなどを作りたい場合は、自分で問題作成をしなくても良くなるので作業が捗りそうです。

[C言語入門]ポインタを理解する その1:メモリとポインタ変数入門

今回の説明はポインタ変数です。
ポインタ変数は、特に難しいというわけではないのですが、C言語での表記の仕方の問題で混乱する人も多いので、今回はゆっくりと順を追って説明していきます。

ポインタの理解には、前提としてコンピュータの構成要素であるメモリ(主記憶装置とも言います)の理解が必要となるので、まずこちらから説明していきます。

変数はメモリ上でどのように保持されているのか?


まずは,変数とメモリについての説明です。今まで、”i=10;”といったコードを”変数に値をセットする”と説明していましたが、変数の値はコンピュータ上でどのように管理されているのでしょうか? コンピュータの構成要素の1つであるメモリの仕組みを確認しつつ、この謎を確認していきます。


実行中のプログラムが使用している変数(データ)は、メモリによって保持されています。
メモリの論理的な構造を図で表すと、以下のような感じになります。


メモリには、複数の値の格納しておくことが可能で、それぞれの格納場所には番地(アドレス)が割り振られています。

アドレス(表の左側)は連番になっていて、通常は0番地から順番に振られています。値の欄(表の右側)は、実際には何らかの数字が入っており、値の読み書きを行う事がが可能なのですが、何の値が入っているかはまだ重要では無いため、上記の絵では”???”と表記しています。(余談ですが、何が入っているか分からないことを情報の世界では”不定値が入っている”といいます。)

順番に振られているので、同一番地の格納場所が複数存在するという事はありえません。この事を、”アドレスはユニークである”と言います。
ここで”ユニーク”というのはコンピュータ用語で、”重複しない”、”一意である”という意味です。



実際のメモリがどの程度の大きさ(どの程度の情報量を記憶できるか)をイメージしてもらうために、例を挙げて説明します。
メモリに振られたアドレスの最後の番地はいくつぐらいになるかというと、これは使っているパソコンに搭載されているメモリ容量によります。ざっくりな値ですが、4GBのメモリを乗せたパソコンの場合は”42億番地”ぐらいまで有ります。(2の32乗≒42億です)


このメモリというものは、かなり大きなモノだということが分かります。(メモリ構造の図でいうと、縦方向の長さが膨大になります)また、このサイズは”大きいけど有限だ”という事も同時に理解しておいてください。

また、入れておく値の大きさ、通常は1つの番地に対して1byte(=8bit)です。
なので、1つのアドレスには、0~255の256通りの値が格納できます。(これは、”上記図の???には0~255の何れかの値がセットされている。”と言い換える事も出来ます)
こっちは、”結構少なめだな…”という印象を持ってもらえればOKです。
また、ここまでC言語を学んできた人にとっては、このサイズが”char型の変数が表せる範囲とちょうど同じ”だという事も押さえておいてください。


C言語の変数とメモリの関係


次に、C言語の変数と、メモリの関係について説明します。
C言語での変数は、コンピュータではメモリに保存されています。

変数を定義すると、コンパイラ(あるいはOS)が、どの変数を何番のアドレスでで管理するかを、(プログラマから見えないところで)勝手に割り当てをしてくれます。


例えば、以下のプログラムがあった場合に…

char c;
c = 20;




コンパイラがaを1000番地に割り当てたとすると、メモリの中身は以下の様になります。
※他の番地は何が管理されているか不明(不定値)なので、ここでは???と表記していますが、実際は何らかの値が格納されています。




charは1byteなので上記の様な形になりますが、intだと様子が少し変わってきます。
intは(32bit環境だと通常は) サイズが4byteになります。
なので、以下のプログラムは…

int i1;
int i2;
int i3;
 
i1 = 10;
i2 = 500;
i3 = 4000000;



仮に、変数i1が1000番地、i2が1004番地、i3が1008番地に割り当てられたとすると、このようなメモリ構造になります。



ここで、上記の構造がイメージできない場合は、以下の事を思い出してください。

メモリ1番地あたりに格納できるデータサイズは8bit
  (=8bitであらわせる値は256通りしかない)



この為、intの値はメモリ1番地分(1byte)だけでは保存しきれず、4マス分の領域(4byte)が必要となるという仕組みです。



ここまでの例で説明したint型やchar型の変数ですが、これは、”数字や文字”を保持するデータ型です。
一方ポインタというのは、”あるデータが保持されている場所”を保持するデータになります。
※余談ですが、ポインタは英語で書くとpointerとなり、”指し示すもの”という意味になります。

C言語でのポインタ型をまだ説明していませんが、ポインタ型がどのようなものかをイメージしてもらうために、ポインタ変数がメモリ上で何を記憶しているかを説明します。

int      i;     /* "int  "型の変数 "i"                                    */
int *    ip;    /* "int *"型の変数 "ip" (=intへのポインタ変数 ipとも呼ぶ) */
 
i  = 10;
ip = &i;



このプログラムで、変数iが2000番地、変数ipが2004番地に割り当たったとすると、メモリ構造は以下の形になります。iは値10を記憶していますが、ipは”変数iがどこの番地に割り当たったか”の場所を記憶しています。



これまで、アドレスの表記は10進数で行っていましたが、コンピュータでは16進数を使う事が多いです。
この習慣にそって、本記事ではこれ以降16進数で表記します。
10進数表記と16進数の違いを示すために、例を1つ挙げておきます。
下記の2つは同じ意味である事に注意して見比べてみてください。




変数が割り当てられたアドレスを確認する


ここまで説明したところで、C言語のプログラムに入っていきます。
まずは、今まで作ってきたようなプログラムで、変数が具体的に何番地のアドレスに割り当たったかを確認する方法を説明します。

というわけで、最初のサンプルです。

#include <stdio.h>
 
int main()
{
    int  i1;
    int  i2;
    char c1;
    int  i3;
 
    i1 = 10;
    i2 = 20;
    c1 = 'a';
    i3 = 30;
 
    printf( "i1=%d, &i1=%p\n", i1, &i1 );
    printf( "i2=%d, &i2=%p\n", i2, &i2 );
    printf( "c1=%c, &c1=%p\n", c1, &c1 );
    printf( "i3=%d, &i3=%p\n", i3, &i3 );
 
    return 0;
}



手元の環境で確認したところ、実行結果は以下のようになりました。
(カンマの後に表示される値は環境によって変わる可能性があります)

$ gcc ptr1.c -o ptr1
 
$ ./ptr1.exe
i1=10, &i1=0x22ac8c
i2=20, &i2=0x22ac88
c1=a, &c1=0x22ac87
i3=30, &i3=0x22ac80




プログラムの内容を順に説明します。
まず、変数に値をセットする箇所は、今まで説明したとおりです。
特に難しい事はしていません。

    i1 = 10;
    i2 = 20;
    c1 = 'a';
    i3 = 30;




次のprintfですが、今までは以下のような書き方でした。

    printf( "i1=%d\n", i1 );


上記の記法は、変数i1の値を画面に表示しています。
i1はint型なので%dの書式指定を行っていますし、c1はchar型なので%cで表示しています。


これに対して、今回は以下の表記になっています。

    printf( "i1=%d, &i1=%p\n", i1, &i1 );


ここで、&i1と変数の前に”&”記号がついていますが、これはアドレス演算子というものです。
変数に対してアドレス演算子をつけることで、変数が割り当てられた場所(アドレス)を求める事ができます。
アドレス演算子を使えば、どのような型の変数でもその場所を求める事が可能です。

アドレス演算子で求めたアドレス値をprintfで表示させるためには、書式指定で%pを使用します。

ここまで理解したところで、再度プログラムの出力結果を見てみます。

$ ./ptr1
i1=10, &i1=0x22ac8c
i2=20, &i2=0x22ac88
c1=a, &c1=0x22ac87
i3=30, &i3=0x22ac80



出力結果を元に、このプログラムが実行されていた時のメモリ構造を再現してみます。


先ほど書いたようにint型は4マス分(4byte分)、char型は1マス分(1byte分)の領域を使用します。
また、int型変数ののアドレスを%pで表示させた場合、先頭アドレス(一番若いアドレス)が表示されるため、&i1=0x22ac8cと出た場合は、0x22ac8c, 8d, 8e, 8fの4番地を占有します。
charは1byteなので、このプログラムでは0x22ac85,86,87番地は使用されていません。

ちなみに、上記のプログラムはwindowsにインストールしたcygwin環境での結果です。
今回は0x22ac80~90番地あたりに変数が割り当てられましたが、この番地はコンパイラやOSや実行タイミングが変わると、異なる番地になる可能性もあります。


コンパイラやOS環境によって番地が変わることがある事を確認するために、全く同じプログラムを別のOSであるLinux上でも実行してみました。
以下が実行結果です。

$ ./ptr1
i1=10, &i1=0xbff65160
i2=20, &i2=0xbff65164
c1=a, &c1=0xbff6516f
i3=30, &i3=0xbff65168



同様にメモリ構造を再現してみると、以下のような形です。


これをみると番地はもちろん、メモリ内での変数の並び順すら変わる(場合がある)という事が分かるかと思います。



ポインタ変数を使用する


次は、ポインタ変数を使用したプログラムのサンプルです。

#include <stdio.h>
 
int main()
{
    int  i;
    int *ip;
 
    /* int の変数に値をセット*/
    i = 10;
    printf( "i=%d, &i=%p\n", i, &i );
 
    /* intの変数がある場所を覚える */
    ip = &i;
    printf( "ip=%p, &ip=%p *ip=%d\n", ip, &ip, *ip );
 
    /* ipが覚えた場所の値を書き換える */
    *ip = 5;
 
    /* iを操作していないのにも関わらず */
    /* iの内容が5に変わってしまった    */
    printf( "i=%d, &i=%p\n", i, &i );
 
    return 0;
}



これを実行すると、以下のようになります。

$ gcc -o ptr2 ptr2.c
 
$ ./ptr2
i=10, &i=0x22ac8c
ip=0x22ac8c, &ip=0x22ac88 *ip=10
i=5, &i=0x22ac8c




実行結果を見ると分かるのですが、変数iを変えて無いにも関わらず、値が5に変わってしまいました。
どうして値が変わったのか、プログラムを順に追っていきます。

    int  i;
    int *ip;



ここで新登場の表記が”int *ip”です。
ここでは、ポインタ変数を定義しています。
このポインタ変数の定義ですが、”int *”型の変数”ip”と読んでください。
※言葉で説明する場合は、”いんとあすたがた”の変数(アスタリスクを省略してアスタと言ってます)や、”int型へのポインタ変数”と発音する事が多いです。


ポインタの勉強ではここで勘違いしてしまい後の説明が分からなくなってくる人が非常に多いので、もう一度説明します。
大事な事なので、以下の文章を3回音読して下さい!!(周りに人がいない事を確認した上で)

**重要**
 
ポインタ変数の定義"int *ip;"は、"int *" 型の変数 "ip" という意味です。
 
C言語の入門書によっては "int" 型のポインタ変数 "*ip" と書かれている場合も有りますが、これは間違いです。
 
なぜなら、アスタリスク記号(*)までが変数の型指定なので"int *"型が正解なのです。
 
プログラム中に"int *ip;"と書かれていると、途中にスペースがあるので、つい変数名が"*ip"であると
勘違いしがちですが、変数名は"ip"です。
 
**重要**




ポインタ変数を使用すると、以下のコードで”ある変数の値が記憶されている場所”を記憶できます。

    ip = &i;



これで、ポインタ変数ipにはiのアドレスが代入されました。


ポインタ変数のipの前に、”*”演算子を使用すると、”ポインタ変数が指している場所”に入っている値にアクセス出来ます。この演算子”*”を、間接演算子と呼びます。

説明の順番が前後しますが、サンプルプログラム中の以下のコードでは、間接演算子を使用して”変数ipが指している,変数i”の値を変更しています。

    *ip = 5;




printfで、ipと、ipに対して先ほど説明したアドレス演算子”&”、間接演算子”*”を適用した値を出力しています。ポインタ変数も単なる変数ですから&ipでアドレスが分かります。

    printf( "ip=%p, &ip=%p *ip=%d\n", ip, &ip, *ip );



再度iの値を表示していますが、ip経由でiの値を変更しているので、ここではi=5が出力されます。

    printf( "i=%d, &i=%p\n", i, &i );




プログラムの実行結果を元にプログラムが終了した時のメモリ構造を描くと、以下のようになります。

プログラムの実行結果(再掲)

i=10, &i=0x22ac8c
ip=0x22ac8c, &ip=0x22ac88 *ip=10
i=5, &i=0x22ac8c


メモリに格納された値



ポインタ型の指定では、例で”int *”型となっている事からも分かるように、ポインタ変数が指す対象のデータ型を指定する必要があります。例えば、int型ではなくchar型を指すポインタ変数を作りたい場合は以下のように変数定義を行います。

char *cp;




“*”記号の意味、勘違いしてませんか?

今回の説明では、ポインタ演算子である”&”と、間接演算子”*”を紹介しました。

ここで、”初心者が間違いやすいポイントその2″ がありますので書いておきます。
例えば以下のコードがあった場合…

    int  i = 10;
    int *ip;
 
    ip = &i;
    printf( "%d\n", *ip );
    return 0;
}



変数定義の”int *ip;”で書かれている”*”記号と、printfの引数で使用している”*ip”の”*”演算子(間接演算子)は全く関係が有りません。

ここで、変数名が”ip”ではなく、”*ip”だという勘違いをしていると、printfにある”*ip”が変数名だと勘違いする人が多いです。
(しつこいようですが”int *ip;”の変数宣言は、データ型が”int *”で、変数名は”ip”です)

ですので、printf分で書かれている*ipは、以下の意味であるという事を忘れないようにしてください。

printf( "%d\n", *ip ); の *ipは...
  → 変数"ip"に対して、間接演算子の"*"を使用することで、ポインタ変数が指す先の値を取得している。




説明した事を整理すると、以下の内容になります。

ポインタ変数の型名の一部と、間接演算子で、同じ記号"*"を使用していますが、両者には関係が有りません。
さらに言うと、掛け算の演算子も"*"ですが、これも間接演算子とは関係有りません。



このようにC言語では、同じ記号を状況によって違う意味で使っている場合があります。
紛らわしいので、勘違いしないよう注意して下さい。



以上でメモリの概念と、ポインタ変数の基本的な考え方の説明は終了です。
次回はもう少し色々なサンプルを通して、再度ポインタ変数の使い方を説明します。

cygwinターミナルの文字サイズを素早く変更する

cygwinをインストールすると付いてくるターミナルであるCygwin Terminalですが、ショートカットキーを使用するとフォントサイズを一瞬で変更できます。


Ctrl + “-“で文字サイズを小さく、Ctrl + “+”で大きくなります。



デフォルトでは以下のようなサイズですが…



Ctrl + “+”を5回ほど押すとこれくらい大きくなります。



さらに押し続けると最大はこんな感じ。
プロジェクタで映すときとかに便利そうです。



逆に Ctrl + “-” で最小まで持っていくと、ここまで小さくなります。
どう考えても読めません。


[C言語入門]プリプロセッサ命令を理解する

今までの学習では、gccコマンドを使用してプログラムを作成してきました。
gccを使用すると、いきなり実行ファイル(.exeファイル)が生成されていたのですが、gcc(というかCコンパイラ)は内部的に以下の手順でexeファイルを作っています。

.cファイル
 
	    ↓
	プリプロセス
	    ↓
 
.iファイル
 
	    ↓
	コンパイル
	    ↓
 
.sファイル
 
	    ↓
	アセンブル
	    ↓
 
.oファイル
 
	    ↓
	  リンク
	    ↓
 
.exeファイル



上の表の見方ですが、例えば一番上のところの記述は、”.cファイル”に対して、”プリプロセス”処理を行うと、”.iファイル”が生成されるという事を意味しています。

今まで行っていたような”gcc -o test01.exe test01.c”というコマンドは、コマンド1つ打つだけで”プリプロセス,コンパイル,アセンブル,リンク”の作業を一気に行ってくれています。




それでは、各ステップを順番に行う方法は無いかというと、gccコマンドにコンパイルオプションを指定することで、1ステップづつ処理を進めることも可能です。

それぞれのステップを行うためのコマンドは以下の通りです。

プリプロセスだけ行う
	gcc -E test01.c > test01.i
 
コンパイルだけ行う
	gcc -S test01.i
 
アセンブルだけ行う
	gcc -c test01.s
 
リンクだけ行う
	gcc test01.o






それでは、実際にcygwinで各処理を順に行ってみます。

今回、確認に使用するプログラムは以下のソースです。

#include<stdio.h>
 
int main(void)
{
    printf( "hello world\n" );
}




プリプロセスだけ行う
(.iファイルが生成される)

$ ls
./  ../  test01.c
 
$ gcc -E test01.c > test01.i
 
$ ls
./  ../  test01.c  test01.i




コンパイルだけ行う
(.sファイルが生成される)

$ gcc -S test01.i
 
$ ls
./  ../  test01.c  test01.i  test01.s




アセンブルだけ行う
(.oファイルが生成される)

$ gcc -c test01.s
 
$ ls
./  ../  test01.c  test01.i  test01.o  test01.s




リンクだけ行う
(.exeファイルが生成される)

$ gcc test01.o
 
$ ls
./  ../  a.exe  test01.c  test01.i  test01.o  test01.s
 
$ ./a.exe
hello world



というわけで、確かに1ステップづつ処理を行わせる事が出来ました。
ファイルの中身をのぞいてみると、(意味は分からないかもしれませんが)各ステップでどんな感じの成果物が出来上がっているのか、イメージできるかと思います。






と、ここまでが前置きです。

それでは、本題のプリプロセス命令を使用したプログラムを作成してみます。

#include<stdio.h>
#define GOOD_SCORE 80
 
void print_result( int );
 
int main(void)
{
    print_result( 94 );
    print_result( 50 );
 
    return 0;
}
 
void print_result( int score )
{
    printf( "あなたの点数は%d点です\n", score );
 
    if ( score > GOOD_SCORE ) {
        printf( " 合格です\n" );
    } else {
        printf( " 不合格です[合格まであと%d点でした]\n", GOOD_SCORE - score );
    }
}




これをコンパイル&実行すると、以下の結果になります。

$ gcc test01.c
 
$ ./a.exe
あなたの点数は94点です
 合格です
あなたの点数は50点です
 不合格です[合格まであと30点でした]





今回のプログラムでは、2行目の”#define”がプリプロセッサ命令です。
(プリプロセッサ命令は、プリプロセッサディレクティブと呼ぶ場合もあります)

define命令は、指定された文字の置換を行ってくれます。

#define 置換前の値 置換後の値



置換がどのように行われているかを確認するために、”gcc -E”でプリプロセスだけ行ってみます。

$ gcc -E test01.c > test01.i
 
$ cat test01.i
 
# 1 "test01.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test01.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 29 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/_ansi.h" 1 3 4
 
... 途中1100行ほど省略 ...
 
        __c = __c2;
      else
        ungetc(__c2, __p);
      }
    return __c;
  }
# 683 "/usr/include/stdio.h" 3 4
 
# 2 "test01.c" 2
 
 
void print_result( int );
 
int main(void)
{
 print_result( 94 );
 print_result( 50 );
 
 return 0;
}
 
 
void print_result( int score )
{
 printf( "あなたの点数は%d点です\n", score );
 
 if ( score > 80 ) {
  printf( " 合格です\n" );
 } else {
  printf( " 不合格です[合格まであと%d点でした]\n", 80 - score );
 }
}



作成されたtest01.iを見ると1200行以上の膨大なファイルになってしまいました。
この内、最初の1100行ぐらいはよくわからないプログラムが出力されていますが、いったん無視します。
注目してもらいたいのは最後の部分で、print_result()関数を見ると、先ほどtest01.cファイルででGOOD_SCOREと書かれていた個所が80に置換されていることが確認できます。

このように、#defineはコンパイルの前に置換処理を行ってしまいます。


パッと見た感じだとGOOD_SCOREという変数を作っているように見えますが、defineの違うところは”コンパイルの前”にこの作業を行っている点です。


プリプロセッサ命令は#defineだけではなく、他にも以下のようなものがあります。

#include
#line
#pragma
 
#define
#undef
 
#error
 
#if
#ifdef
#ifndef
#else
#elif
#endif



今までプログラムの1行目に”おまじない”として書いていた#includeですが、これもプリプロセッサ命令です。#includeの意味は、”その後に書かれたファイルの中身で#includeのある行を置き換える。”という意味です。

先ほど1100行ほど良く分からないソースがくっついていましたが、これはstdio.hというファイルの中身になります。
(stdio.hはコンパイラをインストールしたフォルダに有り、cygwinだと/usr/includeの下に存在します)




プリプロセス命令の位置づけ(再確認)


というわけで、プリプロセス命令というのは、厳密にいうとC言語ではありません。
(“C言語ではない”と書くと、プリプロセッサは不要なのか?と思われるかもしれませんが、実際にはほぼ全てのコンパイラで利用されています)

最初に書いたコンパイル処理の流れでいうと、コンパイラが解釈するのがC言語で、プリプロセッサ命令はC言語のソースを分かりやすく書くための記法です。

DDDデバッガで、ファイルをオープン時にNo Sourceのダイアログが出る時は

gccでコンパイルしたプログラムをデバッガであるdddでデバッグしようとした時、以下のエラーメッセージが表示されることがあります。

コンパイル&デバッガ起動のコマンド

gcc -o test01 test01.c
ddd ./test01



エラーメッセージ

DDD:No Source
/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/gcc/libgcc2.c:No such file or directory



(このエラーは、windows上にインストールしたcygwinで確認しています)



作成された実行ファイルにデバッグ情報が無いと、上記のような状況になります。
コンパイル時に-gオプションをつけて、コンパイルしてください。

gcc -g -o test01 test01.c
ddd ./test01



また、gccはデフォルトでは実行ファイル生成時に最適化を行うので、デバッグで動作を確認する場合は最適化をOFFにしたほうが分かりやすい場合もあります。
最適化を行わせないためには、-O0のオプションをつけます。
-O0は、画面で見ると分かり辛いですが、”ハイフン、アルファベット大文字のオー、ゼロ”です。

gcc -O0 -g -o test01 test01.c