[알아봅시다] 스마트폰 백신

'백신 앱' 2개 깔면 악성코드 완벽차단

1년사이 1200여종으로 늘어
안연구소ㆍ하우리 무료 제공
탈옥 아이폰은 감염에 취약


2009년 11월 80만 명에 그쳤던 스마트폰 가입자 수가 지난 3월 1000만 명을 돌파했습니다. 이는 전체 휴대전화 가입자의 20%에 달하는 것으로 업계에서는 올해 안에 국내 스마트폰 이용자수가 2000만 명을 넘어설 것으로 예상하고 있습니다.

스마트폰 이용자수가 늘어나면서 이에 대한 보안 위협도 점차 증가하고 있는데요. 스마트폰 백신 설치 및 올바른 이용에 대한 숙지가 중요합니다.

스마트폰 악성코드는 PC에서와 마찬가지로 스마트폰에 악영향을 끼칠 수 있는 모든 소프트웨어를 총칭합니다. 스마트폰 악성코드는 △스마트폰 자체 또는 스마트폰 애플리케이션 마켓의 보안 취약점 △정상적인 소프트웨어인 것처럼 위장하는 사회공학적 기법 △외부저장장치 등 매우 다양한 경로를 통해 감염될 수 있습니다.

또 악성코드에 감염된 스마트폰은 이용자 모르게 개인정보유출, 과금 유발, 단말기 이용 제한 등의 악성행위를 수행합니다.

최근 중국 보안전문업체인 넷퀸(NetQin)의 발표에 따르면, 2010년 3월 500개였던 스마트폰 악성코드는 지난 3월 1200여종으로 증가했습니다. 이전에는 심비안 운영체제(OS) 탑재 스마트폰 대상의 악성코드가 발생했지만, 최근에는 안드로이드 OS 탑재 스마트폰의 사용이 증가하면서 이를 대상으로 하는 악성코드가 증가하고 있습니다.
스마트폰이 악성코드에 감염되지 않도록 사전에 예방하고, 악성코드에 감염된 스마트폰을 치료하기 위해 스마트폰 이용자가 취할 수 있는 가장 효과적인 조치는 백신 애플리케이션을 자신의 스마트폰에 설치ㆍ실행시키는 것입니다.

최근 국내 스마트폰의 보급률이 높아지고, 해외 스마트폰 악성코드가 증가하면서 국내에서도 다양한 스마트폰 백신 애플리케이션들이 등장하고 있습니다. 스마트폰 이용자는 국내 백신업체 및 이동통신사의 애플리케이션 마켓 등을 통해 백신 애플리케이션을 검색해 찾을 수 있으며, 쉽게 설치할 수 있습니다.

지난 5월 기준으로 스마트폰 백신을 출시한 국내 업체로는 안철수연구소, 잉카인터넷, 이스트소프트, 하우리 등이 있습니다. 이들 업체는 기업 및 개인고객을 대상으로 백신 애플리케이션을 스마트폰에 탑재해 출시하거나 제조사 홈페이지 또는 별도의 애플리케이션 마켓을 통해 이용자가 다운로드받을 수 있도록 하고 있습니다.

백신 애플리케이션을 다운로드받을 수 있는 마켓은 구글의 안드로이드 마켓과 T-스토어, olleh 마켓, OZ 스토어 등 이동통신사에서 운영하는 애플리케이션 마켓이 있습니다. SKT는 T-스토어 마켓을 통해 △바이러스스캔 모바일(맥아피) △알약 안드로이드(이스트소프트) △바이로봇 모바일(하우리) 백신을 제공하며, KT는 올레마켓을 통해 △KT모바일 보안(F-Secure)△알약 안드로이드(이스트소프트) 백신을 제공하고, LG 유플러스는 OZ스토어를 통해 알약 안드로이드(이스트소프트)를 제공하고 있습니다.

이외에 스마트폰 제품 별 혹은 통신사별로 어떤 모바일 백신을 사용할 수 있는지 등 자세한 정보를 얻고자 할 때는 한국인터넷진흥원(KISA) 모바일홈페이지(http://m.kisa.or.kr)의 `이동통신사별 스마트폰 출시 및 백신 현황'을 통해 확인할 수 있습니다.
스마트폰 백신을 찾았어도, 이에 대한 정보 부족으로 이를 잘 사용하지 못하는 경우도 있습니다. 흔히 스마트폰 백신을 설치하면, 스마트폰 기능이 느려지지 않을까 우려하지만 스마트폰 전용 백신은 백신 애플리케이션 실행 시에만 가동돼, 메모리에 전혀 부담을 주지 않습니다.

그리고 스마트폰 백신을 다운로드받거나 업데이트시 과금을 우려하는 경우가 있습니다. 현재 국내 스마트폰 이용자 대상 백신들은 모두 무료로 제공되고 있습니다. 다만 백신 설치 후, 새로운 악성코드 진단을 위한 데이터 업데이트시 통신을 위해 네트워크를 이용하긴 하지만 그 양이 미비해 부담이 될 정도는 아닙니다.

이밖에 아이폰용 백신은 탈옥한 아이폰의 경우 외부로부터 해킹 및 악성코드 감염에 취약할 수 있어, 국내 백신사에서 탈옥한 아이폰을 대상으로 하는 백신을 개발하고 있습니다. 또 삼성 바다 OS용 백신도 현재 국내 스마트폰 백신업체에서 개발하고 있으며 연내에 보급할 예정입니다.

무엇보다 스마트폰 백신 설치시 주의해야할 점은, 이용자의 안전을 위해 스마트폰 백신을 두 개 이상 설치해 번갈아 가면서 이용하는 게 좋다는 점입니다. 더 중요한 점은 설치된 백신들을 주기적으로 실행해 악성코드를 진단해야한다는 것으로, 결국 백신을 설치했다고 하더라도 사용자의 꾸준한 확인과 노력이 필요합니다.

김지선기자 dubs45@

자료제공=한국인터넷진흥원
▶김지선기자의 블로그 : http://blog.dt.co.kr/blog/?mb_id=dubsrep

[저작권자 ⓒ디지털타임스 무단 전재-재배포 금지]
[알아봅시다] 망중립성 도화선 `모바일 인터넷전화`

"망이용료 내라"… "소비자 선택 부가서비스일뿐"
이통사 - mVoIP사업자 옥신각신

'새 접속료 필요… 소비자 권리'
전문가들도 각각 의견 엇갈려


SK텔레콤이 28일부터 LTE 스마트폰을 출시하고 본격적인 4G 시대를 선언하고 나섰습니다. 기존 3G 보다는 평균 5배 이상 빠른 속도가 구현됨으로써, 과거 3G에서 구현하기 어려웠던 온라인 게임, 동영상 서비스가 보다 수월해질 전망입니다.

그런데 이날 SKT가 발표한 LTE 스마트폰 요금제중에 소비자들의 눈길을 끄는 대목이 하나 있었습니다. 바로 mVoIP(모바일인터넷전화) 부분입니다.

SKT는 LTE 스마트폰 요금제중에 월정액 5만2000원을 내는 LTE52 이상의 가입자에 한해 mVoIP서비스를 제공하겠다고 발표했습니다. 요금제별로 180MB∼750MB 용량 한도라는 단서가 달기는 했지만, 초고속 모바일이 구현되는 LTE에서도 본격적으로 모바일 음성전화 서비스를 열어 놓았다는 점에서 상징성이 큽니다. 특히 이날 SKT 고위임원은 기술적인 완성도만 높아지면 IP 기반으로 설계된 LTE에서 VoLTE 서비스에 나서겠다는 입장도 전했습니다. 과거 유선사업자들이 인터넷전화 서비스로 전환한데 이어, 그동안 서킷 기반의 음성전화 서비스를 제공해 온 이동통신사들도 패킷기반의 mVoIP 서비스로 옷을 갈아입게 될 날이 멀지 않은 것입니다.

지난 2009년 11월, 애플 아이폰이 국내에 보급되기 시작하면서 모바일 음성통화 서비스인 mVoIP 서비스가 통신시장의 폭풍의 핵으로 부상하고 있습니다. 특히 국내 스마트폰 가입자가 연내 2500만을 바라보면서, mVoIP 가입자들은 가히 폭발적으로 늘어나고 있습니다.

국내에서는 다음, 스카이프, 등이 mVoIP 서비스를 경쟁적으로 내 놓으면서 급상승을 기록하고 있습니다. 스마트폰 가입자가 급증하면서 전통적인 음성통신 사업자이던 SKT, KT, LG유플러스 등 통신 3사 모두 일정금액 이상을 사용하면 정액제 가입자에 한해 mVoIP 서비스를 제공하고 있습니다.

스마트폰 가입자 확대와 함께 시장이 급성장하고 있는 mVoIP는 음성데이터를 데이터망인 IP망을 통해 이용하는 통신서비스입니다. 일반 인터넷 회선을 통해 음성신호를 실어 나를 수 있기 때문에, IP 네트워크가 구축된 유무선 통신망에서 자유롭게 음성통화가 가능하고, 특히 최소의 데이터 트래픽을 유발하기 때문에 통신비 유발 효과도 최소화할 수 있습니다.

또한 인터넷, 데이터 등과 함께 음성전화 서비스를 구현할 수 있는 편리성 때문에, 과거 100여년 이상 형성됐던 패킷기반의 음성전화 시장을 대체할 강력한 경쟁자로 부상하고 있는 것입니다.

문제는 이처럼 인터넷전화가 기존 음성통신시장을 대체할 경쟁자로 부상하면서, 기존 통신업체들에 치명타를 주고 있다는 점입니다. 궁여지책으로 이동통신 3사도 월정액 5만5000원 이상의 정액가입자에 한해 mVoIP 서비스를 무료 지원하고 있지만, mVoIP 가입자들의 자양분만 높여주고 있는 실정입니다.

통신업계는 가격경쟁력과 사용의 편리성을 앞세운 mVoIP 서비스가 확대되면서 기존 음성시장을 빠르게 잠식해 들어오고 있다고 말합니다. 스마트폰 보급과 함께 초기 완만하게 상승하던 이통사업자의 가입자당매출액(ARPU)이 최근 정점을 찍은 이후, 급격히 떨어지고 있는 것도 이같은 맥락입니다. 기존 기간통신사업자들 입장에서 보면, 아무런 네트워크 설비도 가지지 않은 mVoIP 사업자들이 자신들의 네트워크에 무임승차해 비즈니스를 하고 있는 점에 분노까지 표출하고 있습니다. 특히 현재 자신들의 가장 큰 수익원인 음성통화 매출을 잠식해 들어오고 있다는 점에서 특단의 대책마련을 호소하고 있습니다.

따라서 이들 이동통신 사업자들은 현재 아무런 규제를 받지 않고 있는 mVoIP를 이통사업자와 마찬가지로 기간통신 역무로 구분하고, 각각 기간통신사업자나 별정통신사업자로 등록시켜야 한다는 입장입니다. 지금처럼 기간통신사업자와 아무런 계약 없이 서비스를 이용하는 사업자는 불법으로 간주하고 별도의 망 임대료를 지불하게 해야 한다는 것입니다.

현재 유선상에서 인터넷전화에 적용하고 있는 접속료 및 규제를 모바일 인터넷 사업자에도 같이 동등하게 부여해야 한다는 게 기본 요지입니다.

그러나 mVoIP 사업자 입장에서는 얘기가 달라집니다. 이 사업자들은 mVoIP가 소비자가 선택한 부가서비스 개념이기 때문에 일반 인터넷 트래픽을 유발하는 콘텐츠 사업자와 같이 별도의 요금을 적용해서는 안된다는 것입니다.

플랫폼을 가진 이통사와 이 망을 통해 서비스를 제공하는 mVoIP 사업자간 간극은 좀처럼 좁혀지지 않고 있는 실정입니다. 보다못한 정부가 나서서 mVoIP 망중립성 문제를 해소하기 위한 전담기구도 만들고 논의를 전개하고 있지만 만족스런 해법을 만들까지는 시간이 걸릴 듯 합니다.

이통사업자와 mVoIP 사업자간 갈등은 이미 미국, 유럽 등지에서도 심각한 망중립성 문제로 비화되고 있습니다. 국내와 마찬가지로 대부분의 글로벌 통신사들이 제한적인 범위에서 mVoIP를 허용하고 있지만, 양측 모두 불만족스런 상황은 여전히 계속되고 있습니다.

대부분의 전문가들은 이동통신의 경우, 유선과 달리 한정된 주파수 자원을 사용하는 만큼, 과거 유선에서와는 다른 새로운 형태의 접속료 체계나 규제장치 등이 필요하다고 보고 있습니다. 그러나 또 한편으로 소비자가 네트워크를 통해 자유롭게 서비스를 이용할 수 있는 ?자유로운 권리?도 강조되고 있습니다.

최경섭기자 kschoi@

[저작권자 ⓒ디지털타임스 무단 전재-재배포 금지]
[알아봅시다] 온라인 광고의 진화

홍대앞서 스마트폰으로 '맛집' 검색하면
5미터 앞 식당 이름 줄줄이 뜨는 시대

IP 확인해 해당 지역 광고 노출기법
디엔에이소프트 '로컬클릭' 대표적
시장 거대화…올 1조9000억원 전망


지난해부터 전 세계 인터넷 업계를 강타한 `모바일`과 `소셜' 열풍은 온라인 광고에도 변화의 바람을 불러오고 있습니다. 천편일률적인 키워드 검색 광고와 디스플레이형 배너 광고에서 탈피, 모바일과 소셜로 인한 소비자의 라이프 스타일 변화와 그에 따른 광고주의 요구에 맞게 다양하게 진화하고 있는데요. 최근 관심을 모으고 있는 콘텐츠 타겟팅 광고와 지역(로컬) 광고에 대해 알아보겠습니다.

◇콘텐츠와 광고를 적절히 매칭한 새로운 광고 비즈니스 모델=우선 콘텐츠 타겟팅 광고(Content targeting AD)는 뉴스나 블로그의 포스팅과 같은 콘텐츠에 관련된 광고를 타겟팅한다는 뜻입니다. 광고와 해당 콘텐츠를 적절하게 매칭하는 것이지요. 구글의 `애드센스'와 디엔에이소프트의 `리얼센스'가 대표적인데요. 두 서비스 모두 자동 키워드 매칭을 통해 해당 콘텐츠를 분석, 가장 적합한 광고를 노출한 후 광고 수익을 콘텐츠 생산자와 나누는 구조입니다.

콘텐츠 타겟팅 광고의 과금 방식은 CPC(Cost per Click)입니다. 이용자가 광고를 한번 클릭할 때마다 요금이 부과되고, 노출만 됐을 경우에는 비용을 지불하지 않습니다. 구글의 애드센스는 광고위치나 클릭률에 따라 단가 차이가 있는 반면, 디엔에이소프트의 리얼센스는 고정된 단가를 적용하고 있습니다. 다만 리얼센스는 인센티브제도를 운용해 실적이 좋은 회원에게는 더 높은 단가를 적용하고 있습니다.
이같은 콘텐츠 타겟팅 광고는 콘텐츠 생산자와 광고주의 요구를 모두 만족시키며 새로운 광고 비즈니스 모델로 자리잡아가고 있다는 게 관련업계의 설명입니다. 1인 매체를 운영하는 블로거나 중소 사이트 운영자의 입장에서는 수익을 얻을 수 있으며, 광고주 입장에서도 부정 클릭을 차단하면서 타겟 마케팅을 할 수 있기 때문에 효과적입니다.

◇특정 지역의 특정 소비자를 대상으로 한 마이크로 마케팅= 로컬 광고는 인터넷을 기반으로 모든 지역에 광고를 노출시키는 것이 아니라, 특정 지역에만 광고를 하고 싶은 광고주와 특정 지역의 정보만을 원하는 소비자를 위한 맞춤 광고입니다.

광고주 입장에서는 `인터넷에 접속한 소비자가 지금 어디 있느냐'를 타겟팅 하고, 온라인 이용자(또는 소비자) 입장에서는 `해당 제품 혹은 서비스를 구매할 수 있는 지점이 내 주변에 있는가'에 대한 정보에 기반해 광고가 노출되도록 하는 것입니다. 기존 온라인 광고가 매스 마케팅이라면, 로컬 광고는 마이크로 마케팅인 셈이지요.

업계에서는 전통적인 오프라인 지역 기반의 광고였던 전단지, 현수막 등이 온라인으로 대거 이동할 것으로 보고 있습니다.

[알아봅시다] OTT(Over-The-Top)

인터넷 연결하면 방송ㆍ영화 '와르르'

넷플릭스 ㆍ 훌루, 미국 대표 사업자
유료방송보다 이용료 저렴 큰 인기
법적지위ㆍ망중립성 문제 부각될 듯


지금 미국에서는 대표적인 온라인 동영상 스트리밍 서비스인 훌루(Hulu)를 인수하기 위해 구글, 애플, 아마존, 야후, 디렉TV 등 기라성 같은 IT 기업들이 경쟁을 벌이고 있습니다. 또 다른 온라인 동영상 업체인 넷플릭스(Netflix)는 지난 6월말 기준 가입자수 2459만명을 돌파했다고 밝혔습니다. 이는 미국 최대 유료방송 사업자인 컴캐스트의 가입자수 2270만명(2011년3월 기준)을 넘어선 것입니다.

국내에서 케이블 방송 프로그램을 온라인으로 제공하는 티빙(Tving)이 출시한지 1년만에 가입자 200만명을 돌파했습니다. 훌루, 넷플릭스, 티빙과 같은 온라인 동영상 서비스를 흔히 OTT(Over-The-Top)라 부릅니다. OTT 서비스가 전통적인 방송매체들을 위협하며 스마트TV 시대의 새로운 주역으로 주목받고 있습니다.

◇OTT란?=OTT란 Over-The-Top의 줄임말입니다. 여기서 `Top'이란 셋톱박스(Set-top Box)를 지칭하는데, 단어 자체만 놓고 보면 OTT 서비스는 셋톱박스를 통해 이루어지는 그 어떤 서비스라고 할 수 있습니다. 셋톱박스는 케이블 또는 위성방송을 수신하기 위해 필요한 장치입니다. 하지만 근래에 OTT에서 Top은 인터넷에 연결되고 동영상을 재생할 수 있는 전용 단말기라는 의미로 사용됩니다. 따라서 Top의 의미는 셋톱박스 뿐 아니라 PC, 스마트폰, 태블릿PC, 게임기 등을 포괄하고 있다고 보는 것이 일반적입니다. 다시 정의하자면 OTT란 개방된 범용 인터넷을 통해 방송프로그램, 영화 등의 동영상을 전달하는 인터넷 동영상 서비스를 말합니다.

OTT는 유료방송에 비해 저렴하고 인터넷에 연결돼 있으면 언제 어디서든 간편하게 TV프로그램 등 동영상을 시청할 수 있다는 점에서 가입자 기반을 급속히 확대하고 있습니다. 미국 온라인 시장 조사 기관인 컴스코어에 따르면 2010년 12월 한달 동안 미국인들이 시청한 온라인 비디오는 8860만개로 전년 동월 대비 32% 증가했습니다.
OTT서비스가 급성장하면서 미국 유료방송과 지상파방송 등 전통 방송 매체들이 위협을 느끼며 OTT 시장에 뛰어들고 있습니다. 미국 지상파방송사들이 설립한 훌루닷컴과 케이블방송사들이 지향하는 `TV Everywhere'가 바로 그것입니다.

◇주요 OTT 사업자=미국의 대표적인 OTT 사업자로 넷플릭스를 들 수 있습니다. 넷플릭스는 1997년 DVD 오프라인 대여 서비스로 시작해 2007년 1월 온라인을 통해 DVD 콘텐츠를 제공하는 `와치 인스턴틀리(Watch Instantly)' 서비스를 시작했습니다. 이 서비스는 초기에는 크게 성공적이지 못했으나 2008년 중반부터 서비스 가능 단말기를 확대하고 2010년 7월에는 월7.99 달러에 무제한 스트리밍 서비스를 제공하면서 가입자가 폭발적으로 증가하기 시작했습니다. 그 결과 넷플릭스는 2011년 6월말 현재 2459만명의 가입자를 확보했습니다. 넷플릭스는 현재 2만3500편의 TV 에피소드와 8250편의 영화를 제공하고 있습니다.

훌루는 2008년 3월 미국 NBC유니버설, 뉴스코프가 합작해 만든 동영상 스트리밍 서비스입니다. 2009년 4월에는 ABC디즈니가 여기에 합류했습니다. 초기에는 각 방송사의 인기 프로그램을 무료로 제공하고, 소비자는 광고를 시청하도록 했습니다. 이어 2010년 6월에 월 9.99달러(현재 7.99달러로 인하)에 프리미엄 콘텐츠를 제공하는 유료 가입 서비스인 `훌루 플러스'를 출시했습니다. 훌루는 대주주로 참여하는 방송사 이외에도 케이블채널, 영화사 등과 제휴해 2만9700편의 TV에피소드와 1700편의 영화를 보유하고 있습니다. 훌루플러스는 PC뿐 아니라 블루레이, 스마트TV, 게임콘솔 등 다양한 단말기에서도 이용할 수 있습니다.

훌루에 참여를 거부한 CBS는 2008년 5월 IT전문 뉴스를 제공하는 씨넷을 인수하고 씨넷이 운영하던 TV닷컴(tv.com)을 개편해 2008년 12월에 자사의 에피소드를 무료로 제공하는 스트리밍 서비스를 시작했습니다. TV닷컴은 훌루에 비해 열세인 콘텐츠를 보강하기 위해 케이블방송사들의 온라인 동영상 서비스인 `TV에브리웨어'에 공급 계약을 맺기도 했습니다.

스마트폰 운영체제(OS)를 양분하고 있는 애플과 구글도 스마트폰의 영향력을 발판으로 OTT에 진출하고 있습니다. 그러나 지금까지는 당초 예상만큼의 파급 효과는 나타나지 않고 있습니다.
애플은 2007년에 셋톱박스 기반의 애플TV를 출시했으나 비싼 단말기 가격(299달러), 불편한 이용 방법으로 호응을 얻지 못했습니다. 이어 2010년 9월 애플은 이러한 문제점을 극복한 새로운 애플TV를 출시했습니다. 애플TV는 오로지 스트리밍 방식으로만 제공되고 있으며, ABC와 폭스의 콘텐츠를 편당 99달러에, VOD는 편당 4.99달러에 서비스 중입니다.

구글TV는 안드로이드 OS, 크롬 브라우저, 유튜브 등과 같은 구글 프로그램을 플랫폼으로 해, TV와 인터넷을 하나의 단말기에서 이용할 수 있도록 하는 OTT 서비스입니다. 구글은 콘텐츠를 강화하기 위해 지상파방송사들과 협상을 벌였으나 지상파방송사의 대부분이 훌루 출자자라는 점 등으로 인해 협조를 얻지 못했습니다. 로지텍은 구글TV를 이용할 수 있는 셋톱박스를 개발했으며 소니가 셋톱박스가 내장된 구글TV를 출시한 바 있습니다. 구글은 현재까지 큰 성과를 내지 못하고 있으나 미국에 이어 유럽에 진출하고 올해 말에는 구글TV2 출시를 계획하는 등 구글TV에 대한 변함없는 애정을 보이고 있습니다.

◇법적 지위ㆍ망중립성 등 이슈로 부각할 듯=OTT는 초기에 광고 기반의 무료 서비스로 진화했으나 점점 유료 서비스로 진화하고 있습니다. 광고 기반의 비즈니스 모델만으로는 매출 확대에 한계가 있기 때문이지요.

OTT 서비스가 확대됨에 따라 프리미엄 콘텐츠를 확보하기 위한 사업자간 경쟁이 치열해질 전망입니다. 또한 인터넷뿐 아니라 게임콘솔, TV, 태블릿PC 등 지원 단말기도 다양해질 것으로 보입니다.

OTT 서비스가 확대되면서 이를 견제하는 움직임도 본격화되고 있습니다. 아직 OTT 서비스는 여타 인터넷 서비스와 마찬가지로 거의 규제를 받지 않는 `부가통신 서비스'로 규정돼 있습니다. 하지만 OTT를 지원하는 단말기가 확대되고 있고, 제공되는 콘텐츠도 기존 지상파 및 유료방송 콘텐츠와 동일하다는 점에서 법적 지위를 재검토해야 한다는 주장이 일고 있습니다. 또, OTT는 대규모 트래픽을 발생시키고 있다는 점에서 망중립성 이슈에서도 자유롭지 못한 상태입니다.

강희종기자 mindle@

<표>OTT 서비스 현황

[저작권자 ⓒ디지털타임스 무단 전재-재배포 금지]
[알아봅시다] 클라우드 컴퓨팅 미래

서버ㆍSW 부담 훌훌… IT서비스 신주류 '각광'

사용료 내면 언제든 사용 가능
보안ㆍ안정성 불안 등 해소해야


2008년 개봉된 할리우드 영화 `이글아이(Eagle Eye)'를 보면 국민 개개인의 정보와 도로, 교통 등 국가 기반 자원에 대한 모든 정보를 담고 있는 시스템이 등장합니다. 이 시스템은 주인공들의 일거수 일투족을 감시하며 조종하게 됩니다. 결국 이 시스템에 엄청난 음모가 담겨 있다는 사실이 밝혀지면서 영화 속 주인공들은 여기에 맞서 싸우게 됩니다.

영화에서 볼 수 있듯이 모든 데이터를 하나의 시스템에 저장해 놓은 것이 이 세계를 파괴할 수 있는 단초를 제공하게 되는데, 이는 단순한 데이터 손실이나 왜곡에 대한 위험뿐만 아니라 범죄의 수단으로 악용될 수 있음을 단적으로 보여준 사례라 할 수 있습니다.

이런 가운데 클라우드 컴퓨팅이 `IT업계의 혁명'이라는 평가를 받고 있는 가운데 클라우드 컴퓨팅이 빠른 속도로 고도화된다면 머지 않은 미래에 정보 보안을 걱정하게 될 날도 도래할지 모를 일입니다.

◇클라우드 컴퓨팅의 미래는=현재 클라우드 컴퓨팅 서비스는 일부 기업들이 초기 시장을 형성하기 시작한 단계로, IT업계의 주류로 대두될 지 아니면 하나의 유행처럼 사라져 버릴지 누구도 단정적으로 전망하기 어려운 상황입니다.

그러나 대부분 전문가들은 클라우드 컴퓨팅 서비스의 밝은 미래를 예측하고 있는데요. 이는 초기 투자비용이 많이 들어가는 IT기기나 서버, 소프트웨어에 대한 기업들의 설치 부담을 덜면서 마치 전기처럼 인터넷을 통해 사용한 만큼의 비용을 지불하는 클라우드 컴퓨팅을 새로운 비즈니스 모델로 받아들이고 있기 때문입니다.

미국 시장조사 기관인 IDC에 따르면 클라우드 컴퓨팅 서비스에 투입되는 전 세계 IT지출 규모는 2008년 160억달러에서 2012년에는 420억달러로 크게 늘어날 것으로 예상하고 있으며 전체 IT분야 지출에서 클라우드 컴퓨팅이 차지하는 비중은 같은 기간 4%에서 9%로 2배 이상 늘어날 것으로 전망되고 있습니다.
물론 클라우드 컴퓨팅이 IT업계의 마케팅 수단에 지나지 않을 것이라며 폄하하고 보안이나 안정성 측면에서 치명적인 결함을 지닌 기술이라는 지적도 있습니다.

하지만 제2의 디지털 혁명을 일으킬 클라우드 컴퓨팅을 향한 발걸음은 이미 시작되고 있습니다. 다만 확실한 것은 클라우드 컴퓨팅은 좀 더 완벽한 기술로 성장하고, 나아가 IT분야의 새로운 주류로 자리잡을 것이라는 점입니다.

◇클라우드 컴퓨팅 서비스 발전조건=클라우드 컴퓨팅 서비스가 발전하기 위해서는 수요기업이 원하는 것을 파악해야 합니다. 또 이를 도입하는 기업들의 세밀한 요구까지 수용할 수 있는 능력을 키워야 합니다.

이를 위해선 클라우드 컴퓨팅 서비스 제공업체는 고객의 요구사항을 수용할 수 있는 충분한 기술력을 보유하고 높은 브랜드 인지도를 유지해야 하는데요. 클라우드 서비스 특성상 갑작스런 서비스 중단이나 사고로 인한 정보분실 등은 고객에게 큰 손해를 안길 수 있습니다. 서비스가 끊기는 일 없이 안정성을 보장할 수 있어야 높은 인지도를 유지할 수 있다는 얘기입니다. 이러한 인지도가 기반이 돼야 기업들이 태동하고 있는 클라우드 컴퓨팅 서비스를 수용하게 됩니다.

아울러 규모의 경제를 수요 업체에 제공해야 합니다. 클라우드 컴퓨팅 서비스 업체는 반드시 막대한 물리적인 IT 인프라를 구축해야 하고, IT 서비스에 대한 경험과 함께 고객이 원하는 클라우드 컴퓨팅 서비스를 모두 제공할 수 있는 대규모 자원을 확보하고 있어야 한다는 의미입니다.

앞으로 클라우드 컴퓨팅은 IT자원의 효율성을 극대화하면서 사용자에게 얼마나 안정적인 서비스를 제공하느냐가 관건으로 남아 있는데요. 사용자들이 요청하는 서비스를 최적화된 자원 배분으로 빠르게 제공할 수 있는 고도화된 기술력이 필요한 것입니다.

특히 기업 시장은 플랫폼의 유연성과 안정성을 동시에 만족시킬 수 있는 기술력이 가장 우선이라 할 수 있는데요. 이와 함께 미래에는 기업 고객확보가 중요해질 전망입니다. 현재는 개인 고객을 중심으로 클라우드 서비스가 제공되지만, 점차 기업 고객을 대상으로 확대하고 있는 양상이기 때문입니다.

결국 표준을 정하고 이를 홍보하는 기업이 `기업용 클라우드 컴퓨팅 서비스' 시장을 장악할 가능성이 높습니다.

◇기업 IT부문에 주는 변화=클라우드 컴퓨팅을 도입하는 기업은 여러 가지 변화를 겪게 될 것입니다. 먼저 IT자원을 직접 소유하지 않기 때문에 초기 투자비용이 많이 들지 않을 것입니다. 또 매월 사용량 만큼 전기요금을 내듯 클라우드 컴퓨팅 역시 사용한 만큼 비용을 지불하는 구조가 바뀌게 될 것입니다.
뿐만 아니라 온라인 서비스 종류도 확대될 전망인데요. 소프트웨어를 서비스로 제공하는 현재와 같은 패러다임에서 확대돼 데이터 관리까지도 원격으로 서비스 받게 될 것입니다. 이를 통해 기존 IT부서 인력이 축소되면서 기업 내 조직에도 적지 않은 변화가 있을 것으로 예상됩니다.

구체적으로 보면 클라우드 컴퓨팅 도입은 IT재무관리 구조와 예산 및 투자방식, IT인프라 구매 및 소유, 가상화 기반의 하드웨어 등에 변화를 가져다 줄 것입니다. 또한 클라우드 컴퓨팅의 핵심인 소프트웨어의 서비스화(Saas)를 가속시킬 것이며, 데이터와 정보까지도 서비스화해 인터넷 데이터 센터들이 단순히 IT 인프라를 지원하는 형태에서 벗어나 데이터를 최적화하고 안정된 정보시스템 서비스를 제공하는 방향으로 변모하는 등 서비스로서의 데이터센터(Daas)로 역할을 옮겨가게 될 것입니다.

이밖에 기업의 최고 정보관리책임자(CIO) 및 전산 조직의 업무와 역할에도 변화를 줘 회사의 전산 및 정보 관련 업무는 부서의 요구에 맞는 최적의 클라우드 컴퓨팅 서비스를 발굴하고 이를 신속하게 도입하는 쪽으로 초점이 맞춰지는 등 IT조직 및 업무에 변화의 바람이 불 것으로 보입니다.

◇해결해야 할 과제=서비스 제공 사업자들을 떠나 클라우드 컴퓨팅 서비스가 본격적으로 성장하기 위해서는 몇 가지 해결해야 할 문제점들이 있습니다. 첫째는 서비스 안정성이고 둘째는 데이터 센터에 저장된 정보의 신뢰도, 셋째는 표준화 등을 들 수 있는데요.

특히 표준화는 매우 중요한 문제로, 대부분 클라우드 컴퓨팅 서비스 회사들은 자체 플랫폼으로 서비스를 제공하고 있으나, 향후 이 부분은 큰 쟁점으로 떠오를 것으로 예상됩니다. 따라서 향후 클라우드 컴퓨팅 서비스가 확대되고 재생산되기 위해서는 표준화가 반드시 이뤄져야 할 것입니다.

대전=이준기기자 bongchu@

자료제공=한국전자통신연구원(ETRI)

[저작권자 ⓒ디지털타임스 무단 전재-재배포 금지]
[알아봅시다] 데스크톱 가상화

사무실 책상서 PC본체 사라진다

중앙 데이터센터에 서버 구축
입출력 장치만으로 업무 처리
공간 확보ㆍ전력량 감소 큰효과


데스크톱 가상화는 `사무실 책상에서 PC본체가 사라지는 것'이라고 요약할 수 있습니다. 가상머신(VM)을 생성해 사용자가 씬 클라이언트 혹은 제로 클라이언트를 통해 VM을 PC로 이용하게 하는 것으로 사용자는 씬 클라이언트 등이 설치된 PC와 같은 단말에서 작업을 하고 실제 컴퓨팅 환경은 데이터 센터에 구축된 서버에서 운용되는 방식입니다. 따라서 사용자와 서버 사이에는 키보드, 마우스와 같은 입력사항만 네트워크를 통해 전송되며 서버는 사용자가 요구한 데이터를 화면에 뿌려주게 됩니다.

데스크톱 가상화는 사용자가 추가돼도 서버 가상화를 통해 유연하게 자원을 할당할 수 있고, 사용자의 데이터가 스토리지 가상화를 통해 데이터센터에 보관돼 보안과 안정성이 향상될 수 있습니다. 추가로 애플리케이션 가상화가 적용되면 사용자마다 애플리케이션을 설치할 필요가 없는 등 기업 입장에서는 애플리케이션 관리와 효율 측면에서도 이점을 누릴 수 있습니다.

또 사무실 내에서 사용하는 PC와 씬클라이언트가 네트워크를 통해 서버와 접속이 가능한 상태라면 원격제어를 통해 개인이 사용하는 PC, 넷북과 같은 온라인 접속이 가능한 기기를 이용해 원격에서 업무를 볼 수 있는 환경도 가능해집니다. 특히 스마트폰에서 자신의 PC에 접속해 작업을 할 수 있도록 주요 솔루션 업체들이 스마트폰 등을 위한 애플리케이션을 개발해 제공하고 있습니다.
데스크톱 가상화는 최근 컴퓨팅 업계의 최대 이슈인 `클라우드' 시스템과도 밀접한 연관이 있습니다. 근무자들이 모니터만 제공받고 이를 키보드, 비디오, 마우스 등 입출력 장치에 연결해 업무를 볼 수 있는 시스템을 의미하기 때문이죠.

데스크톱 가상화는 사무실 내 공간활용을 극대화하고 전력량을 감소시키는 `그린 IT'를 실천한다는 점에서도 친환경 미래지향적인 의미를 담고 있습니다. 관련 업체들은 데스크톱 가상화 솔루션 도입을 통해 기존 PC환경 대비 최대 60% 이상 비용을 절감하고 소비전력도 20% 이상 낮출 수 있다고 주장합니다. 또한 클라우드 환경을 통해 해커침입과 바이러스 감염으로부터도 안전하다는 점도 강조하고 있습니다.

국내에서는 통신업체 KT가 올해 5월 `ucloud vdi'를 출시했고, 이나루티앤티, 틸론 등이 관련 제품을 속속 선보이고 있습니다. 삼성SDS, LG CNS, SK C&C 등 IT서비스 업체들도 그동안의 경험을 살려 데스크톱 가상화 시장 공략에 고삐를 당기고 있습니다. VM웨어, 오라클, 시트릭스시스템스 등 해외업체들의 공세도 거센 편입니다.

한편 산업계에서는 데스크톱 가상화의 장점을 인식하고 이를 속속 도입하고 있습니다. 웅진그룹은 IT본부를 대상으로 관련 솔루션을 시범 적용한 뒤 웅진코웨이, 웅진씽크빅 등 다른 계열사로 도입을 확대시키고 있습니다.

또한 KCC건설은 최근 데스크톱 가상화 환경 구축을 통해 데스크톱을 중앙에서 집중 관리함으로써 지방 현장, 협력사와 협업 시에 도면, 설계도와 같은 핵심 자산의 유출을 방지하도록 했습니다. 기업은행과 우리금융그룹 등 금융권에서도 가상 데스크톱 수백여대의 구축을 통해 전자문서회의시스템, 페이퍼리스 환경 구현과 정보유출 위험 차단까지 효과를 거두고 있습니다.

공공기관에서도 데스크톱 가상화 적용이 활발한 편입니다. 이미 근로복지공단, 교육과학기술부, 국방부, 한국전력, 건강보험관리공단, 한국농어촌공사 등의 공공기관이 데스크톱 가상화 도입을 추진했거나 추진 중입니다. 특히 교육과학기술부는 전체 PC 1000대를 대상으로 2013년까지 데스크톱 가상화를 도입할 예정이며, 국방부도 2012년 통합정보관리소 구축과 함께 데스크톱 가상화를 통한 서버기반컴퓨팅의 구현에 나선다는 계획입니다.

정보통신산업진흥원이 국내 소프트웨어(SW) 산업 종사자, 개발자, SW기업 대표 등 600여명을 대상으로 설문조사를 한 결과 2011년 SW 최대 기술이슈로 가상화가 선정됐는데요, 다양한 가상화 영역 중에서도 가장 활발히 상품화되고 있는 분야가 바로 `데스크톱 가상화'입니다.

데스크톱 가상화가 모바일 영역으로 확대되면서 모바일 접속을 통해 데스크톱 가상화 환경을 구현하려는 기업들도 증가할 전망입니다. 기업 입장에서도 중앙에서 기업 데이터와 IT 자산을 안정적으로 관리하면서 비용 효율을 확보할 수 있다는 장점을 누리고, 직원들도 업무를 자유롭게 수행할 수 있는 효과를 얻을 수 있다는 점에서 데스크톱 가상화를 도입하려는 기업이나 단체는 더욱 늘어날 것으로 보입니다.

신동규 기자 dkshin@

[저작권자 ⓒ디지털타임스 무단 전재-재배포 금지]

네이버 OpenAPI 사용 (java) : http://onedays.co.kr/90088524098

네이버 오픈API와 JAVA를 이용하여 JAVA 어플리케이션에서도 사용할 수 있는 소스입니다. (지도를 빼고 컨텐츠API, 데이터API, 검색API, 기능API 사용이 가능합니다.) 찾아보다가 다음에서 사용하는 소스가 있더군요. 그렇지만 다음도 제대로 파싱을 하지 못하더군요. 그래서 수정좀 하고 네이버에 맞게 해봤습니다. 그래도 편한게 네이버인지라. ㅎ 정말 편리하게 사용이 가능합니다. 제가 정말 필요해서 열심히 찾아서, 수정해 보았습니다. 아마도 필요하신 분들도 있을 것 같아서요.

저는 클래스 파일 하나 더 만들어서 사용했습니다. 그걸 이용하면 더 다양하게 접근 할 수 있을 것 같습니다 (소스파일 첨부할껍니다. 소스에 대해서 설명하는 겁니다). 그리고 저는 책을 검색하는 자바 프로그램을 만들어 보았습니다.

메인으로 사용할 클래스는 NaverOpenAPI.java 라고 하고 사용하였습니다.

apiKey 는 말 그대로 키입니다. 발급받아서 사용하면 되겠죠?

bookName 은 검색할 책이름입니다. Swing이나 AWT에서 이부분을 바꾸면 쉽게 사용 가능하겠죠?

url 은 주소를 만들어 넣는 곳입니다. try 문에서 먼저 apiKey 를 넣어서 사용하구요, 그뒤에서 요청 변수를 넣으면 됩니다. 일단 네이버 오픈API 사이트에서 필수 항목인 key, target, query 는 꼭 넣어야 합니다. 위의 소스를 보시면 target에 book 이라고 되어 있죠? 이것을 바꾸면 여러분이 원하는 API를 사용할 수 있습니다.

그래도 다음과 같이 사용하면 편리하겠죠?

일단 소스는 두번째 수정된 것으로 올리겠습니다. ㅎㅎㅎ 일단 저렇게 해서 자신이 검색하고자 하는 것 번호를 targets[] 안에 입력하면 됩니다. 그리고 NaverParse 클래스를 가지고 와서 객체로 만드는 것을 볼 수 있습니다. 이것을 통해서 수정하면 정말 쉽게 만들수 있겠죠?

NaverParse.java 는 다음과 같습니다.

여기서 주의 깊게 보실것은 getContent 메소드 입니다. 보면 별건 없는 것 같지만 은근히(?) 중요합니다. 따지고 보면 값을 넘겨주는 역할을 하니깐요. 우리가 받아야 하는 것은 문자열이기 때문에 메소드가 돌려보내주는게 문자열인 스트링인 것을 확인하실 수 있습니다. 이건 수정하지 않고 사용하는게 좋겠지만 그래도 입맛에 따라 바꾸세요 ^^ parse 메소드는 실질적으로 주소가 넘어오면(쿼리가 포함된) 그것을 가지고 출력하는 곳입니다. 35번째 줄에서 item 이라고 테그 네임을 지정하는 것을 보실수 있죠? 이것은 아이템을 모두 가지고 온 겁니다. 아이템이 무엇인지는 다들 아실거라고 생각하고. 일단 가지고 올 필드는 41번째 줄과 같이 가지고 오면 됩니다. 필드 명은 네이버 개발자센터를 참조하세요.(네이버 개발자 센터: http://dev.naver.com/openapi/) 일단 책에 관련된 필드는 다음과 같습니다.

여기에서 item 밑에 나온 것을 입력하여 찾게 되는 겁니다. 그리고 출력하게 되는 거죠.

자 어떠세요? 간단하게 만들어본 네이버 오픈API + 자바 프로그래밍 이였습니다.

출력은 다음과 같이 콘솔에 출력이 됩니다. 처음 검색할때 아무것도 입력하지 않으면 10개만 출력하게 됩니다. 그러니 여러분이 원하시는 것을 입력하는게 좋겠죠? 요청변수를 잘 활용하시면 여러분이 원하는 것을 출력할 수 있습니다.

출력하고 나면 검색어에 대해서는 <b> 가 씌워지는데요, 자바에서는 <strong> 이라고 묶입니다. 간단하게 없애는 방법은 NaverParse.java 에서 20번째 줄에 있는 것을 다음과 같이 바꿔줍니다.

String str = cElement.getFirstChild().getNodeValue();
str = str.replaceAll("<strong>", "");
str = str.replaceAll("</strong>", "");

이렇게 되면 <strong> 과 </strong> 을 지울수 있습니다. 지금까지 네이버 오픈API와 자바(JAVA)를 이용한 간단한 팁이였습니다.

구글의 모바일 플랫폼 진저브레드의 빌드 : 프로요만 빌드하다 ubuntu를 64bit로 다시 설치하느라고 늦었군요.

메모리 1G로 잡고 Intel I3 버철머신에서 빌드하니 약 8~9시간 걸립니다.

성능 좋은 직접설치된 우분투에서는 2~3시간 걸리겠꾼요.


- 1002회 리눅스마스터 1급 실기 (2010.11.13)


단답식 (1~10)

1. [사용자 관리] 사용자 계정 생성

2. [시스템 운영] 시스템 백업과 하드디스크 추가

3. [시스템 운영] 프로세스관리 "ps" 명령어의 옵션 및 출력내용 세부 정보

4. [시스템 운영] 시스템 관리 "tar" 명령어의 옵션 및 파일 압축관리

5. [시스템 운영] 시스템에 장치 추가 및 그에 따른 모듈 적재 및 문제해결

6. [시스템 운영] 리눅스 네트워크 설정

7. [시스템 운영] 시스템 로그 파일 관리

8. [시스템 운영] 시스템에 로그인한 사용자 정보 얻기

9. [사용자 관리] 일반사용자를 sudo 유저로 설정

10. [사용자 관리] 시스템 교체시 shadow 유저 이동 및 관리


작업식

11. [시스템 운영] Apache 서버 설정

12. [시스템 운영] proftpd 서버 설정

13. [시스템 운영] sendmail 서버 설정

14. [시스템 운영] Apache 서버 로그 설정

15. [시스템 운영] name 서버 설정파일

16. [시스템 운영] TCP-WRAPPER를 이용한 시스템 보안설정


- 1001회 리눅스마스터 1급 실기 (2010.05.15)


단답형 (1-10)

1. [시스템 운영] telnet에 로그인 후 shell 설정으로 자동 로그아웃

2. [시스템 운영] CD-ROM 마운트

3. [시스템 운영] 프로세스 관리 (top, ps)

4. [시스템 운영] 프로그램 설치 : rpm명령어 옵션

5. [시스템 운영] 이더넷 네트워크 카드 추가 설치

6. [시스템 운영] Apache 웹 서버 설정 : 접근 제어

7. [시스템 운영] 로그를 남기는 데몬들

8. [시스템 운영] SSH 의 보안 모델

9. [시스템 운영] 메일서비스의 컴포넌트

10. [시스템 운영] 슈퍼데몬에 의한 telnetd데몬 구동 설정


작업식 (11~16)

11. [시스템 운영] Apache 웹 서버 가상 도메인 운영

12. [시스템 운영] SATA 방식의 하드디스크 추가 설치

13. [시스템 운영] 로그 시스템 관리

14. [시스템 운영] 웹 서비스의 백업 셋팅 : /etc/rsyncd.conf

15. [시스템 운영] sendmail 시스템 설정

16. [시스템 운영] 패킷필터링 : iptables 명령


- 0902회 리눅스마스터 1급 2차 실기 (2009.11.15)

단답식 (1~10)

1. [시용자 관리] root 계정 일정 시간 후 자동 로그 아웃 설정 : bash셀

2. [시스템 운영] 파일시스템 디렉토리 퍼미션 설정

3. [시스템 운영] cron데몬 : 주기적 프로세스 실행

4. [시스템 개발] gcc 옵션

5. [시스템 운영] 시스템 업그래이드 및 셋팅 확인 ; cat /proc/*

6. [시스템 운영] PCI 장치들에 대한 설정 보기

7. [시스템 운영] lsmod 명령 실행 및 결과 보기

8. [시스템 운영] 리눅스 시스템 로그 분석

9. [시스템 운영] SSH설정 및 운영

10. [시스템 운영] 리눅스 시스템 백업 : tape 드라이브


작업식 (11~16)

11. [시스템 운영] phpinfo.php 실행 시 설정 httpd.conf 설정 파일

12. [시스템 운영] 웹 서버 로그 설정 : httpd.conf

13. [시스템 운영] pop3 메일 서버 접속 및 테스트

14. [시스템 운영] 포트스케닝 : nmap 도구 사용

15. [시스템 운영] 보조 DNS 서버를 위한 설정 파일 : zone파일

16. [시스템 보안] 시스템 침입 감지


- 0901 (19)회 리눅스마스터 1급 실기 (2009.05.17)


단답식 (1~10)

1. [시스템 운영] Samba 서버 설정 및 마운트 관리

2. [시스템 운영] 운영체제 설치 후 가상메모리 swap 공간 추가 설정

3. [사용자 관리] 파일 권한 관리 : chown

4. [사용자 관리] su 명령어 사용

5. [시스템 운영] 시스템 레벨 3으로 부팅 5로 변경

6. [시스템 운영] 하드디스크 추가 및 파티션 마운트

7. [시스템 관리] 리눅스 로그 파일 시스템

8. [시스템 관리] httpd 서버의 로그 분산요령

9. [시스템 관리] TCP Wrapper를 통한 접근제어 설정

10. [시스템 관리] 리눅스 시스템 백업 .sh 스크립트 작성


작업식 (11~16)

11. [시스템 운영] proftpd 서버 운영 및 설정

12. [시스템 운영] DNS 서버의 설정 및 운영, APM 서버 연동

13. [시스템 관리] 아파치 웹서버 인증기능 사용

14. [시스템 운영] SendMail서버 설정 및 운영

15. [시스템 관리] bind를 사용한 DNS 서버 설정 : conf, zone 파일 작성

16. [시스템 보안] iptables 사용



- 0802 (18)회 리눅스마스터 1급 실기 (2008.12.07)


단답식 (1~10)

1. [사용자 관리] passwd 명령어로 사용자 접속 제한

2. [시스템 관리] 시스템에 마운트된 정보가 저장된 파일

3. [시스템 관리] pstree 명령어 구문

4. [시스템 관리] rpm 명령어 사용

5. [시스템 운영] 메일서버 스펨메일 차단

6. [시스템 보안] 네트워크 공격 종류 판별

7. [시스템 관리] logrotate 설정

8. [시스템 관리] 사용자 로그인 메시지 설정

9. [시스템 관리] 커널 컴파일 방법/순서

10. [시스템 관리] 시스템에 랜카드 추가


작업식(11~16)

11. [시스템 운영] 아파치 웹서버 운영

12. [시스템 운영] 아파치 http.conf 파일 설정

13. [시스템 운영] Samba 서버 운영

14. [시스템 관리] /etc/named.conf 설정

15. [시스템 관리] iptables 명령어 설정

16. [시스템 관리] 시스템 백업 cpio 파일 생성 설정


- 0801 (17)회 리눅스마스터 1급 실기 (2008.09.27)


단답식 (1~10)

1. [시스템 관리] 플로피 시스템 관리

2. [시스템 관리] telnet 로그 관리

3. [시스템 관리] ssh 서버의 로그 관리

4. [시스템 관리] 사용자 로그 관리

5. [시스템 관리] HDD 장착

6. [시스템 관리] group 명령어

7. [시스템 관리] df 명령어로 swap 파티션 표시

8. [시스템 관리] 특정한 사용자가 수행한 프로세스 삭제

9. [시스템 관리] rpm 패키지 중복 설치 제거

10. [시스템 관리] 랜카드 구성 / 설정


작업식 (11~16)

11. [시스템 운영] sendmail 서버 메일링 리스트 관리

12. [시스템 운영] xinetd에 설치된 telnet 서비스 설정

13. [시스템 관리] ssh 서버의 TCP wrapper로 보안 설정

14. [시스템 보안] iptables를 사용한 보안 설정 (포트 차단)

15. [시스템 운영] 아파치 웹서버 httpd.conf 설정

16. [시스템 운영] 아파치 가상호스트 httpd.conf 설정



- 16회 리눅스마스터 1급 실기 (2008.06.29)


단답식 (1~10)

1. [사용자 관리] 사용자 그룹관리

2. [시스템 운영] 파일 시스템에 따른 디스크 마운트 / 언마운트

3. [시스템 운영] 서비스(http) Stop : 시그널 명령

4. [시스템 운영] 서버의 데이타 백업 : tar, gzip,

5. [시스템 운영] 하드디스크 추가 및 운영, 자동 마운트

6. [시스템 운영] 하드디스크 추가 / 파티션 별

7. [시스템 운영] 원격 로그 서버 설정 및 운영

8. [시스템 운영] 아파치 웹 서버의 로그 파일 관리

9. [시스템 운영] ssh 서버 운영 및 보안키

10. [시스템 운영] cpio를 이용한 시스템 백업


작업식(11~16)

11. [시스템 운영] 웹 서버 설정 파일, 가상 호스트 : http.conf

12. [시스템 운영] MySql 서버의 설정 : ./configure 활용

13. [시스템 운영] Samba 서버의 설정과 운영

14. [시스템 운영] xinetd를 이용한 서버간 포트 포워딩

15. [시스템 운영] SendMail의 설정과 운영

16. [시스템 보안] DOS 공결의 로그확인 : netstat

롯데라아에서....


[출처] http://www.cs.cf.ac.uk/Dave/C/node32.html

Subsections


Thread programming examples

This chapter gives some full code examples of thread programs. These examles are taken from a variety of sources:

  • The sun workshop developers web page http://www.sun.com/workshop/threads/share-code/ on threads is an excelleny source
  • The web page http://www.sun.com/workshop/threads/Berg-Lewis/examples.html where example from the Threads Primer Book by D. Berg anD B. Lewis are also a major resource.

Using thr_create() and thr_join()

This example exercises the thr_create() and thr_join() calls. There is not a parent/child relationship between threads as there is for processes. This can easily be seen in this example, because threads are created and joined by many different threads in the process. The example also shows how threads behave when created with different attributes and options.

Threads can be created by any thread and joined by any other.

The main thread: In this example the main thread's sole purpose is to create new threads. Threads A, B, and C are created by the main thread. Notice that thread B is created suspended. After creating the new threads, the main thread exits. Also notice that the main thread exited by calling thr_exit(). If the main thread had used the exit() call, the whole process would have exited. The main thread's exit status and resources are held until it is joined by thread C.

Thread A: The first thing thread A does after it is created is to create thread D. Thread A then simulates some processing and then exits, using thr_exit(). Notice that thread A was created with the THR_DETACHED flag, so thread A's resources will be immediately reclaimed upon its exit. There is no way for thread A's exit status to be collected by a thr_join() call.

Thread B: Thread B was created in a suspended state, so it is not able to run until thread D continues it by making the thr_continue() call. After thread B is continued, it simulates some processing and then exits. Thread B's exit status and thread resources are held until joined by thread E.

Thread C: The first thing that thread C does is to create thread F. Thread C then joins the main thread. This action will collect the main thread's exit status and allow the main thread's resources to be reused by another thread. Thread C will block, waiting for the main thread to exit, if the main thread has not yet called thr_exit(). After joining the main thread, thread C will simulate some processing and then exit. Again, the exit status and thread resources are held until joined by thread E.

Thread D: Thread D immediately creates thread E. After creating thread E, thread D continues thread B by making the thr_continue() call. This call will allow thread B to start its execution. Thread D then tries to join thread E, blocking until thread E has exited. Thread D then simulates some processing and exits. If all went well, thread D should be the last nondaemon thread running. When thread D exits, it should do two things: stop the execution of any daemon threads and stop the execution of the process.

Thread E: Thread E starts by joining two threads, threads B and C. Thread E will block, waiting for each of these thread to exit. Thread E will then simulate some processing and will exit. Thread E's exit status and thread resources are held by the operating system until joined by thread D.

Thread F: Thread F was created as a bound, daemon thread by using the THR_BOUND and THR_DAEMON flags in the thr_create() call. This means that it will run on its own LWP until all the nondaemon threads have exited the process. This type of thread can be used when you want some type of "background" processing to always be running, except when all the "regular" threads have exited the process. If thread F was created as a non-daemon thread, then it would continue to run forever, because a process will continue while there is at least one thread still running. Thread F will exit when all the nondaemon threads have exited. In this case, thread D should be the last nondaemon thread running, so when thread D exits, it will also cause thread F to exit.

This example, however trivial, shows how threads behave differently, based on their creation options. It also shows what happens on the exit of a thread, again based on how it was created. If you understand this example and how it flows, you should have a good understanding of how to use thr_create() and thr_join() in your own programs. Hopefully you can also see how easy it is to create and join threads.

The source to multi_thr.c:

#define _REENTRANT#include <stdio.h>#include <thread.h>/* Function prototypes for thread routines */void *sub_a(void *);void *sub_b(void *);void *sub_c(void *);void *sub_d(void *);void *sub_e(void *);void *sub_f(void *);thread_t thr_a, thr_b, thr_c;void main(){thread_t main_thr;main_thr = thr_self();printf("Main thread = %d\n", main_thr); if (thr_create(NULL, 0, sub_b, NULL, THR_SUSPENDED|THR_NEW_LWP, &thr_b))        fprintf(stderr,"Can't create thr_b\n"), exit(1);if (thr_create(NULL, 0, sub_a, (void *)thr_b, THR_NEW_LWP, &thr_a))        fprintf(stderr,"Can't create thr_a\n"), exit(1);if (thr_create(NULL, 0, sub_c, (void *)main_thr, THR_NEW_LWP, &thr_c))        fprintf(stderr,"Can't create thr_c\n"), exit(1);printf("Main Created threads A:%d B:%d C:%d\n", thr_a, thr_b, thr_c); printf("Main Thread exiting...\n"); thr_exit((void *)main_thr);}void *sub_a(void *arg){thread_t thr_b = (thread_t) arg;thread_t thr_d;int i;printf("A: In thread A...\n"); if (thr_create(NULL, 0, sub_d, (void *)thr_b, THR_NEW_LWP, &thr_d))        fprintf(stderr, "Can't create thr_d\n"), exit(1);printf("A: Created thread D:%d\n", thr_d); /* process */for (i=0;i<1000000*(int)thr_self();i++);printf("A: Thread exiting...\n"); thr_exit((void *)77);}void * sub_b(void *arg){int i;printf("B: In thread B...\n"); /* process */for (i=0;i<1000000*(int)thr_self();i++);printf("B: Thread exiting...\n"); thr_exit((void *)66);}void * sub_c(void *arg){void *status;int i;thread_t main_thr, ret_thr;main_thr = (thread_t)arg;printf("C: In thread C...\n"); if (thr_create(NULL, 0, sub_f, (void *)0, THR_BOUND|THR_DAEMON, NULL))        fprintf(stderr, "Can't create thr_f\n"), exit(1);printf("C: Join main thread\n"); if (thr_join(main_thr,(thread_t *)&ret_thr, &status))         fprintf(stderr, "thr_join Error\n"), exit(1);printf("C: Main thread (%d) returned thread (%d) w/status %d\n", main_thr, ret_thr, (int) status); /* process */for (i=0;i<1000000*(int)thr_self();i++);printf("C: Thread exiting...\n"); thr_exit((void *)88);}void * sub_d(void *arg){thread_t thr_b = (thread_t) arg;int i;thread_t thr_e, ret_thr;void *status;printf("D: In thread D...\n"); if (thr_create(NULL, 0, sub_e, NULL, THR_NEW_LWP, &thr_e))        fprintf(stderr,"Can't create thr_e\n"), exit(1);printf("D: Created thread E:%d\n", thr_e); printf("D: Continue B thread = %d\n", thr_b); thr_continue(thr_b);printf("D: Join E thread\n"); if(thr_join(thr_e,(thread_t *)&ret_thr, &status))         fprintf(stderr,"thr_join Error\n"), exit(1);printf("D: E thread (%d) returned thread (%d) w/status %d\n", thr_e, ret_thr, (int) status); /* process */for (i=0;i<1000000*(int)thr_self();i++);printf("D: Thread exiting...\n"); thr_exit((void *)55);}void * sub_e(void *arg){int i;thread_t ret_thr;void *status;printf("E: In thread E...\n"); printf("E: Join A thread\n"); if(thr_join(thr_a,(thread_t *)&ret_thr, &status))         fprintf(stderr,"thr_join Error\n"), exit(1);printf("E: A thread (%d) returned thread (%d) w/status %d\n", ret_thr, ret_thr, (int) status); printf("E: Join B thread\n"); if(thr_join(thr_b,(thread_t *)&ret_thr, &status))         fprintf(stderr,"thr_join Error\n"), exit(1);printf("E: B thread (%d) returned thread (%d) w/status %d\n", thr_b, ret_thr, (int) status); printf("E: Join C thread\n"); if(thr_join(thr_c,(thread_t *)&ret_thr, &status))         fprintf(stderr,"thr_join Error\n"), exit(1);printf("E: C thread (%d) returned thread (%d) w/status %d\n", thr_c, ret_thr, (int) status); for (i=0;i<1000000*(int)thr_self();i++);printf("E: Thread exiting...\n"); thr_exit((void *)44);}void *sub_f(void *arg){int i;printf("F: In thread F...\n"); while (1) {        for (i=0;i<10000000;i++);        printf("F: Thread F is still running...\n");         }}

Arrays

This example uses a data structure that contains multiple arrays of data. Multiple threads will concurrently vie for access to the arrays. To control this access, a mutex variable is used within the data structure to lock the entire array and serialize the access to the data.

The main thread first initializes the data structure and the mutex variable. It then sets a level of concurrency and creates the worker threads. The main thread then blocks by joining all the threads. When all the threads have exited, the main thread prints the results.

The worker threads modify the shared data structure from within a loop. Each time the threads need to modify the shared data, they lock the mutex variable associated with the shared data. After modifying the data, the threads unlock the mutex, allowing another thread access to the data.

This example may look quite simple, but it shows how important it is to control access to a simple, shared data structure. The results can be quite different if the mutex variable is not used.

The source to array.c:

#define _REENTRANT#include <stdio.h>#include <thread.h>/* sample array data structure */struct {        mutex_t data_lock[5];        int     int_val[5];        float   float_val[5];        } Data;/* thread function */void *Add_to_Value();main(){int i;/* initialize the mutexes and data */for (i=0; i<5; i++) {        mutex_init(&Data.data_lock[i], USYNC_THREAD, 0);        Data.int_val[i] = 0;        Data.float_val[i] = 0;        }/* set concurrency and create the threads */thr_setconcurrency(4);for (i=0; i<5; i++)    thr_create(NULL, 0, Add_to_Value, (void *)(2*i), 0, NULL);/* wait till all threads have finished */for (i=0; i<5; i++)        thr_join(0,0,0);/* print the results */printf("Final Values.....\n");for (i=0; i<5; i++) {        printf("integer value[%d] =\t%d\n", i, Data.int_val[i]);        printf("float value[%d] =\t%.0f\n\n", i, Data.float_val[i]);        }return(0);}/* Threaded routine */void *Add_to_Value(void *arg){int inval = (int) arg;int i;for (i=0;i<10000;i++){    mutex_lock(&Data.data_lock[i%5]);       Data.int_val[i%5] += inval;       Data.float_val[i%5] += (float) 1.5 * inval;    mutex_unlock(&Data.data_lock[i%5]);    }return((void *)0);}

Deadlock

This example demonstrates how a deadlock can occur in multithreaded programs that use synchronization variables. In this example a thread is created that continually adds a value to a global variable. The thread uses a mutex lock to protect the global data.

The main thread creates the counter thread and then loops, waiting for user input. When the user presses the Return key, the main thread suspends the counter thread and then prints the value of the global variable. The main thread prints the value of the global variable under the protection of a mutex lock.

The problem arises in this example when the main thread suspends the counter thread while the counter thread is holding the mutex lock. After the main thread suspends the counter thread, it tries to lock the mutex variable. Since the mutex variable is already held by the counter thread, which is suspended, the main thread deadlocks.

This example may run fine for a while, as long as the counter thread just happens to be suspended when it is not holding the mutex lock. The example demonstrates how tricky some programming issues can be when you deal with threads.

The source to susp_lock.c

#define _REENTRANT#include <stdio.h>#include <thread.h>/* Prototype for thread subroutine */void *counter(void *);int count;mutex_t count_lock;main(){char str[80];thread_t ctid;/* create the thread counter subroutine */thr_create(NULL, 0, counter, 0, THR_NEW_LWP|THR_DETACHED, &ctid);while(1) {        gets(str);        thr_suspend(ctid);        mutex_lock(&count_lock);        printf("\n\nCOUNT = %d\n\n", count);        mutex_unlock(&count_lock);        thr_continue(ctid);        }return(0);}void *counter(void *arg){int i;while (1) {        printf("."); fflush(stdout);        mutex_lock(&count_lock);        count++;        for (i=0;i<50000;i++);        mutex_unlock(&count_lock);        for (i=0;i<50000;i++);        }return((void *)0);}

Signal Handler

This example shows how easy it is to handle signals in multithreaded programs. In most programs, a different signal handler would be needed to service each type of signal that you wanted to catch. Writing each of the signal handlers can be time consuming and can be a real pain to debug.

This example shows how you can implement a signal handler thread that will service all asynchronous signals that are sent to your process. This is an easy way to deal with signals, because only one thread is needed to handle all the signals. It also makes it easy when you create new threads within the process, because you need not worry about signals in any of the threads.

First, in the main thread, mask out all signals and then create a signal handling thread. Since threads inherit the signal mask from their creator, any new threads created after the new signal mask will also mask all signals. This idea is key, because the only thread that will receive signals is the one thread that does not block all the signals.

The signal handler thread waits for all incoming signals with the sigwait() call. This call unmasks the signals given to it and then blocks until a signal arrives. When a signal arrives, sigwait() masks the signals again and then returns with the signal ID of the incoming signal.

You can extend this example for use in your application code to handle all your signals. Notice also that this signal concept could be added in your existing nonthreaded code as a simpler way to deal with signals.

The source to thr_sig.c

#define _REENTRANT#include <stdio.h>#include <thread.h>#include <signal.h>#include <sys/types.h>void *signal_hand(void *);main(){sigset_t set;/* block all signals in main thread.  Any other threads that are   created after this will also block all signals */sigfillset(&set); thr_sigsetmask(SIG_SETMASK, &set, NULL);/* create a signal handler thread.  This thread will catch all   signals and decide what to do with them.  This will only   catch nondirected signals.  (I.e., if a thread causes a SIGFPE    then that thread will get that signal. */thr_create(NULL, 0, signal_hand, 0, THR_NEW_LWP|THR_DAEMON|THR_DETACHED, NULL);while (1) {	/*	Do your normal processing here....	*/	}  /* end of while */return(0);}void *signal_hand(void *arg){sigset_t set;int sig;sigfillset(&set); /* catch all signals */while (1) { 	/* wait for a signal to arrive */	switch (sig=sigwait(&set)) {	  /* here you would add whatever signal you needed to catch */	  case SIGINT : {			printf("Interrupted with signal %d, exiting...\n", sig);			exit(0); 			}	  default : printf("GOT A SIGNAL = %d\n", sig); 	  } /* end of switch */	} /* end of while */return((void *)0);} /* end of signal_hand */

Another example of a signal handler, sig_kill.c:

/**  Multithreaded Demo Source* *  Copyright (C) 1995 by Sun Microsystems, Inc.*  All rights reserved.* *  This file is a product of SunSoft, Inc. and is provided for*  unrestricted use provided that this legend is included on all*  media and as a part of the software program in whole or part.*  Users may copy, modify or distribute this file at will.* *  THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING*  THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR*  PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.* *  This file is provided with no support and without any obligation on the*  part of SunSoft, Inc. to assist in its use, correction, modification or*  enhancement.* *  SUNSOFT AND SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT*  TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS*  FILE OR ANY PART THEREOF.* *  IN NO EVENT WILL SUNSOFT OR SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY*  LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL*  DAMAGES, EVEN IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH*  DAMAGES.* *  SunSoft, Inc.*  2550 Garcia Avenue*  Mountain View, California  94043*//*  * Rich Schiavi writes:  		Sept 11, 1994 * * I believe the recommended way to kill certain threads is * using a signal handler which then will exit that particular * thread properly. I'm not sure the exact reason (I can't remember), but * if you take out the signal_handler routine in my example, you will see what * you describe, as the main process dies even if you send the * thr_kill to the specific thread. * I whipped up a real quick simple example which shows this using * some sleep()s to get a good simulation. */#include <stdio.h>#include <thread.h>#include <signal.h>static	thread_t 	one_tid, two_tid, main_thread;static 	void	*first_thread();static 	void	*second_thread();void		ExitHandler(int);static	mutex_t	    	first_mutex, second_mutex;int 	first_active = 1 ; int	second_active = 1;main(){  int i;  struct sigaction act;  act.sa_handler = ExitHandler;  (void) sigemptyset(&act.sa_mask);  (void) sigaction(SIGTERM, &act, NULL);   mutex_init(&first_mutex, 0 , 0);  mutex_init(&second_mutex, 0 , 0);  main_thread = thr_self();  thr_create(NULL,0,first_thread,0,THR_NEW_LWP,&one_tid);  thr_create(NULL,0,second_thread,0,THR_NEW_LWP,&two_tid);   for (i = 0; i < 10; i++){    fprintf(stderr, "main loop: %d\n", i);    if (i == 5)	{      thr_kill(one_tid, SIGTERM);    }    sleep(3);  }  thr_kill(two_tid, SIGTERM);  sleep(5);  fprintf(stderr, "main exit\n");}static void *first_thread(){  int i = 0;   fprintf(stderr, "first_thread id: %d\n", thr_self());  while (first_active){    fprintf(stderr, "first_thread: %d\n", i++);	    sleep(2);  }  fprintf(stderr, "first_thread exit\n");}static void *second_thread(){  int i = 0;  fprintf(stderr, "second_thread id: %d\n", thr_self());  while (second_active){    fprintf(stderr, "second_thread: %d\n", i++);    sleep(3);  }  fprintf(stderr, "second_thread exit\n");}void ExitHandler(int sig){  thread_t id;  id = thr_self();  fprintf(stderr, "ExitHandler thread id: %d\n", id);  thr_exit(0);}

Interprocess Synchronization

This example uses some of the synchronization variables available in the threads library to synchronize access to a resource shared between two processes. The synchronization variables used in the threads library are an advantage over standard IPC synchronization mechanisms because of their speed. The synchronization variables in the threads libraries have been tuned to be very lightweight and very fast. This speed can be an advantage when your application is spending time synchronizing between processes.

This example shows how semaphores from the threads library can be used between processes. Note that this program does not use threads; it is just using the lightweight semaphores available from the threads library.

When using synchronization variables between processes, it is important to make sure that only one process initializes the variable. If both processes try to initialize the synchronization variable, then one of the processes will overwrite the state of the variable set by the other process.

The source to ipc.c

#include <stdio.h>#include <fcntl.h>#include <sys/mman.h>#include <synch.h>#include <sys/types.h>#include <unistd.h>/* a structure that will be used between processes */typedef struct {	sema_t mysema;	int num;} buf_t;main(){int 	i, j, fd;buf_t 	*buf;/* open a file to use in a memory mapping */fd = open("/dev/zero", O_RDWR);/* create a shared memory map with the open file for the data    structure that will be shared between processes */buf=(buf_t *)mmap(NULL, sizeof(buf_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);/* initialize the semaphore -- note the USYNC_PROCESS flag; this makes   the semaphore visible from a process level */sema_init(&buf->mysema, 0, USYNC_PROCESS, 0);/* fork a new process */if (fork() == 0) {	/* The child will run this section of code */	for (j=0;j<5;j++)		{		/* have the child "wait" for the semaphore */		printf("Child PID(%d): waiting...\n", getpid());		sema_wait(&buf->mysema);		/* the child decremented the semaphore */		printf("Child PID(%d): decrement semaphore.\n", getpid());		}	/* exit the child process */ 	printf("Child PID(%d): exiting...\n", getpid());	exit(0);	}/* The parent will run this section of code *//* give the child a chance to start running */sleep(2);for (i=0;i<5;i++)	{	/* increment (post) the semaphore */	printf("Parent PID(%d): posting semaphore.\n", getpid());	sema_post(&buf->mysema);	/* wait a second */	sleep(1);	}/* exit the parent process */printf("Parent PID(%d): exiting...\n", getpid());return(0);}

The Producer / Consumer Problem

This example will show how condition variables can be used to control access of reads and writes to a buffer. This example can also be thought as a producer/consumer problem, where the producer adds items to the buffer and the consumer removes items from the buffer.

Two condition variables control access to the buffer. One condition variable is used to tell if the buffer is full, and the other is used to tell if the buffer is empty. When the producer wants to add an item to the buffer, it checks to see if the buffer is full; if it is full the producer blocks on the cond_wait() call, waiting for an item to be removed from the buffer. When the consumer removes an item from the buffer, the buffer is no longer full, so the producer is awakened from the cond_wait() call. The producer is then allowed to add another item to the buffer.

The consumer works, in many ways, the same as the producer. The consumer uses the other condition variable to determine if the buffer is empty. When the consumer wants to remove an item from the buffer, it checks to see if it is empty. If the buffer is empty, the consumer then blocks on the cond_wait() call, waiting for an item to be added to the buffer. When the producer adds an item to the buffer, the consumer's condition is satisfied, so it can then remove an item from the buffer.

The example copies a file by reading data into a shared buffer (producer) and then writing data out to the new file (consumer). The Buf data structure is used to hold both the buffered data and the condition variables that control the flow of the data.

The main thread opens both files, initializes the Buf data structure, creates the consumer thread, and then assumes the role of the producer. The producer reads data from the input file, then places the data into an open buffer position. If no buffer positions are available, then the producer waits via the cond_wait() call. After the producer has read all the data from the input file, it closes the file and waits for (joins) the consumer thread.

The consumer thread reads from a shared buffer and then writes the data to the output file. If no buffers positions are available, then the consumer waits for the producer to fill a buffer position. After the consumer has read all the data, it closes the output file and exits.

If the input file and the output file were residing on different physical disks, then this example could execute the reads and writes in parallel. This parallelism would significantly increase the throughput of the example through the use of threads.

The source to prod_cons.c:

#define _REEENTRANT#include <stdio.h>#include <thread.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/uio.h>#define BUFSIZE 512#define BUFCNT  4/* this is the data structure that is used between the producer   and consumer threads */struct {        char buffer[BUFCNT][BUFSIZE];        int byteinbuf[BUFCNT];        mutex_t buflock;        mutex_t donelock;        cond_t adddata;        cond_t remdata;        int nextadd, nextrem, occ, done;} Buf;/* function prototype */void *consumer(void *);main(int argc, char **argv){int ifd, ofd;thread_t cons_thr;/* check the command line arguments */if (argc != 3)        printf("Usage: %s <infile> <outfile>\n", argv[0]), exit(0);/* open the input file for the producer to use */if ((ifd = open(argv[1], O_RDONLY)) == -1)        {        fprintf(stderr, "Can't open file %s\n", argv[1]);        exit(1);        }/* open the output file for the consumer to use */if ((ofd = open(argv[2], O_WRONLY|O_CREAT, 0666)) == -1)        {        fprintf(stderr, "Can't open file %s\n", argv[2]);        exit(1);        }/* zero the counters */Buf.nextadd = Buf.nextrem = Buf.occ = Buf.done = 0;/* set the thread concurrency to 2 so the producer and consumer can   run concurrently */thr_setconcurrency(2);/* create the consumer thread */thr_create(NULL, 0, consumer, (void *)ofd, NULL, &cons_thr);/* the producer ! */while (1) {        /* lock the mutex */        mutex_lock(&Buf.buflock);        /* check to see if any buffers are empty */        /* If not then wait for that condition to become true */        while (Buf.occ == BUFCNT)                cond_wait(&Buf.remdata, &Buf.buflock);        /* read from the file and put data into a buffer */        Buf.byteinbuf[Buf.nextadd] = read(ifd,Buf.buffer[Buf.nextadd],BUFSIZE);        /* check to see if done reading */        if (Buf.byteinbuf[Buf.nextadd] == 0) {                /* lock the done lock */                mutex_lock(&Buf.donelock);                /* set the done flag and release the mutex lock */                Buf.done = 1;                mutex_unlock(&Buf.donelock);                /* signal the consumer to start consuming */                cond_signal(&Buf.adddata);                /* release the buffer mutex */                mutex_unlock(&Buf.buflock);                /* leave the while looop */                break;                }        /* set the next buffer to fill */        Buf.nextadd = ++Buf.nextadd % BUFCNT;        /* increment the number of buffers that are filled */        Buf.occ++;        /* signal the consumer to start consuming */        cond_signal(&Buf.adddata);        /* release the mutex */        mutex_unlock(&Buf.buflock);        }close(ifd);/* wait for the consumer to finish */thr_join(cons_thr, 0, NULL);/* exit the program */return(0);}/* The consumer thread */void *consumer(void *arg){int fd = (int) arg;/* check to see if any buffers are filled or if the done flag is set */while (1) {        /* lock the mutex */        mutex_lock(&Buf.buflock);        if (!Buf.occ && Buf.done) {           mutex_unlock(&Buf.buflock);           break;           }        /* check to see if any buffers are filled */        /* if not then wait for the condition to become true */        while (Buf.occ == 0 && !Buf.done)                cond_wait(&Buf.adddata, &Buf.buflock);        /* write the data from the buffer to the file */        write(fd, Buf.buffer[Buf.nextrem], Buf.byteinbuf[Buf.nextrem]);        /* set the next buffer to write from */        Buf.nextrem = ++Buf.nextrem % BUFCNT;        /* decrement the number of buffers that are full */        Buf.occ--;        /* signal the producer that a buffer is empty */        cond_signal(&Buf.remdata);        /* release the mutex */        mutex_unlock(&Buf.buflock);        }/* exit the thread */thr_exit((void *)0);}

A Socket Server

The socket server example uses threads to implement a "standard" socket port server. The example shows how easy it is to use thr_create() calls in the place of fork() calls in existing programs.

A standard socket server should listen on a socket port and, when a message arrives, fork a process to service the request. Since a fork() system call would be used in a nonthreaded program, any communication between the parent and child would have to be done through some sort of interprocess communication.

We can replace the fork() call with a thr_create() call. Doing so offers a few advantages: thr_create() can create a thread much faster then a fork() could create a new process, and any communication between the server and the new thread can be done with common variables. This technique makes the implementation of the socket server much easier to understand and should also make it respond much faster to incoming requests.

The server program first sets up all the needed socket information. This is the basic setup for most socket servers. The server then enters an endless loop, waiting to service a socket port. When a message is sent to the socket port, the server wakes up and creates a new thread to handle the request. Notice that the server creates the new thread as a detached thread and also passes the socket descriptor as an argument to the new thread.

The newly created thread can then read or write, in any fashion it wants, to the socket descriptor that was passed to it. At this point the server could be creating a new thread or waiting for the next message to arrive. The key is that the server thread does not care what happens to the new thread after it creates it.

In our example, the created thread reads from the socket descriptor and then increments a global variable. This global variable keeps track of the number of requests that were made to the server. Notice that a mutex lock is used to protect access to the shared global variable. The lock is needed because many threads might try to increment the same variable at the same time. The mutex lock provides serial access to the shared variable. See how easy it is to share information among the new threads! If each of the threads were a process, then a significant effort would have to be made to share this information among the processes.

The client piece of the example sends a given number of messages to the server. This client code could also be run from different machines by multiple users, thus increasing the need for concurrency in the server process.

The source code to soc_server.c:

#define _REENTRANT#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>#include <sys/uio.h>#include <unistd.h>#include <thread.h>/* the TCP port that is used for this example */#define TCP_PORT   6500/* function prototypes and global variables */void *do_chld(void *);mutex_t lock;int	service_count;main(){	int 	sockfd, newsockfd, clilen;	struct sockaddr_in cli_addr, serv_addr;	thread_t chld_thr;	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)		fprintf(stderr,"server: can't open stream socket\n"), exit(0);	memset((char *) &serv_addr, 0, sizeof(serv_addr));	serv_addr.sin_family = AF_INET;	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	serv_addr.sin_port = htons(TCP_PORT);		if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)		fprintf(stderr,"server: can't bind local address\n"), exit(0);	/* set the level of thread concurrency we desire */	thr_setconcurrency(5);	listen(sockfd, 5);	for(;;){		clilen = sizeof(cli_addr);		newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);		if(newsockfd < 0)			fprintf(stderr,"server: accept error\n"), exit(0);		/* create a new thread to process the incomming request */		thr_create(NULL, 0, do_chld, (void *) newsockfd, THR_DETACHED, &chld_thr);		/* the server is now free to accept another socket request */		}	return(0);}/* 	This is the routine that is executed from a new thread */void *do_chld(void *arg){int 	mysocfd = (int) arg;char 	data[100];int 	i;	printf("Child thread [%d]: Socket number = %d\n", thr_self(), mysocfd);	/* read from the given socket */	read(mysocfd, data, 40);	printf("Child thread [%d]: My data = %s\n", thr_self(), data);	/* simulate some processing */	for (i=0;i<1000000*thr_self();i++);	printf("Child [%d]: Done Processing...\n", thr_self()); 	/* use a mutex to update the global service counter */	mutex_lock(&lock);	service_count++;	mutex_unlock(&lock);	printf("Child thread [%d]: The total sockets served = %d\n", thr_self(), service_count);	/* close the socket and exit this thread */	close(mysocfd);	thr_exit((void *)0);}

Using Many Threads

This example that shows how easy it is to create many threads of execution in Solaris. Because of the lightweight nature of threads, it is possible to create literally thousands of threads. Most applications may not need a very large number of threads, but this example shows just how lightweight the threads can be.

We have said before that anything you can do with threads, you can do without them. This may be a case where it would be very hard to do without threads. If you have some spare time (and lots of memory), try implementing this program by using processes, instead of threads. If you try this, you will see why threads can have an advantage over processes.

This program takes as an argument the number of threads to create. Notice that all the threads are created with a user-defined stack size, which limits the amount of memory that the threads will need for execution. The stack size for a given thread can be hard to calculate, so some testing usually needs to be done to see if the chosen stack size will work. You may want to change the stack size in this program and see how much you can lower it before things stop working. The Solaris threads library provides the thr_min_stack() call, which returns the minimum allowed stack size. Take care when adjusting the size of a threads stack. A stack overflow can happen quite easily to a thread with a small stack.

After each thread is created, it blocks, waiting on a mutex variable. This mutex variable was locked before any of the threads were created, which prevents the threads from proceeding in their execution. When all of the threads have been created and the user presses Return, the mutex variable is unlocked, allowing all the threads to proceed.

After the main thread has created all the threads, it waits for user input and then tries to join all the threads. Notice that the thr_join() call does not care what thread it joins; it is just counting the number of joins it makes.

This example is rather trivial and does not serve any real purpose except to show that it is possible to create a lot of threads in one process. However, there are situations when many threads are needed in an application. An example might be a network port server, where a thread is created each time an incoming or outgoing request is made.

The source to many_thr.c:

#define _REENTRANT#include <stdio.h>#include <stdlib.h>#include <thread.h>/* function prototypes and global varaibles */void *thr_sub(void *);mutex_t lock;main(int argc, char **argv){int i, thr_count = 100;char buf;/* check to see if user passed an argument   -- if so, set the number of threads to the value      passed to the program */if (argc == 2) thr_count = atoi(argv[1]);printf("Creating %d threads...\n", thr_count);/* lock the mutex variable -- this mutex is being used to    keep all the other threads created from proceeding   */mutex_lock(&lock);/* create all the threads -- Note that a specific stack size is   given.  Since the created threads will not use all of the   default stack size, we can save memory by reducing the threads'   stack size */for (i=0;i<thr_count;i++) {	thr_create(NULL,2048,thr_sub,0,0,NULL);	}printf("%d threads have been created and are running!\n", i);printf("Press <return> to join all the threads...\n", i);/* wait till user presses return, then join all the threads */gets(&buf);printf("Joining %d threads...\n", thr_count);/* now unlock the mutex variable, to let all the threads proceed */mutex_unlock(&lock);/* join the threads */for (i=0;i<thr_count;i++) 	thr_join(0,0,0);printf("All %d threads have been joined, exiting...\n", thr_count);return(0);}/* The routine that is executed by the created threads */void *thr_sub(void *arg){/* try to lock the mutex variable -- since the main thread has   locked the mutex before the threads were created, this thread   will block until the main thread unlock the mutex */mutex_lock(&lock);printf("Thread %d is exiting...\n", thr_self());/* unlock the mutex to allow another thread to proceed */mutex_unlock(&lock);/* exit the thread */return((void *)0);}

Real-time Thread Example

This example uses the Solaris real-time extensions to make a single bound thread within a process run in the real-time scheduling class. Using a thread in the real-time class is more desirable than running a whole process in the real-time class, because of the many problems that can arise with a process in a real-time state. For example, it would not be desirable for a process to perform any I/O or large memory operations while in realtime, because a real-time process has priority over system-related processes; if a real-time process requests a page fault, it can starve, waiting for the system to fault in a new page. We can limit this exposure by using threads to execute only the instructions that need to run in realtime.

Since this book does not cover the concerns that arise with real-time programming, we have included this code only as an example of how to promote a thread into the real-time class. You must be very careful when you use real-time threads in your applications. For more information on real-time programming, see the Solaris  documentation.

This example should be safe from the pitfalls of real-time programs because of its simplicity. However, changing this code in any way could have adverse affects on your system.

The example creates a new thread from the main thread. This new thread is then promoted to the real-time class by looking up the real-time class ID and then setting a real-time priority for the thread. After the thread is running in realtime, it simulates some processing. Since a thread in the real-time class can have an infinite time quantum, the process is allowed to stay on a CPU as long as it likes. The time quantum is the amount of time a thread is allowed to stay running on a CPU. For the timesharing class, the time quantum (time-slice) is 1/100th of a second by default.

In this example, we set the time quantum for the real-time thread to infinity. That is, it can stay running as long as it likes; it will not be preempted or scheduled off the CPU. If you run this example on a UP machine, it will have the effect of stopping your system for a few seconds while the thread simulates its processing. The system does not actually stop, it is just working in the real-time thread. When the real-time thread finishes its processing, it exits and the system returns to normal.

Using real-time threads can be quite useful when you need an extremely high priority and response time but can also cause big problems if it not used properly. Also note that this example must be run as root or have root execute permissions.

The source to rt_thr.c:

#define _REENTRANT#include <stdio.h>#include <thread.h>#include <string.h>#include <sys/priocntl.h>#include <sys/rtpriocntl.h>/* thread prototype */void *rt_thread(void *);main(){/* create the thread that will run in realtime */thr_create(NULL, 0, rt_thread, 0, THR_DETACHED, 0);/* loop here forever, this thread is the TS scheduling class */while (1) {	printf("MAIN: In time share class... running\n"); 	sleep(1);	}return(0);}/*	This is the routine that is called by the created thread*/void *rt_thread(void *arg){pcinfo_t pcinfo;pcparms_t pcparms;int i;/* let the main thread run for a bit */sleep(4);/* get the class ID for the real-time class */strcpy(pcinfo.pc_clname, "RT");if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)	fprintf(stderr, "getting RT class id\n"), exit(1);/* set up the real-time parameters */pcparms.pc_cid = pcinfo.pc_cid;((rtparms_t *)pcparms.pc_clparms)->rt_pri = 10;((rtparms_t *)pcparms.pc_clparms)->rt_tqnsecs = 0;/* set an infinite time quantum */((rtparms_t *)pcparms.pc_clparms)->rt_tqsecs = RT_TQINF; /* move this thread to the real-time scheduling class */if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, (caddr_t)&pcparms) == -1)	fprintf(stderr, "Setting RT mode\n"), exit(1);/* simulate some processing */for (i=0;i<100000000;i++);printf("RT_THREAD: NOW EXITING...\n");thr_exit((void *)0);}

POSIX Cancellation

This example uses the POSIX thread cancellation capability to kill a thread that is no longer needed. Random termination of a thread can cause problems in threaded applications, because a thread may be holding a critical lock when it is terminated. Since the lock was help before the thread was terminated, another thread may deadlock, waiting for that same lock. The thread cancellation capability enables you to control when a thread can be terminated. The example also demonstrates the capabilities of the POSIX thread library in implementing a program that performs a multithreaded search.

This example simulates a multithreaded search for a given number by taking random guesses at a target number. The intent here is to simulate the same type of search that a database might execute. For example, a database might create threads to start searching for a data item; after some amount of time, one or more threads might return with the target data item.

If a thread guesses the number correctly, there is no need for the other threads to continue their search. This is where thread cancellation can help. The thread that finds the number first should cancel the other threads that are still searching for the item and then return the results of the search.

The threads involved in the search can call a cleanup function that can clean up the threads resources before it exits. In this case, the cleanup function prints the progress of the thread when it was cancelled.

The source to posix_cancel.c:

 #define _REENTRANT#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <pthread.h>/* defines the number of searching threads */#define NUM_THREADS 25/* function prototypes */void *search(void *);void print_it(void *);/* global variables */pthread_t  threads[NUM_THREADS];pthread_mutex_t lock;int tries;main(){int i;int pid;/* create a number to search for */pid = getpid();/* initialize the mutex lock */pthread_mutex_init(&lock, NULL); printf("Searching for the number = %d...\n", pid);/* create the searching threads */for (i=0;i<NUM_THREADS;i++)	pthread_create(&threads[i], NULL, search, (void *)pid);/* wait for (join) all the searching threads */for (i=0;i<NUM_THREADS;i++) 	pthread_join(threads[i], NULL);printf("It took %d tries to find the number.\n", tries);/* exit this thread */pthread_exit((void *)0);}/*	This is the cleanup function that is called when 	the threads are cancelled */void print_it(void *arg){int *try = (int *) arg;pthread_t tid;/* get the calling thread's ID */tid = pthread_self();/* print where the thread was in its search when it was cancelled */printf("Thread %d was canceled on its %d try.\n", tid, *try); }/*	This is the search routine that is executed in each thread */void *search(void *arg){int num = (int) arg;int i=0, j;pthread_t tid;/* get the calling thread ID */tid = pthread_self();/* use the thread ID to set the seed for the random number generator */srand(tid);/* set the cancellation parameters --   - Enable thread cancellation    - Defer the action of the cancellation */pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);/* push the cleanup routine (print_it) onto the thread   cleanup stack.  This routine will be called when the    thread is cancelled.  Also note that the pthread_cleanup_push   call must have a matching pthread_cleanup_pop call.  The   push and pop calls MUST be at the same lexical level    within the code *//* pass address of `i' since the current value of `i' is not    the one we want to use in the cleanup function */pthread_cleanup_push(print_it, (void *)&i);/* loop forever */while (1) {	i++;	/* does the random number match the target number? */	if (num == rand()) {		/* try to lock the mutex lock --                   if locked, check to see if the thread has been cancelled		   if not locked then continue */		while (pthread_mutex_trylock(&lock) == EBUSY)				pthread_testcancel();		/* set the global variable for the number of tries */	   	tries = i;	   	printf("thread %d found the number!\n", tid);		/* cancel all the other threads */	   	for (j=0;j<NUM_THREADS;j++) 			if (threads[j] != tid) pthread_cancel(threads[j]);		/* break out of the while loop */		break;	  	}	/* every 100 tries check to see if the thread has been cancelled            if the thread has not been cancelled then yield the thread's	   LWP to another thread that may be able to run */	if (i%100 == 0) {		pthread_testcancel();		sched_yield();		}	}/* The only way we can get here is when the thread breaks out   of the while loop.  In this case the thread that makes it here   has found the number we are looking for and does not need to run   the thread cleanup function.  This is why the pthread_cleanup_pop   function is called with a 0 argument; this will pop the cleanup   function off the stack without executing it */pthread_cleanup_pop(0);return((void *)0);}

Software Race Condition

This example shows a trivial software race condition. A software race condition occurs when the execution of a program is affected by the order and timing of a threads execution. Most software race conditions can be alleviated by using synchronization variables to control the threads' timing and access of shared resources. If a program depends on order of execution, then threading that program may not be a good solution, because the order in which threads execute is nondeterministic.

In the example, thr_continue() and thr_suspend() calls continue and suspend a given thread, respectively. Although both of these calls are valid, use caution when implementing them. It is very hard to determine where a thread is in its execution. Because of this, you may not be able to tell where the thread will suspend when the call to thr_suspend() is made. This behavior can cause problems in threaded code if not used properly.

The following example uses thr_continue() and thr_suspend() to try to control when a thread starts and stops. The example looks trivial, but, as you will see, can cause a big problem.

Do you see the problem? If you guessed that the program would eventually suspend itself, you were correct! The example attempts to flip-flop between the main thread and a subroutine thread. Each thread continues the other thread and then suspends itself.

Thread A continues thread B and then suspends thread A; now the continued thread B can continue thread A and then suspend itself. This should continue back and forth all day long, right? Wrong! We can't guarantee that each thread will continue the other thread and then suspend itself in one atomic action, so a software race condition could be created. Calling thr_continue() on a running thread and calling thr_suspend() on a suspended thread has no effect, so we don't know if a thread is already running or suspended.

If thread A continues thread B and if between the time thread A suspends itself, thread B continues thread A, then both of the threads will call thr_suspend(). This is the race condition in this program that will cause the whole process to become suspended.

It is very hard to use these calls, because you never really know the state of a thread. If you don't know exactly where a thread is in its execution, then you don't know what locks it holds and where it will stop when you suspend it.

The source to sw_race.c

Tgrep: Threadeds version of UNIX grep

Tgrep is a multi-threaded version of grep. Tgrep supports all but the -w (word search) options of the normal grep command, and a few options that are only avaliable to Tgrep. The real change from grep, is that Tgrep will recurse down through sub-directories and search all files for the target string. Tgrep searches files like the following command:

find <start path> -name "<file/directory pattern>" -exec \ (Line wrapped)        grep <options> <target> /dev/null {} \;

An example of this would be (run from this Tgrep directory)

% find . -exec grep thr_create /dev/null {} \;./Solaris/main.c:  if (thr_create(NULL,0,SigThread,NULL,THR_DAEMON,NULL)) {./Solaris/main.c:          err = thr_create(NULL,0,cascade,(void *)work,./Solaris/main.c:           err = thr_create(NULL,0,search_thr,(void *)work,%Running the same command with timex: real        4.26user        0.64sys         2.81

The same search run with Tgrep would be

% {\tt Tgrep} thr_create./Solaris/main.c:  if (thr_create(NULL,0,SigThread,NULL,THR_DAEMON,NULL)) {./Solaris/main.c:          err = thr_create(NULL,0,cascade,(void *)work,./Solaris/main.c:           err = thr_create(NULL,0,search_thr,(void *)work,%Running the same command with timex:real        0.79user        0.62sys         1.50

Tgrep gets the results almost four times faster. The numbers above where gathered on a SS20 running 5.5 (build 18) with 4 50MHz CPUs.

You can also filter the files that you want Tgrep to search like you can with find. The next two commands do the same thing, just Tgrep gets it done faster.

find . -name "*.c" -exec grep thr_create /dev/null {} \;and{\tt Tgrep} -p '.*\.c$' thr_create

The -p option will allow Tgrep to search only files that match the "regular expression" file pattern string. This option does NOT use shell expression, so to stop Tgrep from seeing a file named foobar.cyou must add the "$" meta character to the pattern and escape the real ``.'' character.

Some of the other Tgrep only options are -r, -C, -P, -e, -B, -S and -Z. The -r option stops Tgrep from searching any sub-directories, in other words, search only the local directory, but -l was taken. The -C option will search for and print "continued" lines like you find in Makefile. Note the differences in the results of grep and Tgrep run in the current directory.

The Tgrep output prints the continued lines that ended with the "character. In the case of grep I would not have seen the three values assigned to SUBDIRS, but Tgrep shows them to me (Common, Solaris, Posix).

The -P option I use when I am sending the output of a long search to a file and want to see the "progress" of the search. The -P option will print a "." (dot) on stderr for every file (or groups of files depending on the value of the -P argument) Tgrep searches.

The -e option will change the way Tgrep uses the target string. Tgrep uses two different patter matching systems. The first (with out the -e option) is a literal string match call Boyer-Moore. If the -e option is used, then a MT-Safe PD version of regular expression is used to search for the target string as a regexp with meta characters in it. The regular expression method is slower, but Tgrep needed the functionality. The -Z option will print help on the meta characters Tgrep uses.

The -B option tells Tgrep to use the value of the environment variable called TGLIMIT to limit the number of threads it will use during a search. This option has no affect if TGLIMIT is not set. Tgrep can "eat" a system alive, so the -B option was a way to run Tgrep on a system with out having other users scream at you.

The last new option is -S. If you want to see how things went while Tgrep was searching, you can use this option to print statistic about the number of files, lines, bytes, matches, threads created, etc.

Here is an example of the -S options output. (again run in the current directory)

% {\tt Tgrep} -S zimzap----------------- {\tt Tgrep} Stats. --------------------Number of directories searched:           7Number of files searched:                 37Number of lines searched:                 9504Number of matching lines to target:       0Number of cascade threads created:        7Number of search threads created:         20Number of search threads from pool:       17Search thread pool hit rate:              45.95%Search pool overall size:                 20Search pool size limit:                   58Number of search threads destroyed:       0Max # of threads running concurrenly:     20Total run time, in seconds.               1Work stopped due to no FD's:  (058)       0 Times, 0.00%Work stopped due to no work on Q:         19 Times, 43.18%Work stopped due to TGLIMITS: (Unlimited) 0 Times, 0.00%----------------------------------------------------%

For more information on the usage and options, see the man page Tgrep

The Tgrep.c source code is:

/* Copyright (c) 1993, 1994  Ron Winacott                               *//* This program may be used, copied, modified, and redistributed freely *//* for ANY purpose, so long as this notice remains intact.              */#define _REENTRANT#include <stdio.h>#include <string.h>#include <stdlib.h> #include <unistd.h>#include <assert.h>#include <errno.h>#include <signal.h>#include <ctype.h> #include <sys/types.h>#include <time.h>#include <sys/stat.h>#ifdef __sparc#include <note.h> /* warlock/locklint */#else#define NOTE(s) #endif#include <dirent.h>#include <fcntl.h>#include <sys/uio.h>#include <thread.h> #include <synch.h>#include "version.h"#include "pmatch.h"#include "debug.h" #define PATH_MAX		1024 /* max # of characters in a path name */#define HOLD_FDS                6  /* stdin,out,err and a buffer */#define UNLIMITED               99999 /* The default tglimit */#define MAXREGEXP               10  /* max number of -e options */#define FB_BLOCK                0x00001#define FC_COUNT                0x00002#define FH_HOLDNAME             0x00004#define FI_IGNCASE              0x00008#define FL_NAMEONLY             0x00010#define FN_NUMBER               0x00020#define FS_NOERROR              0x00040#define FV_REVERSE              0x00080#define FW_WORD                 0x00100#define FR_RECUR                0x00200#define FU_UNSORT               0x00400#define FX_STDIN                0x00800#define TG_BATCH                0x01000#define TG_FILEPAT              0x02000#define FE_REGEXP               0x04000#define FS_STATS                0x08000#define FC_LINE                 0x10000#define TG_PROGRESS             0x20000#define FILET                   1#define DIRT                    2#define ALPHASIZ        	128/* * New data types  */typedef struct work_st {    char                *path;    int                 tp;    struct work_st      *next;} work_t;typedef struct out_st {    char                *line;    int                 line_count;    long                byte_count;    struct out_st       *next;} out_t;typedef struct bm_pattern {     /* Boyer - Moore pattern                */        short            p_m;           /* length of pattern string     */        short            p_r[ALPHASIZ]; /* "r" vector                   */        short           *p_R;           /* "R" vector                   */        char            *p_pat;         /* pattern string               */} BM_PATTERN;/* * Prototypes *//* bmpmatch.c */extern BM_PATTERN *bm_makepat(char *);extern char *bm_pmatch(BM_PATTERN *, register char *);extern void bm_freepat(BM_PATTERN *);/* pmatch.c */extern char *pmatch(register PATTERN *, register char *, int *);extern PATTERN *makepat(char *string, char *);extern void freepat(register PATTERN *);extern void printpat(PATTERN *);#include "proto.h"  /* function prototypes of main.c */void *SigThread(void *arg);void sig_print_stats(void);/* * Global data */BM_PATTERN      *bm_pat;  /* the global target read only after main */NOTE(READ_ONLY_DATA(bm_pat))PATTERN         *pm_pat[MAXREGEXP];  /* global targets read only for pmatch */NOTE(READ_ONLY_DATA(pm_pat))mutex_t global_count_lk; int     global_count = 0;NOTE(MUTEX_PROTECTS_DATA(global_count_lk, global_count))NOTE(DATA_READABLE_WITHOUT_LOCK(global_count))  /* see prnt_stats() */work_t  *work_q = NULL;cond_t  work_q_cv;mutex_t work_q_lk;int     all_done = 0;int     work_cnt = 0;int     current_open_files = 0;int     tglimit = UNLIMITED;    /* if -B limit the number of threads */NOTE(MUTEX_PROTECTS_DATA(work_q_lk, work_q all_done work_cnt \			 current_open_files tglimit))work_t  *search_q = NULL;mutex_t search_q_lk;cond_t  search_q_cv;int     search_pool_cnt = 0;    /* the count in the pool now */int     search_thr_limit = 0;   /* the max in the pool */NOTE(MUTEX_PROTECTS_DATA(search_q_lk, search_q search_pool_cnt))NOTE(DATA_READABLE_WITHOUT_LOCK(search_pool_cnt)) /* see prnt_stats() */NOTE(READ_ONLY_DATA(search_thr_limit))work_t	*cascade_q = NULL;mutex_t cascade_q_lk;cond_t  cascade_q_cv;int	cascade_pool_cnt = 0;int     cascade_thr_limit = 0;NOTE(MUTEX_PROTECTS_DATA(cascade_q_lk, cascade_q cascade_pool_cnt))NOTE(DATA_READABLE_WITHOUT_LOCK(cascade_pool_cnt))  /* see prnt_stats() */NOTE(READ_ONLY_DATA(cascade_thr_limit))int     running = 0;mutex_t running_lk;NOTE(MUTEX_PROTECTS_DATA(running_lk, running))sigset_t set, oldset;NOTE(READ_ONLY_DATA(set oldset))mutex_t stat_lk;time_t  st_start = 0;int     st_dir_search = 0;int     st_file_search = 0;int     st_line_search = 0;int     st_cascade = 0;int	st_cascade_pool = 0;int     st_cascade_destroy = 0;int     st_search = 0;int     st_pool = 0;int     st_maxrun = 0;int     st_worknull = 0;int     st_workfds = 0;int     st_worklimit = 0;int     st_destroy = 0;NOTE(MUTEX_PROTECTS_DATA(stat_lk, st_start st_dir_search st_file_search \			 st_line_search st_cascade st_cascade_pool \			 st_cascade_destroy st_search st_pool st_maxrun \			 st_worknull st_workfds st_worklimit st_destroy))int     progress_offset = 1;NOTE(READ_ONLY_DATA(progress_offset))mutex_t output_print_lk;/* output_print_lk used to print multi-line output only */int     progress = 0;NOTE(MUTEX_PROTECTS_DATA(output_print_lk, progress))unsigned int    flags = 0;int     regexp_cnt = 0;char    *string[MAXREGEXP];int     debug = 0;int     use_pmatch = 0;char    file_pat[255];  /* file patten match */PATTERN *pm_file_pat; /* compiled file target string (pmatch()) */NOTE(READ_ONLY_DATA(flags regexp_cnt string debug use_pmatch \		    file_pat pm_file_pat))/* * Locking ording. */NOTE(LOCK_ORDER(output_print_lk stat_lk))/* * Main: This is where the fun starts */intmain(int argc, char **argv){    int         c,out_thr_flags;    long        max_open_files = 0l, ncpus = 0l;    extern int  optind;    extern char *optarg;    NOTE(READ_ONLY_DATA(optind optarg))    int         prio = 0;    struct stat sbuf;    thread_t    tid,dtid;    void        *status;    char        *e = NULL, *d = NULL; /* for debug flags */    int         debug_file = 0;    int         err = 0, i = 0, pm_file_len = 0;    work_t      *work;    int         restart_cnt = 10;    flags = FR_RECUR;  /* the default */    thr_setprio(thr_self(),127);  /* set me up HIGH */    while ((c = getopt(argc, argv, "d:e:bchilnsvwruf:p:BCSZzHP:")) != EOF) {        switch (c) {#ifdef DEBUG                case 'd':            debug = atoi(optarg);            if (debug == 0)                debug_usage();            d = optarg;            fprintf(stderr,"tgrep: Debug on at level(s) ");            while (*d) {                for (i=0; i<9; i++)                    if (debug_set[i].level == *d) {                        debug_levels |= debug_set[i].flag;                        fprintf(stderr,"%c ",debug_set[i].level);                        break;                    }                d++;            }            fprintf(stderr,"\n");            break;        case 'f': 	    debug_file = atoi(optarg); 	    break;#endif      /* DEBUG */        case 'B':            flags |= TG_BATCH;	    if ((e = getenv("TGLIMIT"))) {		tglimit = atoi(e);	    }	    else {		if (!(flags & FS_NOERROR))  /* order dependent! */		    fprintf(stderr,"env TGLIMIT not set, overriding -B\n");		flags &= ~TG_BATCH;	    }            break;        case 'p':            flags |= TG_FILEPAT;            strcpy(file_pat,optarg);            pm_file_pat = makepat(file_pat,NULL);             break;        case 'P':            flags |= TG_PROGRESS;            progress_offset = atoi(optarg);            break;        case 'S': flags |= FS_STATS;    break;        case 'b': flags |= FB_BLOCK;    break;        case 'c': flags |= FC_COUNT;    break;        case 'h': flags |= FH_HOLDNAME; break;        case 'i': flags |= FI_IGNCASE;  break;        case 'l': flags |= FL_NAMEONLY; break;        case 'n': flags |= FN_NUMBER;   break;        case 's': flags |= FS_NOERROR;  break;        case 'v': flags |= FV_REVERSE;  break;        case 'w': flags |= FW_WORD;     break;        case 'r': flags &= ~FR_RECUR;   break;        case 'C': flags |= FC_LINE;     break;        case 'e':            if (regexp_cnt == MAXREGEXP) {                fprintf(stderr,"Max number of regexp's (%d) exceeded!\n",                        MAXREGEXP);                exit(1);            }            flags |= FE_REGEXP;            if ((string[regexp_cnt] =(char *)malloc(strlen(optarg)+1))==NULL){                fprintf(stderr,"tgrep: No space for search string(s)\n");                exit(1);            }            memset(string[regexp_cnt],0,strlen(optarg)+1);            strcpy(string[regexp_cnt],optarg);             regexp_cnt++;            break;        case 'z':        case 'Z': regexp_usage();            break;        case 'H':        case '?':        default : usage();        }    }    if (!(flags & FE_REGEXP)) {       if (argc - optind < 1) {            fprintf(stderr,"tgrep: Must supply a search string(s) "                    "and file list or directory\n");            usage();        }        if ((string[0]=(char *)malloc(strlen(argv[optind])+1))==NULL){            fprintf(stderr,"tgrep: No space for search string(s)\n");            exit(1);        }        memset(string[0],0,strlen(argv[optind])+1);        strcpy(string[0],argv[optind]);        regexp_cnt=1;        optind++;    }    if (flags & FI_IGNCASE)        for (i=0; i<regexp_cnt; i++)            uncase(string[i]);#ifdef __lock_lint    /*     ** This is NOT somthing you really want to do. This    ** function calls are here ONLY for warlock/locklint !!!    */    pm_pat[i] = makepat(string[i],NULL);    bm_pat = bm_makepat(string[0]);    bm_freepat(bm_pat);  /* stop it from becomming a root */#else    if (flags & FE_REGEXP) {        for (i=0; i<regexp_cnt; i++)            pm_pat[i] = makepat(string[i],NULL);        use_pmatch = 1;    }    else {        bm_pat = bm_makepat(string[0]); /* only one allowed */    }#endif        flags |= FX_STDIN;    max_open_files = sysconf(_SC_OPEN_MAX);    ncpus = sysconf(_SC_NPROCESSORS_ONLN);    if ((max_open_files - HOLD_FDS - debug_file) < 1) {        fprintf(stderr,"tgrep: You MUST have at lest ONE fd "                "that can be used, check limit (>10)\n");        exit(1);    }    search_thr_limit = max_open_files - HOLD_FDS - debug_file;    cascade_thr_limit = search_thr_limit / 2;    /* the number of files that can by open */    current_open_files = search_thr_limit;     mutex_init(&stat_lk,USYNC_THREAD,"stat");    mutex_init(&global_count_lk,USYNC_THREAD,"global_cnt");    mutex_init(&output_print_lk,USYNC_THREAD,"output_print");    mutex_init(&work_q_lk,USYNC_THREAD,"work_q");    mutex_init(&running_lk,USYNC_THREAD,"running");    cond_init(&work_q_cv,USYNC_THREAD,"work_q");    mutex_init(&search_q_lk,USYNC_THREAD,"search_q");    cond_init(&search_q_cv,USYNC_THREAD,"search_q");    mutex_init(&cascade_q_lk,USYNC_THREAD,"cascade_q");    cond_init(&cascade_q_cv,USYNC_THREAD,"cascade_q");    if ((argc == optind) && ((flags & TG_FILEPAT) || (flags & FR_RECUR))) {        add_work(".",DIRT);        flags = (flags & ~FX_STDIN);    }    for ( ; optind < argc; optind++) {        restart_cnt = 10;        flags = (flags & ~FX_STDIN);      STAT_AGAIN:        if (stat(argv[optind], &sbuf)) {            if (errno == EINTR) { /* try again !, restart */                if (--restart_cnt)                    goto STAT_AGAIN;            }            if (!(flags & FS_NOERROR))                fprintf(stderr,"tgrep: Can't stat file/dir %s, %s\n",                         argv[optind], strerror(errno));                     continue;        }        switch (sbuf.st_mode & S_IFMT) {        case S_IFREG :            if (flags & TG_FILEPAT) {                if (pmatch(pm_file_pat, argv[optind], &pm_file_len))		    add_work(argv[optind],FILET);            }            else {                add_work(argv[optind],FILET);            }            break;        case S_IFDIR :            if (flags & FR_RECUR) {                add_work(argv[optind],DIRT);            }            else {                if (!(flags & FS_NOERROR))                    fprintf(stderr,"tgrep: Can't search directory %s, "                            "-r option is on. Directory ignored.\n",                            argv[optind]);            }            break;        }    }     NOTE(COMPETING_THREADS_NOW)  /* we are goinf threaded */    if (flags & FS_STATS) {	mutex_lock(&stat_lk);        st_start = time(NULL);	mutex_unlock(&stat_lk);#ifdef SIGNAL_HAND	/* 	** setup the signal thread so the first call to SIGINT will 	** only print stats, the second will interupt.	*/	sigfillset(&set);	thr_sigsetmask(SIG_SETMASK, &set, &oldset);	if (thr_create(NULL,0,SigThread,NULL,THR_DAEMON,NULL)) {	    thr_sigsetmask(SIG_SETMASK,&oldset,NULL);	    fprintf(stderr,"SIGINT for stats NOT setup\n");	}	thr_yield(); /* give the other thread time */#endif /* SIGNAL_HAND */    }    thr_setconcurrency(3);    if (flags & FX_STDIN) {        fprintf(stderr,"tgrep: stdin option is not coded at this time\n");        exit(0);                        /* XXX Need to fix this SOON */        search_thr(NULL);  /* NULL is not understood in search_thr() */        if (flags & FC_COUNT) {            mutex_lock(&global_count_lk);            printf("%d\n",global_count);            mutex_unlock(&global_count_lk);        }        if (flags & FS_STATS) {	    mutex_lock(&stat_lk);            prnt_stats();	    mutex_unlock(&stat_lk);	}        exit(0);    }    mutex_lock(&work_q_lk);    if (!work_q) {        if (!(flags & FS_NOERROR))            fprintf(stderr,"tgrep: No files to search.\n");        exit(0);    }    mutex_unlock(&work_q_lk);    DP(DLEVEL1,("Starting to loop through the work_q for work\n"));        /* OTHER THREADS ARE RUNNING */    while (1) {        mutex_lock(&work_q_lk);        while ((work_q == NULL || current_open_files == 0 || tglimit <= 0) &&                all_done == 0) {            if (flags & FS_STATS) {                mutex_lock(&stat_lk);                if (work_q == NULL)                    st_worknull++;                if (current_open_files == 0)                    st_workfds++;                if (tglimit <= 0)                    st_worklimit++;                mutex_unlock(&stat_lk);            }            cond_wait(&work_q_cv,&work_q_lk);        }        if (all_done != 0) {            mutex_unlock(&work_q_lk);            DP(DLEVEL1,("All_done was set to TRUE\n"));            goto OUT;        }        work = work_q;        work_q = work->next;  /* maybe NULL */        work->next = NULL;        current_open_files--;        mutex_unlock(&work_q_lk);        tid = 0;        switch (work->tp) {        case DIRT:	    mutex_lock(&cascade_q_lk);	    if (cascade_pool_cnt) {		if (flags & FS_STATS) {                    mutex_lock(&stat_lk);                    st_cascade_pool++;                    mutex_unlock(&stat_lk);                }		work->next = cascade_q;		cascade_q = work;		cond_signal(&cascade_q_cv);                mutex_unlock(&cascade_q_lk);                DP(DLEVEL2,("Sent work to cascade pool thread\n"));	    }	    else {		mutex_unlock(&cascade_q_lk);		err = thr_create(NULL,0,cascade,(void *)work,				 THR_DETACHED|THR_DAEMON|THR_NEW_LWP				 ,&tid);		DP(DLEVEL2,("Sent work to new cascade thread\n"));		thr_setprio(tid,64);  /* set cascade to middle */		if (flags & FS_STATS) {		    mutex_lock(&stat_lk);		    st_cascade++;		    mutex_unlock(&stat_lk);		}	    }            break;        case FILET:            mutex_lock(&search_q_lk);            if (search_pool_cnt) {                if (flags & FS_STATS) {                    mutex_lock(&stat_lk);                    st_pool++;                    mutex_unlock(&stat_lk);                }                work->next = search_q;  /* could be null */                search_q = work;                cond_signal(&search_q_cv);                mutex_unlock(&search_q_lk);                DP(DLEVEL2,("Sent work to search pool thread\n"));            }            else {                mutex_unlock(&search_q_lk);                err = thr_create(NULL,0,search_thr,(void *)work,                                 THR_DETACHED|THR_DAEMON|THR_NEW_LWP                                 ,&tid);                thr_setprio(tid,0);  /* set search to low */                DP(DLEVEL2,("Sent work to new search thread\n"));                if (flags & FS_STATS) {                    mutex_lock(&stat_lk);                    st_search++;                    mutex_unlock(&stat_lk);                }            }            break;        default:            fprintf(stderr,"tgrep: Internal error, work_t->tp no valid\n");            exit(1);        }        if (err) {  /* NEED TO FIX THIS CODE. Exiting is just wrong */            fprintf(stderr,"Cound not create new thread!\n");            exit(1);        }    } OUT:    if (flags & TG_PROGRESS) {        if (progress)            fprintf(stderr,".\n");        else            fprintf(stderr,"\n");    }    /* we are done, print the stuff. All other threads ar parked */    if (flags & FC_COUNT) {        mutex_lock(&global_count_lk);        printf("%d\n",global_count);        mutex_unlock(&global_count_lk);    }    if (flags & FS_STATS)        prnt_stats();    return(0); /* should have a return from main */}/* * Add_Work: Called from the main thread, and cascade threads to add file  * and directory names to the work Q. */intadd_work(char *path,int tp){    work_t      *wt,*ww,*wp;    if ((wt = (work_t *)malloc(sizeof(work_t))) == NULL)        goto ERROR;         if ((wt->path = (char *)malloc(strlen(path)+1)) == NULL)        goto ERROR;        strcpy(wt->path,path);    wt->tp = tp;    wt->next = NULL;    if (flags & FS_STATS) {        mutex_lock(&stat_lk);        if (wt->tp == DIRT)            st_dir_search++;        else            st_file_search++;        mutex_unlock(&stat_lk);    }    mutex_lock(&work_q_lk);    work_cnt++;    wt->next = work_q;    work_q = wt;    cond_signal(&work_q_cv);    mutex_unlock(&work_q_lk);    return(0); ERROR:    if (!(flags & FS_NOERROR))        fprintf(stderr,"tgrep: Could not add %s to work queue. Ignored\n",                path);    return(-1);}/* * Search thread: Started by the main thread when a file name is found * on the work Q to be serached. If all the needed resources are ready * a new search thread will be created. */void *search_thr(void *arg) /* work_t *arg */{        FILE        *fin;    char        fin_buf[(BUFSIZ*4)];  /* 4 Kbytes */    work_t      *wt,std;    int         line_count;    char        rline[128];    char        cline[128];    char        *line;    register char *p,*pp;    int            pm_len;    int         len = 0;    long        byte_count;    long        next_line;    int         show_line;  /* for the -v option */    register int slen,plen,i;    out_t       *out = NULL;    /* this threads output list */    thr_setprio(thr_self(),0);  /* set search to low */    thr_yield();    wt = (work_t *)arg; /* first pass, wt is passed to use. */    /* len = strlen(string);*/  /* only set on first pass */        while (1) {  /* reuse the search threads */        /* init all back to zero */        line_count = 0;        byte_count = 0l;        next_line = 0l;        show_line = 0;        mutex_lock(&running_lk);        running++;        mutex_unlock(&running_lk);        mutex_lock(&work_q_lk);        tglimit--;        mutex_unlock(&work_q_lk);        DP(DLEVEL5,("searching file (STDIO) %s\n",wt->path));        if ((fin = fopen(wt->path,"r")) == NULL) {            if (!(flags & FS_NOERROR)) {                fprintf(stderr,"tgrep: %s. File \"%s\" not searched.\n",                        strerror(errno),wt->path);            }            goto ERROR;        }        setvbuf(fin,fin_buf,_IOFBF,(BUFSIZ*4));  /* XXX */        DP(DLEVEL5,("Search thread has opened file %s\n",wt->path));        while ((fgets(rline,127,fin)) != NULL) {            if (flags & FS_STATS) {                mutex_lock(&stat_lk);                st_line_search++;                mutex_unlock(&stat_lk);            }            slen = strlen(rline);            next_line += slen;              line_count++;            if (rline[slen-1] == '\n')                rline[slen-1] = '\0';            /*            ** If the uncase flag is set, copy the read in line (rline)            ** To the uncase line (cline) Set the line pointer to point at            ** cline.            ** If the case flag is NOT set, then point line at rline.            ** line is what is compared, rline is waht is printed on a             ** match.            */            if (flags & FI_IGNCASE) {                strcpy(cline,rline);                uncase(cline);                line = cline;            }            else {                line = rline;            }            show_line = 1;  /* assume no match, if -v set */            /* The old code removed */            if (use_pmatch) {                for (i=0; i<regexp_cnt; i++) {                    if (pmatch(pm_pat[i], line, &pm_len)) {                        if (!(flags & FV_REVERSE)) {                            add_output_local(&out,wt,line_count,                                             byte_count,rline);                            continue_line(rline,fin,out,wt,                                          &line_count,&byte_count);                        }                        else {                            show_line = 0;                         } /* end of if -v flag if / else block */                        /*                         ** if we get here on ANY of the regexp targets                        ** jump out of the loop, we found a single                        ** match so, do not keep looking!                        ** If name only, do not keep searcthing the same 			** file, we found a single match, so close the file, 			** print the file name and move on to the next file.                        */                        if (flags & FL_NAMEONLY)                             goto OUT_OF_LOOP;                        else                            goto OUT_AND_DONE;                    } /* end found a match if block */                } /* end of the for pat[s] loop */            }            else {                if (bm_pmatch( bm_pat, line)) {                    if (!(flags & FV_REVERSE)) {                        add_output_local(&out,wt,line_count,byte_count,rline);                        continue_line(rline,fin,out,wt,                                      &line_count,&byte_count);                    }                    else {                        show_line = 0;                     }                    if (flags & FL_NAMEONLY)                         goto OUT_OF_LOOP;                }            }          OUT_AND_DONE:            if ((flags & FV_REVERSE) && show_line) {                 add_output_local(&out,wt,line_count,byte_count,rline);                show_line = 0;            }            byte_count = next_line;        }      OUT_OF_LOOP:        fclose(fin);        /*        ** The search part is done, but before we give back the FD,        ** and park this thread in the search thread pool, print the        ** local output we have gathered.        */        print_local_output(out,wt);  /* this also frees out nodes */        out = NULL; /* for the next time around, if there is one */    ERROR:        DP(DLEVEL5,("Search done for %s\n",wt->path));        free(wt->path);        free(wt);        notrun();        mutex_lock(&search_q_lk);        if (search_pool_cnt > search_thr_limit) {            mutex_unlock(&search_q_lk);            DP(DLEVEL5,("Search thread exiting\n"));            if (flags & FS_STATS) {                mutex_lock(&stat_lk);                st_destroy++;                mutex_unlock(&stat_lk);            }            return(0);        }        else {            search_pool_cnt++;            while (!search_q)                cond_wait(&search_q_cv,&search_q_lk);            search_pool_cnt--;            wt = search_q;  /* we have work to do! */            if (search_q->next)                search_q = search_q->next;            else                search_q = NULL;            mutex_unlock(&search_q_lk);        }    }    /*NOTREACHED*/}/* * Continue line: Speacial case search with the -C flag set. If you are  * searching files like Makefiles, some lines may have escape char's to * contine the line on the next line. So the target string can be found, but  * no data is displayed. This function continues to print the escaped line * until there are no more "\" chars found. */intcontinue_line(char *rline, FILE *fin, out_t *out, work_t *wt, 	      int *lc, long *bc){    int len;    int cnt = 0;    char *line;    char nline[128];    if (!(flags & FC_LINE))        return(0);    line = rline;  AGAIN:    len = strlen(line);    if (line[len-1] == '\\') {        if ((fgets(nline,127,fin)) == NULL) {            return(cnt);        }        line = nline;        len = strlen(line);        if (line[len-1] == '\n')            line[len-1] = '\0';        *bc = *bc + len;        *lc++;        add_output_local(&out,wt,*lc,*bc,line);        cnt++;        goto AGAIN;    }    return(cnt);}/* * cascade: This thread is started by the main thread when directory names * are found on the work Q. The thread reads all the new file, and directory * names from the directory it was started when and adds the names to the  * work Q. (it finds more work!) */void *cascade(void *arg)  /* work_t *arg */{    char        fullpath[1025];    int         restart_cnt = 10;    DIR         *dp;    char        dir_buf[sizeof(struct dirent) + PATH_MAX];    struct dirent *dent = (struct dirent *)dir_buf;    struct stat   sbuf;    char        *fpath;    work_t      *wt;    int         fl = 0, dl = 0;    int         pm_file_len = 0;    thr_setprio(thr_self(),64);  /* set search to middle */    thr_yield();  /* try toi give control back to main thread */    wt = (work_t *)arg;    while(1) {	fl = 0;	dl = 0;	restart_cnt = 10;	pm_file_len = 0;	mutex_lock(&running_lk);	running++;	mutex_unlock(&running_lk);	mutex_lock(&work_q_lk);	tglimit--;	mutex_unlock(&work_q_lk);	if (!wt) {	    if (!(flags & FS_NOERROR))		fprintf(stderr,"tgrep: Bad work node passed to cascade\n");	    goto DONE;	}	fpath = (char *)wt->path;	if (!fpath) {	    if (!(flags & FS_NOERROR))		fprintf(stderr,"tgrep: Bad path name passed to cascade\n");	    goto DONE;	}	DP(DLEVEL3,("Cascading on %s\n",fpath));	if (( dp = opendir(fpath)) == NULL) {	    if (!(flags & FS_NOERROR))		fprintf(stderr,"tgrep: Can't open dir %s, %s. Ignored.\n",			fpath,strerror(errno));	    goto DONE;	}	while ((readdir_r(dp,dent)) != NULL) {	    restart_cnt = 10;  /* only try to restart the interupted 10 X */	    	    if (dent->d_name[0] == '.') {		if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') 		    continue;		if (dent->d_name[1] == '\0')		    continue;	    }	    fl = strlen(fpath);	    dl = strlen(dent->d_name);	    if ((fl + 1 + dl) > 1024) {		fprintf(stderr,"tgrep: Path %s/%s is too long. "			"MaxPath = 1024\n",			fpath, dent->d_name);		continue;  /* try the next name in this directory */	    }	    strcpy(fullpath,fpath);	    strcat(fullpath,"/");	    strcat(fullpath,dent->d_name);	  RESTART_STAT:	    if (stat(fullpath,&sbuf)) {		if (errno == EINTR) {		    if (--restart_cnt)			goto RESTART_STAT;		}		if (!(flags & FS_NOERROR))		    fprintf(stderr,"tgrep: Can't stat file/dir %s, %s. "			    "Ignored.\n",			    fullpath,strerror(errno));		goto ERROR;	    }	    switch (sbuf.st_mode & S_IFMT) {	    case S_IFREG :		if (flags & TG_FILEPAT) {		    if (pmatch(pm_file_pat, dent->d_name, &pm_file_len)) {			DP(DLEVEL3,("file pat match (cascade) %s\n",				    dent->d_name));			add_work(fullpath,FILET);		    }		}		else {		    add_work(fullpath,FILET);		    DP(DLEVEL3,("cascade added file (MATCH) %s to Work Q\n",				fullpath));		}		break;	    case S_IFDIR :		DP(DLEVEL3,("cascade added dir %s to Work Q\n",fullpath));		add_work(fullpath,DIRT);		break;	    }	}      ERROR:	closedir(dp);      DONE:	free(wt->path);	free(wt);	notrun();	mutex_lock(&cascade_q_lk);	if (cascade_pool_cnt > cascade_thr_limit) {	    mutex_unlock(&cascade_q_lk);	    DP(DLEVEL5,("Cascade thread exiting\n"));	    if (flags & FS_STATS) {		mutex_lock(&stat_lk);		st_cascade_destroy++;		mutex_unlock(&stat_lk);	    }	    return(0); /* thr_exit */	}	else {	    DP(DLEVEL5,("Cascade thread waiting in pool\n"));	    cascade_pool_cnt++;	    while (!cascade_q)		cond_wait(&cascade_q_cv,&cascade_q_lk);	    cascade_pool_cnt--;	    wt = cascade_q;  /* we have work to do! */	    if (cascade_q->next)		cascade_q = cascade_q->next;	    else		cascade_q = NULL;	    mutex_unlock(&cascade_q_lk);	}    }    /*NOTREACHED*/}/* * Print Local Output: Called by the search thread after it is done searching * a single file. If any oputput was saved (matching lines), the lines are  * displayed as a group on stdout.  */intprint_local_output(out_t *out, work_t *wt){    out_t       *pp, *op;    int         out_count = 0;    int         printed = 0;    int 	print_name = 1;    pp = out;    mutex_lock(&output_print_lk);    if (pp && (flags & TG_PROGRESS)) {        progress++;        if (progress >= progress_offset) {            progress = 0;            fprintf(stderr,".");        }    }    while (pp) {	out_count++;	if (!(flags & FC_COUNT)) {	    if (flags & FL_NAMEONLY) {  /* Pint name ONLY ! */		if (!printed) {		    printed = 1;		    printf("%s\n",wt->path);		}	    }	    else {  /* We are printing more then just the name */		if (!(flags & FH_HOLDNAME))  /* do not print name ? */		    printf("%s :",wt->path);		if (flags & FB_BLOCK)		    printf("%ld:",pp->byte_count/512+1);		if (flags & FN_NUMBER)		    printf("%d:",pp->line_count);		printf("%s\n",pp->line);	    }	}        op = pp;        pp = pp->next;        /* free the nodes as we go down the list */        free(op->line);        free(op);    }    mutex_unlock(&output_print_lk);    mutex_lock(&global_count_lk);    global_count += out_count;    mutex_unlock(&global_count_lk);    return(0);}/* * add output local: is called by a search thread as it finds matching lines.  * the matching line, it's byte offset, line count, etc are stored until the * search thread is done searching the file, then the lines are printed as  * a group. This way the lines from more then a single file are not mixed * together. */intadd_output_local(out_t **out, work_t *wt,int lc, long bc, char *line){    out_t       *ot,*oo, *op;    if (( ot = (out_t *)malloc(sizeof(out_t))) == NULL)        goto ERROR;    if (( ot->line = (char *)malloc(strlen(line)+1)) == NULL)        goto ERROR;    strcpy(ot->line,line);    ot->line_count = lc;    ot->byte_count = bc;        if (!*out) {        *out = ot;        ot->next = NULL;        return(0);    }    /* append to the END of the list, keep things sorted! */    op = oo = *out;        while(oo) {        op = oo;        oo = oo->next;    }    op->next = ot;    ot->next = NULL;    return(0); ERROR:    if (!(flags & FS_NOERROR))        fprintf(stderr,"tgrep: Output lost. No space. "                "[%s: line %d byte %d match : %s\n",                wt->path,lc,bc,line);    return(1);}/* * print stats: If the -S flag is set, after ALL files have been searched,  * main thread calls this function to print the stats it keeps on how the * search went. */voidprnt_stats(void){    float a,b,c;    float t = 0.0;    time_t  st_end = 0;    char    tl[80];    st_end = time(NULL); /* stop the clock */    fprintf(stderr,"\n----------------- Tgrep Stats. --------------------\n");    fprintf(stderr,"Number of directories searched:           %d\n",	    st_dir_search);    fprintf(stderr,"Number of files searched:                 %d\n",	    st_file_search);    c = (float)(st_dir_search + st_file_search) / (float)(st_end - st_start);    fprintf(stderr,"Dir/files per second:                     %3.2f\n",	    c);    fprintf(stderr,"Number of lines searched:                 %d\n",	    st_line_search);    fprintf(stderr,"Number of matching lines to target:       %d\n",	    global_count);    fprintf(stderr,"Number of cascade threads created:        %d\n",	    st_cascade);    fprintf(stderr,"Number of cascade threads from pool:      %d\n",	    st_cascade_pool);    a = st_cascade_pool; b = st_dir_search;    fprintf(stderr,"Cascade thread pool hit rate:             %3.2f%%\n",	    ((a/b)*100));    fprintf(stderr,"Cascade pool overall size:                %d\n",	    cascade_pool_cnt);    fprintf(stderr,"Cascade pool size limit:                  %d\n",	    cascade_thr_limit);    fprintf(stderr,"Number of cascade threads destroyed:      %d\n",	    st_cascade_destroy);    fprintf(stderr,"Number of search threads created:         %d\n",	    st_search);    fprintf(stderr,"Number of search threads from pool:       %d\n",	    st_pool);    a = st_pool; b = st_file_search;    fprintf(stderr,"Search thread pool hit rate:              %3.2f%%\n",	    ((a/b)*100));    fprintf(stderr,"Search pool overall size:                 %d\n",	    search_pool_cnt);    fprintf(stderr,"Search pool size limit:                   %d\n",	    search_thr_limit);    fprintf(stderr,"Number of search threads destroyed:       %d\n",	    st_destroy);    fprintf(stderr,"Max # of threads running concurrenly:     %d\n",	    st_maxrun);    fprintf(stderr,"Total run time, in seconds.               %d\n",           (st_end - st_start));    /* Why did we wait ? */    a = st_workfds; b = st_dir_search+st_file_search;    c = (a/b)*100; t += c;    fprintf(stderr,"Work stopped due to no FD's:  (%.3d)       %d Times, %3.2f%%\n",           search_thr_limit,st_workfds,c);    a = st_worknull; b = st_dir_search+st_file_search;    c = (a/b)*100; t += c;    fprintf(stderr,"Work stopped due to no work on Q:         %d Times, %3.2f%%\n",           st_worknull,c);#ifndef __lock_lint  /* it is OK to read HERE with out the lock ! */    if (tglimit == UNLIMITED)        strcpy(tl,"Unlimited");    else        sprintf(tl,"   %.3d   ",tglimit);#endif    a = st_worklimit; b = st_dir_search+st_file_search;    c = (a/b)*100; t += c;    fprintf(stderr,"Work stopped due to TGLIMIT:  (%.9s) %d Times, %3.2f%%\n",           tl,st_worklimit,c);    fprintf(stderr,"Work continued to be handed out:          %3.2f%%\n",	    100.00-t);    fprintf(stderr,"----------------------------------------------------\n");}/* * not running: A glue function to track if any search threads or cascade  * threads are running. When the count is zero, and the work Q is NULL, * we can safly say, WE ARE DONE. */void notrun (void){    mutex_lock(&work_q_lk);    work_cnt--;    tglimit++;    current_open_files++;    mutex_lock(&running_lk);    if (flags & FS_STATS) {        mutex_lock(&stat_lk);        if (running > st_maxrun) {            st_maxrun = running;	    DP(DLEVEL6,("Max Running has increased to %d\n",st_maxrun));	}        mutex_unlock(&stat_lk);    }    running--;    if (work_cnt == 0 && running == 0) {        all_done = 1;        DP(DLEVEL6,("Setting ALL_DONE flag to TRUE.\n"));    }    mutex_unlock(&running_lk);    cond_signal(&work_q_cv);    mutex_unlock(&work_q_lk);}/* * uncase: A glue function. If the -i (case insensitive) flag is set, the * target strng and the read in line is converted to lower case before * comparing them. */voiduncase(char *s){    char        *p;    for (p = s; *p != NULL; p++)        *p = (char)tolower(*p);}/* * SigThread: if the -S option is set, the first ^C set to tgrep will * print the stats on the fly, the second will kill the process. */void *SigThread(void *arg){    int sig;    int stats_printed = 0;    while (1) {	sig = sigwait(&set);	DP(DLEVEL7,("Signal %d caught\n",sig));	switch (sig) {	case -1:	    fprintf(stderr,"Signal error\n");	    break;	case SIGINT:	    if (stats_printed)		exit(1);	    stats_printed = 1;	    sig_print_stats();	    break;	case SIGHUP:	    sig_print_stats();	    break;	default:	    DP(DLEVEL7,("Default action taken (exit) for signal %d\n",sig));	    exit(1);  /* default action */	}    }}voidsig_print_stats(void){    /*    ** Get the output lock first    ** Then get the stat lock.    */    mutex_lock(&output_print_lk);    mutex_lock(&stat_lk);    prnt_stats();    mutex_unlock(&stat_lk);    mutex_unlock(&output_print_lk);    return;}/* * usage: Have to have one of these. */void usage(void){    fprintf(stderr,"usage: tgrep <options> pattern <{file,dir}>...\n");    fprintf(stderr,"\n");    fprintf(stderr,"Where:\n");#ifdef DEBUG        fprintf(stderr,"Debug     -d = debug level -d <levels> (-d0 for usage)\n");    fprintf(stderr,"Debug     -f = block fd's from use (-f #)\n");#endif        fprintf(stderr,"          -b = show block count (512 byte block)\n");    fprintf(stderr,"          -c = print only a line count\n");    fprintf(stderr,"          -h = do not print file names\n");    fprintf(stderr,"          -i = case insensitive\n");    fprintf(stderr,"          -l = print file name only\n");    fprintf(stderr,"          -n = print the line number with the line\n");    fprintf(stderr,"          -s = Suppress error messages\n");    fprintf(stderr,"          -v = print all but matching lines\n");#ifdef NOT_IMP        fprintf(stderr,"          -w = search for a \"word\"\n");#endif        fprintf(stderr,"          -r = Do not search for files in all "                                "sub-directories\n");    fprintf(stderr,"          -C = show continued lines (\"\\\")\n");    fprintf(stderr,"          -p = File name regexp pattern. (Quote it)\n");    fprintf(stderr,"          -P = show progress. -P 1 prints a DOT on stderr\n"                   "               for each file it finds, -P 10 prints a DOT\n"                   "               on stderr for each 10 files it finds, etc...\n");    fprintf(stderr,"          -e = expression search.(regexp) More then one\n");    fprintf(stderr,"          -B = limit the number of threads to TGLIMIT\n");    fprintf(stderr,"          -S = Print thread stats when done.\n");    fprintf(stderr,"          -Z = Print help on the regexp used.\n");    fprintf(stderr,"\n");    fprintf(stderr,"Notes:\n");    fprintf(stderr,"      If you start tgrep with only a directory name\n");    fprintf(stderr,"      and no file names, you must not have the -r option\n");    fprintf(stderr,"      set or you will get no output.\n");    fprintf(stderr,"      To search stdin (piped input), you must set -r\n");    fprintf(stderr,"      Tgrep will search ALL files in ALL \n");    fprintf(stderr,"      sub-directories. (like */* */*/* */*/*/* etc..)\n");    fprintf(stderr,"      if you supply a directory name.\n");    fprintf(stderr,"      If you do not supply a file, or directory name,\n");    fprintf(stderr,"      and the -r option is not set, the current \n");    fprintf(stderr,"      directory \".\" will be used.\n");    fprintf(stderr,"      All the other options should work \"like\" grep\n");    fprintf(stderr,"      The -p patten is regexp, tgrep will search only\n");    fprintf(stderr,"      the file names that match the patten\n");    fprintf(stderr,"\n");    fprintf(stderr,"      Tgrep Version %s\n",Tgrep_Version);    fprintf(stderr,"\n");    fprintf(stderr,"      Copy Right By Ron Winacott, 1993-1995.\n");    fprintf(stderr,"\n");    exit(0);}/* * regexp usage: Tell the world about tgrep custom (THREAD SAFE) regexp! */int regexp_usage (void){    fprintf(stderr,"usage: tgrep <options> -e \"pattern\" <-e ...> "	    "<{file,dir}>...\n");    fprintf(stderr,"\n");    fprintf(stderr,"metachars:\n");    fprintf(stderr,"    . - match any character\n");    fprintf(stderr,"    * - match 0 or more occurrences of pervious char\n");    fprintf(stderr,"    + - match 1 or more occurrences of pervious char.\n");    fprintf(stderr,"    ^ - match at begining of string\n");    fprintf(stderr,"    $ - match end of string\n");    fprintf(stderr,"    [ - start of character class\n");    fprintf(stderr,"    ] - end of character class\n");    fprintf(stderr,"    ( - start of a new pattern\n");    fprintf(stderr,"    ) - end of a new pattern\n");    fprintf(stderr,"    @(n)c - match <c> at column <n>\n");    fprintf(stderr,"    | - match either pattern\n");    fprintf(stderr,"    \\ - escape any special characters\n");    fprintf(stderr,"    \\c - escape any special characters\n");    fprintf(stderr,"    \\o - turn on any special characters\n");    fprintf(stderr,"\n");    fprintf(stderr,"To match two diffrerent patterns in the same command\n");    fprintf(stderr,"Use the or function. \n"            "ie: tgrep -e \"(pat1)|(pat2)\" file\n"            "This will match any line with \"pat1\" or \"pat2\" in it.\n");    fprintf(stderr,"You can also use up to %d -e expresions\n",MAXREGEXP);    fprintf(stderr,"RegExp Pattern matching brought to you by Marc Staveley\n");    exit(0);}/* * debug usage: If compiled with -DDEBUG, turn it on, and tell the world * how to get tgrep to print debug info on different threads. */#ifdef DEBUGvoiddebug_usage(void){    int i = 0;    fprintf(stderr,"DEBUG usage and levels:\n");    fprintf(stderr,"--------------------------------------------------\n");    fprintf(stderr,"Level                   code\n");    fprintf(stderr,"--------------------------------------------------\n");    fprintf(stderr,"0                 This message.\n");    for (i=0; i<9; i++) {        fprintf(stderr,"%d                 %s\n",i+1,debug_set[i].name);    }    fprintf(stderr,"--------------------------------------------------\n");    fprintf(stderr,"You can or the levels together like -d134 for levels\n");    fprintf(stderr,"1 and 3 and 4.\n");    fprintf(stderr,"\n");    exit(0);}#endif

Multithreaded Quicksort

The following example tquick.cimplements the quicksort algorithm using threads.

/**  Multithreaded Demo Source* *  Copyright (C) 1995 by Sun Microsystems, Inc.*  All rights reserved.* *  This file is a product of SunSoft, Inc. and is provided for*  unrestricted use provided that this legend is included on all*  media and as a part of the software program in whole or part.*  Users may copy, modify or distribute this file at will.* *  THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING*  THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR*  PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.* *  This file is provided with no support and without any obligation on the*  part of SunSoft, Inc. to assist in its use, correction, modification or*  enhancement.* *  SUNSOFT AND SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT*  TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS*  FILE OR ANY PART THEREOF.* *  IN NO EVENT WILL SUNSOFT OR SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY*  LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL*  DAMAGES, EVEN IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH*  DAMAGES.* *  SunSoft, Inc.*  2550 Garcia Avenue*  Mountain View, California  94043*//* * multiple-thread quick-sort.  See man page for qsort(3c) for info. * Works fine on uniprocessor machines as well. * *  Written by:  Richard Pettit (Richard.Pettit@West.Sun.COM) */#include <unistd.h>#include <stdlib.h>#include <thread.h>/* don't create more threads for less than this */#define SLICE_THRESH   4096/* how many threads per lwp */#define THR_PER_LWP       4/* cast the void to a one byte quanitity and compute the offset */#define SUB(a, n)      ((void *) (((unsigned char *) (a)) + ((n) * width)))typedef struct {  void    *sa_base;  int      sa_nel;  size_t   sa_width;  int    (*sa_compar)(const void *, const void *);} sort_args_t;/* for all instances of quicksort */static int threads_avail;#define SWAP(a, i, j, width) \{ \  int n; \  unsigned char uc; \  unsigned short us; \  unsigned long ul; \  unsigned long long ull; \ \  if (SUB(a, i) == pivot) \    pivot = SUB(a, j); \  else if (SUB(a, j) == pivot) \    pivot = SUB(a, i); \ \  /* one of the more convoluted swaps I've done */ \  switch(width) { \  case 1: \    uc = *((unsigned char *) SUB(a, i)); \    *((unsigned char *) SUB(a, i)) = *((unsigned char *) SUB(a, j)); \    *((unsigned char *) SUB(a, j)) = uc; \    break; \  case 2: \    us = *((unsigned short *) SUB(a, i)); \    *((unsigned short *) SUB(a, i)) = *((unsigned short *) SUB(a, j)); \    *((unsigned short *) SUB(a, j)) = us; \    break; \  case 4: \    ul = *((unsigned long *) SUB(a, i)); \    *((unsigned long *) SUB(a, i)) = *((unsigned long *) SUB(a, j)); \    *((unsigned long *) SUB(a, j)) = ul; \    break; \  case 8: \    ull = *((unsigned long long *) SUB(a, i)); \    *((unsigned long long *) SUB(a,i)) = *((unsigned long long *) SUB(a,j)); \    *((unsigned long long *) SUB(a, j)) = ull; \    break; \  default: \    for(n=0; n<width; n++) { \      uc = ((unsigned char *) SUB(a, i))[n]; \      ((unsigned char *) SUB(a, i))[n] = ((unsigned char *) SUB(a, j))[n]; \      ((unsigned char *) SUB(a, j))[n] = uc; \    } \    break; \  } \}static void *_quicksort(void *arg){  sort_args_t *sargs = (sort_args_t *) arg;  register void *a = sargs->sa_base;  int n = sargs->sa_nel;  int width = sargs->sa_width;  int (*compar)(const void *, const void *) = sargs->sa_compar;  register int i;  register int j;  int z;  int thread_count = 0;  void *t;  void *b[3];  void *pivot = 0;  sort_args_t sort_args[2];  thread_t tid;  /* find the pivot point */  switch(n) {  case 0:  case 1:    return 0;  case 2:    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {      SWAP(a, 0, 1, width);    }    return 0;  case 3:    /* three sort */    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {      SWAP(a, 0, 1, width);    }    /* the first two are now ordered, now order the second two */    if ((*compar)(SUB(a, 2), SUB(a, 1)) < 0) {      SWAP(a, 2, 1, width);    }    /* should the second be moved to the first? */    if ((*compar)(SUB(a, 1), SUB(a, 0)) < 0) {      SWAP(a, 1, 0, width);    }    return 0;  default:    if (n > 3) {      b[0] = SUB(a, 0);      b[1] = SUB(a, n / 2);      b[2] = SUB(a, n - 1);      /* three sort */      if ((*compar)(b[0], b[1]) > 0) {        t = b[0];        b[0] = b[1];        b[1] = t;      }      /* the first two are now ordered, now order the second two */      if ((*compar)(b[2], b[1]) < 0) {        t = b[1];        b[1] = b[2];        b[2] = t;      }      /* should the second be moved to the first? */      if ((*compar)(b[1], b[0]) < 0) {        t = b[0];        b[0] = b[1];        b[1] = t;      }      if ((*compar)(b[0], b[2]) != 0)        if ((*compar)(b[0], b[1]) < 0)          pivot = b[1];        else          pivot = b[2];    }    break;  }  if (pivot == 0)    for(i=1; i<n; i++) {      if (z = (*compar)(SUB(a, 0), SUB(a, i))) {        pivot = (z > 0) ? SUB(a, 0) : SUB(a, i);        break;      }    }  if (pivot == 0)    return;  /* sort */  i = 0;  j = n - 1;  while(i <= j) {    while((*compar)(SUB(a, i), pivot) < 0)      ++i;    while((*compar)(SUB(a, j), pivot) >= 0)      --j;    if (i < j) {      SWAP(a, i, j, width);      ++i;      --j;    }  }  /* sort the sides judiciously */  switch(i) {  case 0:  case 1:    break;  case 2:    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {      SWAP(a, 0, 1, width);    }    break;  case 3:    /* three sort */    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {      SWAP(a, 0, 1, width);    }    /* the first two are now ordered, now order the second two */    if ((*compar)(SUB(a, 2), SUB(a, 1)) < 0) {      SWAP(a, 2, 1, width);    }    /* should the second be moved to the first? */    if ((*compar)(SUB(a, 1), SUB(a, 0)) < 0) {      SWAP(a, 1, 0, width);    }    break;  default:    sort_args[0].sa_base          = a;    sort_args[0].sa_nel           = i;    sort_args[0].sa_width         = width;    sort_args[0].sa_compar        = compar;    if ((threads_avail > 0) && (i > SLICE_THRESH)) {      threads_avail--;      thr_create(0, 0, _quicksort, &sort_args[0], 0, &tid);      thread_count = 1;    } else      _quicksort(&sort_args[0]);    break;  }  j = n - i;  switch(j) {  case 1:    break;  case 2:    if ((*compar)(SUB(a, i), SUB(a, i + 1)) > 0) {      SWAP(a, i, i + 1, width);    }    break;  case 3:    /* three sort */    if ((*compar)(SUB(a, i), SUB(a, i + 1)) > 0) {      SWAP(a, i, i + 1, width);    }    /* the first two are now ordered, now order the second two */    if ((*compar)(SUB(a, i + 2), SUB(a, i + 1)) < 0) {      SWAP(a, i + 2, i + 1, width);    }    /* should the second be moved to the first? */    if ((*compar)(SUB(a, i + 1), SUB(a, i)) < 0) {      SWAP(a, i + 1, i, width);    }    break;  default:    sort_args[1].sa_base          = SUB(a, i);    sort_args[1].sa_nel           = j;    sort_args[1].sa_width         = width;    sort_args[1].sa_compar        = compar;    if ((thread_count == 0) && (threads_avail > 0) && (i > SLICE_THRESH)) {      threads_avail--;      thr_create(0, 0, _quicksort, &sort_args[1], 0, &tid);      thread_count = 1;    } else      _quicksort(&sort_args[1]);    break;  }  if (thread_count) {    thr_join(tid, 0, 0);    threads_avail++;  }  return 0;}voidquicksort(void *a, size_t n, size_t width,          int (*compar)(const void *, const void *)){  static int ncpus = -1;  sort_args_t sort_args;  if (ncpus == -1) {    ncpus = sysconf( _SC_NPROCESSORS_ONLN);    /* lwp for each cpu */    if ((ncpus > 1) && (thr_getconcurrency() < ncpus))      thr_setconcurrency(ncpus);    /* thread count not to exceed THR_PER_LWP per lwp */    threads_avail = (ncpus == 1) ? 0 : (ncpus * THR_PER_LWP);  }  sort_args.sa_base = a;  sort_args.sa_nel = n;  sort_args.sa_width = width;  sort_args.sa_compar = compar;  (void) _quicksort(&sort_args);}



Dave Marshall
1/5/1999

'기본 카테고리' 카테고리의 다른 글

[리눅스마스터 1급 2차] 과년도 문제 분석서  (0) 2011.04.11
어느 멋진 벽화????  (0) 2010.12.02
컴퓨터 자동종료 설정하기  (0) 2010.10.15
오라클 AQ (advanced queuing)  (0) 2010.10.15
SQL 로더 사용법  (0) 2010.10.12

S/W 제어
윈도우 : 시작 -> 실행 -> 열기.
열기 부분에 보면 키보드 입력이 가능하다. 이 곳에 shutdown -s -t 3600 을 입력하면 PC는 1시간 후에 컴퓨터가 자동 종료한다. 3600은 초를 나타내는 시간이다. 7200을 치면 2시간 동안 PC를 사용할 수 있다.

'기본 카테고리' 카테고리의 다른 글

어느 멋진 벽화????  (0) 2010.12.02
유닉스 쓰레드 프로그래밍  (0) 2010.11.15
오라클 AQ (advanced queuing)  (0) 2010.10.15
SQL 로더 사용법  (0) 2010.10.12
소스인사이트 단축키(2)  (0) 2010.10.11

+ Recent posts