« Previous : 1 : ... 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : ... 33 : Next »


출처 : 미래에셋미디어  2007-11-21
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 

▣재테크 계획서를 작성하는 이유

 

재테크의 시작! 그것은 계획입니다. 정해진 목표로 걷기 위해서는 목표물을 쳐다보고 걸어야 하듯, 목표를 제대로 세우면 방황하지 아니하고 정해진 목표에 빨리 도달할 수 있게 됩니다. 계획은 나침반이며, 지도입니다.

 

또한 계획서는 가족 또는 부부 간 올바른 재테크를 위한 대화 수단이 됩니다. 가계의 재테크에 무엇이 허점이 있는지, 서로가 노력하였는지를 평가할 수 있는 대화의 수단이 됩니다. 재테크라는 긴 여행을 떠나실 분 계획부터 제대로 세우시길 바랍니다. 아래의 이미지와 첨부된 엑셀 파일은 제가 직접 작성한 2008년도 재테크 계획서입니다. 제가 아래에 알려드리는 계획서 작성 원칙에 맞춰서 나름대로 자신에게 적합한 재테크 계획을 세워보시길 바랍니다.

 

▣계획서 작성 요령 및 원칙

 

재테크에 대한 계획서를 작성해야 하는 이유에 대해 이해를 하셨다면 이제 문서를 작성해 볼 차례입니다. 재테크 계획서는 엑셀, 워드, 파워포인트 등의 다양한 문서 폼을 이용하여 작성할 수 있습니다. 제가 올린 샘플은 엑셀 폼으로 만든 것인데, 항목의 추가 및 삭제가 편리하기 때문에 엑셀 폼을 사용한 것입니다. 본인이 가장 잘 다루는 문서 폼을 이용하시는 게 가장 적합합니다.

 

또한 재테크의 목표도 자신의 상황(재무 상황, 직업 등의 조건)에 맞게 추가하거나 삭제하여야 합니다. 아래에서 제시된 항목은 제 개인이 판단할 때 재테크를 하는데 있어서 가장 필요한 항목들을 정리한 것이며, 재테크 뿐 만 아니라 직업 등의 다방면에 걸쳐서 목표를 잡은 것입니다.

 

제일 중요한 것은 목표를 잡을 때는 가급적 정량적 목표를 잡아야 합니다.

정량적인 목표, 즉 측정 가능한 목표를 잡아야 하는 이유는 계획이 단순한 계획으로 머무르지 않도록 하기 위해서입니다. 목표가 뚜렷하고 명확해야 일정 시기가 지났을 때 그 목표를 제대로 성취하였는지 확인하고 평가할 수 있습니다. 단순히 재테크를 열심히 하자라거나 부동산에 관심을 가지자 등의 불명확한 목표는 어떤 행동을 해야 할지, 내가 제대로 한 것인지 판단하기가 애매모호합니다. 애매모호한 평가는 결국 계획 자체를 무의미하게 만듭니다.

 

또한 목표를 잡는 것은 항상 현실에 기반을 두어야 합니다.

누구나 부자를 꿈꿉니다. 또한 재테크를 하는 누구라도 재테크를 잘 하고 싶어합니다. 허나, 개인의 재무적인 여건이나 재테크 지식 및 노력 여하에 따라 그 결과는 크게 차이가 납니다. 남들이 쉽게 하는 일이라 하더라도 나 자신에게는 무척 버거운 일 일수 있습니다. 자신의 능력을 과대평가하여 무리한 목표를 잡아서 조기에 포기하는 실수를 범하지 않기를 바랍니다. 목표는 항상 내가 최선을 다했을 때 실행 가능한 범위 내에서 잡아야 합니다.

 

▣재무부문의 목표 세우기

 

돈과 직접적으로 관련된 목표입니다. 년 말 자산/부채/순자산의 금액을 정확히 파악하여 자신이 재테크를 통해 성취 가능한 증감 목표를 세웁니다. 돈과 관련되어 있는 목표이므로 쉽게 정량적으로 측정 가능합니다. 그렇지만, 측정이 가능하다 하여 무리하게 잡으면 안됩니다. 자산을 두 배로 늘린다거나 몇 배로 늘리는 것과 같은 무리한 목표는 늘 무리한 투자를 수반하게 되며, 무리수는 항상 위험을 초래하게 됩니다. 오히려 자산의 손실을 가져올 수 있습니다.

 

투자를 하는데 있어서 보수적이라면 수익률을 금리 수준보다 약간 높게 잡고, 적극적이라면 금리수준보다 2~3배 정도 높게 잡아서 포트폴리오를 구성하시길 바랍니다. 수익률을 높게 잡는다면 주식 또는 펀드 등의 비중을 높게 잡아야 합니다. 이렇게 잡은 수익률을 주위에 투자 경험이 많은 지인들에게 한번쯤 검증 받는 것도 좋은 방법이 됩니다. 이렇게 잡은 수익률에 적합한 각종 투자처(·적금 또는 펀드 등)를 찾아 적합한 상품을 찾아서 가입합니다.

 

부채가 있는 경우 부채의 감소 목표를 세웁니다. 이 또한 자산을 늘리는 것만큼이나 중요한 목표라 할 수 있습니다. 또한, 가계부 쓰는 습관이 있으신 분은 지출 중 고정 지출(식비, 월세, 통신비 등) 감소 목표나 변동 지출(문화·여가비, 의류비 등)을 낮추는 계획을 세워 이를 실천하는 것도 가계부와 함께 쓸 수 있는 재테크 방법 중의 하나가 될 것입니다. 그 외에 직장인은 연봉 협상에 대한 목표를 세우거나 투자처의 매도 수익을 목표로 잡는 것도 방법이 됩니다. 각자의 여건에 맞게 재무적 목표를 세우고 이를 실천할 수 있는 방안을 구체적으로 세우시길 바랍니다.

 

▣비재무부문의 목표 세우기

 

재테크가 주로 돈에 대해 이야기를 하지만 재테크를 위한 기반을 다지는 것 또한 급하지는 않지만 중요한 부분이라 할 수 있습니다. 재테크에 대한 지식을 쌓는다거나 재테크에 좋은 습관을 쌓는 것이 한 예라 할 수 있습니다. 급하지는 않으나 중요하여 후에 재테크에 대해 직접적으로 좋은 결과만을 가져올 뿐만 아니라 생활 전반에 걸쳐 균형 잡힌 생활을 유지할 수 있습니다.

 

비재무적인 재테크 목표라 하더라도 가급적 정량적으로 세우는 것이 좋습니다. “재테크 강연회 참석하기보다는 1회 주식투자 관련 재테크 강연회 참석등과 같이 월말에 평가 가능한 목표를 세우는 것이 좋습니다. 또한, “주식투자 중급 책 2권 정독등의 목표가 보다 뚜렷한 목표의 예라 할 수 있습니다.

 

그 외, 건강, 문화 등 재테크와 직접적으로 연관되어 있지는 않으나 가족 간의 애정과 사랑을 충만하게 할 수 있는 목표 또한 추가하여 실천하는 것이 장기적으로 행복한 삶의 기반을 마련할 수 있다고 생각합니다.

 

▣자기보상계획 세우기

 

목표 뒤에는 성취와 실패라는 결과가 따르게 됩니다. 실패에는 반드시 반성이 필요한 것이며, 성취에는 또한 보상이 필요합니다. 자신이 세운 목표를 성취하는 것에 대해 보상이 왜 필요하냐 라고 반문하실 분도 계실 것이고, 보상이 금전적으로 이루어진다면 그렇게 보상해주다가 재테크가 원점에 돌아오는 것은 아니냐라는 의구심이 들 수 있을 것입니다.

 

허나 여러분이 세운 목표를 내년 연말에 돌아보십시오. 쉽게 세운 목표라 하더라도 성취하기가 얼마나 어려운 지를 느끼게 될 것입니다. 그렇기에 보상은 나름의 가치가 있습니다. 특히나 재테크 초보일수록 자신을 다스리는 채찍과 더불어 격려할 수 있는 자극제로서 보상을 잘 활용하시길 바랍니다. 목표가 구체적일수록 그 결과를 측정하기 쉽고, 그 결과에 따라 보상을 책정하기도 쉽습니다.

 

이때 보상은 가급적 개인이 좋아하는 취미나 선호하는 항목으로 잡는 게 좋습니다. 불필요한 지출을 줄일 수 있을 뿐더러, 보상을 받을 경우 스스로에 대한 성취감도 느낄 수 있는 기회가 됩니다.

 

▣주요 이정표의 정리

 

재무/비재무 목표와 이에 대한 실행방안, 그리고 자기보상 계획을 세웠다면 시기 별로 중요한 이정표를 정리할 필요가 있습니다. 결혼, 이사 등 재무에 중요한 영향을 줄 수 있는 이정표를 정리함으로써 개인과 가정이 해당 시점에 자금을 어떻게 운용할지를 구체적으로 확인할 수 있습니다.

 

계획이 중요하냐 또는 실천이 중요하냐라는 논쟁이 있습니다. 허나 답은 계획도 중요하고 실천도 중요하다라고 생각합니다. 둘 중에 하나가 빠진다면 아무 의미가 없습니다. 좋은 계획과 끈기 있는 실천으로 내년 연말에는 2009년의 계획을 보다 더 구체적으로 세우길 기원합니다.

 

사용자 삽입 이미지

 

출처: http://myblog.moneta.co.kr/memories4u     별사탕과건빵

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 

봐야할 명화

2007/12/05 00:58 / Flavor

아무도 모른다 (誰も知らない: Nobody Knows, 2004)
일본
감독 : 고레에다 히로카즈
출연 : Yuya Yagira, Ayu Kitaura, Hiei Kimura

<원더풀라이프(ワンダフルライフ, 1998)> <디스턴스(ディスタンス, 2001)>의 코레에다 히로카즈(是枝裕和) 감독이 1988년 실제로 일어났던 사건을 소재로 영화화한 인간 드라마. 어머니가 버리고 간 4명의 아이들이 어른들 몰래 남매들끼리 살아가는 모습을 담담하게 그리고 있다. 2004년 제57회 칸국제영화제 경쟁 부문에 출품되어 장남을 연기한 야기라 유야(柳樂優彌)가 일본인 최초로, 또 칸영화제 사상 최연소로 (최민식을 제치고)남우주연상을 수상해 큰 화제가 되었다.




리틀 킹 (King Of The Hill, 1993)
미국 / 103분
감독 : 스티븐 소더버그
출연 : 예로엔 크라베, 리사 에이크혼, 카렌 알렌, 스팰딩 그레이

궁핍하지만 단란한 삶을 꾸려가던 아론(Aaron Kurlander: 제시 브래포드 분)의 가족들이 어느날 뿔뿔이 흩어지게 된다. 생활고를 견디다못한 부모는 아론의 동생 셜리반(Sullivan: 카메론 보이드 분)을 삼촌댁에 보내게 되고, 지병인 폐결핵으로 상태가 악화된 어머니(Mrs. Kurlander: 리자 에이치혼 분)는 요양소로, 시계 회사영업사원으로 취직이 된 아버지(Mr. Kurlander: 제로엔 크라베 분)는 이도시 저도시를 전전하게 된다. 그후 혼자 남겨진 아론에게 생계마져 막막한 상황이 닥쳐오고...

30년대 공황기의 미국을 배경으로 어느 고립된 유대소년의 꿋꿋한 삶을 그린 작품으로, 특히 1930년대 초반 공황기에 대한 묘사가 영화사상 가장 생생하다는 평가를 받았다.




뽀네뜨 (Ponette, 1996)
프랑스 / 1997.11.08 / 가족,드라마 / 97분
감독 : Jacques Doillon
출연 : 자비에 보브와, Matiaz Bureau Caton, Claire Nebout

엄마의 죽음을 애써 거부하는 4살 꼬마 뽀네뜨의 눈물 어린 감동적인 연기가 볼만한 아동 영화. 전세계 관객들을 감동시킨 이 아역 배우는 네살임에도 불구하고 감정을 조율할 줄 아는 영민함과 천연덕스러운 연기 덕분에 감탄을 자아내면서, 베니스 영화제 여우주연상을 수상하였다. 자끄 드와이옹 감독은 뽀네뜨 역을 찾기위해 프랑스 전역에 있는 유치원을 찾아다녔다고 한다.




천국의 아이들 (Bacheha-Ye aseman / The Children Of Heaven, 1997)
이란 / 2001.03.17 / 드라마,코메디 / 88분
감독 : 마지드 마지디
출연 : Mohammad Amir Naji, Amir Farrokh Hashemian

테헤란 남쪽의 가난한 가정에 살고 있는 초등학생 알리. 엄마의 심부름을 갔다가 금방 수선한 여동생 자라의 구두를 잃어버린다. 하나뿐인 여동생의 한 켤레 뿐인 구두를. 자라는 학교에 뭘 신고 가냐며 눈물이 글썽글썽하다. 집에 새 신발을 살 여유가 없다는 걸 아는 알리는 여동생에게 부탁한다. "울지마. 오빠가 찾아줄게. 그때까지 오빠 운동화를 함께 신자..."




자전거 도둑 (Ladri Di Biciclette / The Bicycle Thief, 1948)
이탈리아 / 93분
감독 : 비토리오 데 시카
출연 : Lamberto Maggiorani, Enzo Staiola, Lianella Carell

2차 대전 직후 이탈리아의 수도 로마의 거리. 오랜동안 일자리를 구하지 못하고 거리를 배회하던 안토니오는 우연히 직업 소개소를 통해 벽보를 붙이는 일거리를 얻는다. 그 일을 하기 위해선 자전거가 필요했고 이 사실을 안 아내 마리아는 남편의 직업을 위해 자신이 소중하게 간수했던 침대 시트를 전당포에 잡히고 자전거를 구한다.

전후 사회의 빈곤과 모순을 리얼한 영상으로 묘사한 이탈리아 네오 리얼리즘을 대표하는 거장 데시카의 명작. 흑백 영상은 마치 다큐멘타리를 보는 듯한 인상을 준다. 1948년 아카데미 최우수 외국영화상, 49년 뉴욕 영화 비평가 최우수 외국영화상, 49년 로카르노 영화제 대상 수상.




책상 서랍 속의 동화 (一個都不稜少: Not One Less, 1999)
중국 / 1999.10.30 / 드라마 / 106분
감독 : 장예모
출연 : Minzhi Wei, Huike Zhang, Zhenda Tian

잠시 대리 선생을 맡은 시골 소녀가 도시로 가출한 학생을 찾으러 상경하면서 벌어지는 감동적인 이야기. 가난 때문에 학교를 떠나야 하는 아이들과 낙후된 시골 교육 환경을 통해, 진정한 교육이 무엇인지를 보여주는 장예모 감독의 작품이다. '99년 베니스 영화제 황금사자상 수상작. 부산영화제 폐막작.




인생 (人生 / Lifetimes, 1994)
중국,대만 / 1995.05.27 / 드라마 / 125분
감독 : 장예모
출연 : 공리, 갈우, Niu Ben, Ni Dabong

중국 작가 여화(余華)의 동명 소설 '활착'을 개작한 것으로 후우꿰이라는 인물과 그의 가족에 대한 현대사 40년간의 인생 이야기. 장예모 감독?<귀주 이야기>의 다음 작품으로 대만연대공사(-年代公司)의 투자와 상해영화제작소(上海電影制片-)의 후원으로 만들어졌다. 상해에서 상영된 후 장예모의 모교인 북경 영화 아카데미에서 후배들에게 먼저 보여졌다. 94년 칸느 영화제 심사위원 대상, 남우주연상, 박애주의상 수상, 94년 골든글로브 외국어영화상 수상, 94년 영국 아카데미 외국어영화상 수상.




길로틴 트래지디 (La Veuve De Saint-Pierre / The Widow Of Saint-Pierre, 2000)
프랑스,캐나다 / 2001.09.08 / 드라마 / 110분
감독 : 빠트리스 르꽁트
출연 : 줄리엣 비노쉬, 다니엘 오떼유, 에밀 쿠스트리차, 미첼 듀차우소이

1850년 캐나다 근처의 프랑스령 생 피에르 섬을 배경으로 일어났던 실화를 바탕으로, 사형선고를 받은 사람이 길로틴(단두대)가 없어 집행이 여러달 연기되면서 사형수(에밀 쿠스트리차)의 회개와 그에 대한 간수장 부부(다니엘 오떼유, 줄리엣 비노슈)의 연민을 그린 시대극.




스텔라 (Stella, 1990)
미국 / 1990.06.02 / 드라마 / 108분
감독 : 존 어만
출연 : 베트 미들러, 존 굿맨, 트리니 알바라도, 스티븐 콜린스

두 모녀간의 이야기를 그린 가족 영화. 베티 미들러가 딸을 사랑하는 제멋대로인 어머니의 헌신적이고 지고지순한 사랑을 연기했다. 올리브 히긴스 프로티(Olive Higgins Prouty)의 '스텔라 달라스(Stella Dallas)가 원작으로 이미 25년, 37, 42년에 각각 영화화된 바 있다.








푸른 불꽃 (靑の炎, 2003)
일본 / 범죄,스릴러,드라마 / 116분
감독 : 니나가와 유키오
출연 : 니노미야 카즈나리, 마츠우라 아야, 스즈키 안

미스테리 호러 소설가이며, 발표작들이 대부분 영화화된 키시 유스케의 동명 소설을 원작으로 한 범죄 드라마로, 이혼한 의부가 나타나 가족들을 괴롭히기 시작하자 사회적인 법률로는 엄마와 여동생을 지키지 못함을 깨닫고, 아버지를 살해하기 위해 완전 범죄를 계획하는 소년 슈이치의 이야기.




정복자 펠레 (Pelle Erovraren / Pelle Erobreren / Pelle The Conqueror, 1987)
덴마크,스웨덴 / 1989.07.15 / 150분
감독 : 빌 오거스트
출연 : 막스 본 시도우, Pelle Hvenegaard, Erik Paaske, 브존 그라나스

덴마크로 일자리를 찾아온 스웨덴 노동자 라세와 그의 아들 펠레(Pelle the Conqueror: Pelle Hvenegaard 분). 열심히 일해 돈도 벌고 재혼도 하여 일요일이면 침대에서 커피를 마시고 싶다는 소박한 소망을 갖고 있는 자상한 아버지(Lassefar: 막스 본 시도우 분), 하루종일 노는 것이 꿈인 영리한 아들, 펠레. 두 부자가 일하는 스톤 농장에는 많은 스웨덴 노동자들이 열악하고 비참한 노동 환경 속에서도 감히 불평조차 못하고 군소리없이 일하고 있지만...

19세기 덴마크 이민 노동자들의 삶을 무대로, 안정을 바라는 아버지와 미지의 가능성에 끊임없는 호기심을 갖고 도전하는 아들의 시각 차이를 그리면서, 한창 세상을 배워가는 소년의 눈을 통해 인생의 여러 단면을 보여주는 수작. 누군가의 표현을 빌면, "자연의 도도한 아름다움에 비해 비참하고 고통뿐인 인간의 삶을 더욱 처절하게, 그리고 화려한 바닷가 전원의 사계절 변화를 시시각각 담은 화면은 장엄한 음악과 함께 비장미를 더해준다."라고 했다.




하치 이야기 (八チ公物語 / Hachi-ko Monogatari, 1987)
일본 / 2002.06.28 / 가족,드라마 / 107분
감독 : 코야마 세이지로
출연 : 나카다이 다쓰야, 야치구사 카오루, 이시노 마코, 야나기바 토시로

1923년 12월, 아키다현 오오다테. 흰눈이 소담스레 내리는 어느 겨울날, 흰눈처럼 하얀 하치가 누렁이, 검둥이 형제들과 함께 태어난다. 아키다현청 토목 과장은 그중 하얀 강아지를 자신의 은사인 동경제대 농학부 교수 우에노 박사에게 보내기로 한다. 태어난지 한달, 세상에 눈뜨기도 전에 강아지는 동경으로의 낯선 여행을 시작한다. 동경 시부야에 우에노 교수 댁에 보내진 흰둥이. 하얀 색 털과 초롱초롱한 눈망울의 강아지는 단번에 식구들의 귀여움을 독차지하고, 유독 애정을 느끼는 우에노 교수는 힘차게 땅을 박차고 서있는 이 강아지를 보고 八자라는 뜻의 '하치'라는 이름을 지어준다. 볕드는 마루에서 하치의 벼룩을 잡아주고, 첨벙첨벙 목욕도 함께 하는 우에노 교수님의 하치에 대한 사랑은 유별나서 부인이 질투할 정도다.

실화에 기초하여, 시부야 역에서 10년의 시간동안 꼼짝도 않고 주인을 기다린 충견 하치의 이야기. 인간과 개 사이의 특별한 관계를 다루고 있다.




안개 속의 풍경 (Topio Stin Omichli / Landscape In The Mist, 1988)
프랑스,그리스,이탈리아 / 1996.09.00 / 드라마 / 121분
감독 : 테오 앙겔로플로스
출연 : 타냐 팔라이오로그, 디미트리스 카베리디스

볼라와 알렉산더 두 남매는 역에 나가 아버지가 오기만을 기다리지만 아버지가 오지 않자 아버지가 계시다는 독일에 가기 위해 기차에 오른다. 그러나 무임승차였던 그들은 곧 쫓겨나고 삼촌의 공장으로 간다. 거기서 삼촌과 경찰과의 대화 중 독일에 아버지가 있다는 것이 거짓말이었음을 듣는다. 문득 눈이 온다. 환호하며 나가는 사람들. 그러나 그들은 곧 정물로 변해 버린다. 죽어가는 말이 끌려오고 옆에서는 파티의 손님들이 시끄럽다. 무언가 부조화스럽다. 죽음 앞에서조차도 사람들은 모두 냉담하고 무관심할 뿐이다. 알렉산더는 울음을 터뜨린다.

공허하고 스산한 현대의 그리스를, 나이 어린 오누이가 아버지를 찾아나서는 단순한 여정 속에 담아내고 있는 작품. 서정시를 읽는 듯한 몽상적이고 초현실적인 로드무비로서, 긴 호흡으로 찍어낸 현대 그리스의 풍경은 비어있고 비가 내리고 어둡고 삭막하다. 음침한 느낌의 꿈 같은 이미지를 표현해낸 촬영 기법이 돋보이며, 눈 속의 결혼식 장면과, 크레인에 의해 거대한 손이 바다에서 이끌려 나오는 모습, 또 순진한 아이들이 이해하기 어려운 어른들의 세계를 응시하는 광경 등이 인상적이다.




돈 크라이 마미 (Bastard Out Of Carolina, 1996)
미국 / 1997.11.08 / 97분
감독 : 안젤리카 휴스턴
출연 : 제니퍼 제이슨 리, 론 엘다드, 글렌 헤들리, 라일 로벳, 지나 말론

젊은 날, 한 남자의 깊은 사랑에 빠지지만 돌연 그의 배신으로 혼자 아이를 키우며 살던 애니(Anney: 제니퍼 제이슨 리 분)는 외로움 속에 딸과 함께 살아간다. 그러다 만난 파슨스(Lyle Parsons: 더못 멜로니 분)라는 남자는 진정으로 애니를 위해 주며 딸에게도 친절했지만 그만 교통사고로 죽고 만다. 그때 애니는 그의 아이를 임신한 상태였고 또 다시 사랑하는 남자(Glen Waddell: 론 엘다드 분)가 나타난다. 애니는 두 딸에게 자신이 결혼할 것을 알린다. '꼬맹이'라는 뜻의 맏딸 본(Bone: 제다 마론 분)은 엄마를 위해 함박웃음을 보내준다. 그와 결혼한 애니는 한동안 조용한 결혼생활을 보내다가 유산을 하게 되고 다시는 임신을 못할 불운이 겹치는데...

'딸에겐 고통이었던 엄마의 사랑', 계부의 학대 속에서, 기구한 삶을 살아가는 엄마의 행복을 바라는 어린 딸의 시선을 통해, 부부간의 사랑과 모녀간의 사랑을 밀도깊게 그린 애증의 드라마. 개성파 여배우 안젤리카 휴스톤의 첫 감독 데뷔작으로, 제49회 깐느 영화제에서 큰 주목을 받으며, 비평가들의 찬사를 받았다. 특히 3천대 1의 경쟁률 끝에 선발된 아역 배우 지나 말론의 연기가 훌륭하다.




챔프 (The Champ, 1979)
미국 / 117분
감독 : 프란코 제피렐리
출연 : 존 보이트, 페이 더너웨이, 릭 슈로더, 잭 워든

눈물샘을 자극하는 전형적인 최루성 영화로서 코끝이 시큰해지는 정도가 아니라 그야말로 통곡이 나올 만큼 슬프다. 페이 더너웨이나 존 보이트가 가벼운 마음으로 연기했지만 워낙에 저력있는 배우들이라 영화를 끌어 가는 힘이 보인다. 하지만 이 두 대배우 사이에서 어린 리키 슈로더가 더욱 빛이 난다. 언제나 아역 배우들은 함께 출연하는 어른 배우들과 동등하거나 아예 능가하는 연기를 보여준다. 이 영화로 리키 슈로더는 골든 글로브 상을 받았다.




아빠는 출장 중 (Otac Na Sluzbenom Putu / When Father Was Away On Business, 1985)
유고 / 1988.10.29 / 드라마 / 136분
감독 : 에밀 쿠스트리차
출연 : 모레노 D'E 바톨리, 미키 마뇰로비치, 머자나 카라노빅

스탈린주의와 전후 50년대에 나타난 수정주의인 유고슬라비아의 독립주의 사이를 적응해 살아가기 힘든 상황하에 정치적인 수감이 늘어나게 된다. 노동성의 간부인, 말리크(Malik: 모레노데 바르톨리 분)의 아빠 메쉬아(Mesa: 미키 마노로빅 분)는 바람둥이이다. 그는 가족들에게는 출장간다고 하고서는 2년 전부터 아내 몰래 정을 통해오던 정부 알리카(Ankica: 미라 푸어란 분)와 함께 기차를 타고 여행을 가게되는데 기차에서 알리카는 메쉬아에게 자꾸 아내와 이혼하라고 조르자 메쉬아가 무심코 알리카에게 내뱉은 말이 화근를 부른다. "감옥같은 세상에서 어떻게 사랑을 해!". 무심코 내뱉은 이 한 마디 말 때문에 미쉬아는 2년간 바라지 않은 출장을 해야만 했다. 당시의 유고에서는 정치적 감금이 출장 중이라는 말로 대신 사용된 것이다. 가장이 없는 말리코의 집안은 가난했지만 그런 대로 견디며 살아간다.

우리나라에 소개된 최초의 유고슬라비아 영화로 깐느 영화제에서 심사위원 만장일치로 그랑프리를 수상한 화제작. 소련을 추종하는 스탈린 주의자들과 티토 정권의 독전 노선이 서로 대립하고 있던 50년대 초, 혼란한 정치적 상황을 배경으로 한 평범한 가족의 사실적인 삶의 모습을 말리크라는 어린 소년의 시선으로 그려간 작품이다.




소피의 선택 (Sophie's Choice, 1982)
미국 / 드라마 / 151분
감독 : 알란 J. 파큘라
출연 : 메릴 스트립, 케빈 클라인, 피터 맥니콜, 리타 카린

영화는 역사의 상처를 안고 사는, 끔찍할 정도로 창백한 얼굴의 소피를 통해 사랑과 인생과 죽음을 이야기한다. 배우들의 연기가 뛰爭?감동적인 영화이다.




마르셀리노 (Marcelino Pan Y Vino / The Miracle Of Marcelino, 1954)
스페인 / 1990.04.07 / 코메디,드라마 / 90분
감독 : 라디슬라오 바다
출연 : 파블리토 칼보, 라파엘 리벨레스, 주안 칼보, 페르난도 레이

스페인 작가 호세 마리아 산체스 실바라의 동화가 원작으로 했다. 이 영화가 우리 나라에 수입된 것은 30년 전으로 올드팬들에게는 추억의 영화인 셈이다. 젊은 영화팬들에게는 주제곡 '마르셀리노의 노래'로 더 잘 알려져 있다.

앙증맞고 사랑스러운 꼬마 역을 한 카르보는 55년 칸느영화제에서 특별 아역상을 받았다. 이 소년의 표정과 움직임을 쫓다 보면 지치고 오염된 영혼이 어느덧 순화되고 정화되는 듯 행복한, 그러면서도 슬픈 이율 배반적인 감정을 체험하게 된다. 거칠고 투박해 보이는 흑백 영상이 빚어내는 독특한 미감은 밝음과 어두움 그 영혼의 이원성과 특유의 종교적 분위기를 상징적으로 형상화하며 스페인 산촌 특유의 풍경을 생생하게 담아낸다. 세월이 가도 바래지 않는 고전이다.




철도원 (Il Ferroviere / The Railroad Man, 1956)
이탈리아 / 드라마 / 116분
감독 : 피에트로 제르미
출연 : 피에트로 제르미, 루이자 델라 노크, 실바 코시나

<철도원>은 리얼한 홈 드라마이면서도, 그로부터 한걸음 보폭을 넓힌 작품으로 평가된다. 이 영화가 흥행적으로 성공한 포인트는 네보라 소년의 기용과 테마 음악의 우수성 때문이다. 특히 <철도원>은 전후 이태리 영화의 황금기를 구가한 네오 리얼리즘의 대표작으로 일상 생활의 섬세한 묘사에 인생의 애환이 넘치고, 전후 이태리 서민층의 생활고가 잘 묘사되어 있다. 또한 장남과의 단절로부터 화해의 분위기로, 그리고 집안을 조용하게 지켜나가는 어머니의 애정을 비롯해서 각 캐릭터의 성격이 잘 살아 있다. 그러나 뭐니뭐니해도 영화 전편에서 유머를 가득 넘치게 하는 보네라 소년의 귀여움이야말로 이 영화의 최고 볼거리이다.




미션 (The Mission, 1986)
영국 / 1986.12.24 / 드라마 / 125분
감독 : 롤랑 조페
출연 : 로버트 드니로, 제레미 아이언스, 레이 맥아널리

"그리하여... 신부들은 죽고, 저만 살아 남았습니다. 하지만 실제로 죽은 건 나고, 산 자는 그들입니다. 그것은 언제나 그렇듯 죽은 자의 정신은 산 자의 기억 속에 남아있기 때문입니다."

1750년 경, 파라과이와 브라질의 국경 부근에 일어난 실화를 바탕으로, 원주민 과라니족을 상대로 선교 활동을 벌이는 두 선교사의 대립되는 모습을 통해서 종교와 사랑, 정의가 무엇인가를 심오하게 그린 영화. 1986년 제39회 칸느 영화제 그랑프리 수상.



 

(クイ-ル / Quill, 2004)
일본 / 드라마
감독 : 최양일
출연 : 고바야시 가오루, 시나 깃페이, 카가와 테루유키

댓가를 바라지 않는 무한한 애정과 신뢰를 주는 한 마리의 맹인 안내견의 이야기를 그린 원작 [맹인안내견 퀼의 일생(盲導犬クイ-ルの一生)]을 영화화한 작품. 원작은 어린이에서 성인 독자까지 폭넓은 층의 지지를 받아 70만부 이상의 판매고를 올리며 베스트셀러가 되었고 NHK를 통해 드라마로 제작되어 높은 시청률을 기록하기도 했다. 그리고 2004년 봄, 그 이야기가 다시 영화로 만들어져 스크린에 살아난다
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.
TAGS

Leave your greetings here.

  
  
  
  
  
  
  
  
 
Max Bruning
 

많은 개발자들이 리눅스용으로 어플리케이션을 작성하고 있었습니다만 솔라리스10의 수많은 새로운 기능과 썬의 계속되는 AMD, 인텔 프로세서 지원을 통해 개발자들이 솔라리스 플랫폼에서 어플리케이션을 작성하는 것에 대해 관심을 가지게되었습니다. 이 글은 각 운영체제에서의 개발 환경의 유사점과 차이점에 대해 살펴 봅니다. 이 글을 통해 어플리케이션을 리눅스에서 솔라리스로 포팅 하거나, 리눅스에서 개발 경험이 있는 개발자가 솔라리스에서 프로그래밍을 하는 데 도움을 얻을 수 있을 것입니다.

이 글에서 용어 "솔라리스"는 솔라리스 10 (과 오픈 솔라리스)를 나타내고 "리눅스" 는 리눅스 2.6을 나타 냅니다. 이 글에서 다루는 많은 부분들은 솔라리스와 리눅스의 이전 버젼에도 또한 적용될 것입니다. 이 글의 테스트는 SuSe 9.1 하에서 이루어 졌으나 리눅스 배포판이란 범용 리눅스를 뜻합니다. 또한 이 글은 C++ 도 동일하게 수행되겠지만 C 프로그래밍을 작성하는데 촛점을 맞추고 있습니다. 자바 기반의 어플리케이션은 리눅스 혹은 솔라리스에 특정한 함수를 호출해서는 안되는 관계로 어플리케이션은 포팅이 가능할 것입니다.

소개

이 글에서는 솔라리스와 리눅스에서 어플리케이션 프로그래머와 분석가들에게 보여질 수 있는 동일점과 차이점에 대해 생각해 봅니다. 꼭 이것이 차이점에 대한 소모적인 설명을 뜻하거나 어떠한 OS가 상대편 보다 좋다는 것을 보여주는 것은 아닙니다. 이 글은 한쪽 OS에 경험이 많은 개발자가 다른쪽 OS 또한 쉽게 다를 수 있도록 돕는데 그 목적이 있습니다.

POSIX과 호환되며 어떠한 시스템 콜 혹은 라이브러리 함수도 호출하지 않는 간단한 어플리케이션은 어떠한 수정도 없이 두 OS 상에 포팅이 가능할 것입니다. 즉 어플리케이션을 작성하고 솔라리스 혹은 리눅스에서 컴파일 한 다음 다른 OS 에서 간단히 재 컴파일하면 두 곳 모두에서 작동할 수 있습니다. 대부분의 시스템 콜 과 라이브러리 루틴은 이와 같은 것입니다.

리눅스의 많은 시스템 콜들은 솔라리스에 라이브러리 함수 형태로 존재하며, 또한 그 반대도 마찬가지입니다. 예를 들어 sched_setscheduler() 는 리눅스에서 시스템 콜이고 솔라리스에서는 priocntl(2) 시스템 콜을 호출 하는 라이브러리 함수 입니다. priocntl(2) 시스템 콜은 리눅스에서는 존재 하지않습니다만 리눅스는 시간 공유, 실시간 이상의 멀티 스케쥴러를 지원하지 않습니다. 뒷부분에서 시스템콜을 기능적 섹션으로 그룹을 묶고 각 OS에서 어떠한 것이 사용 가능한지 비교해 봅니다.

리눅스 세계의 대부분의 어플리케이션과 툴킷은 어떠한 수정도 없이 컴파일이 가능할 것입니다. 여기에는 gcc, emacs, MySQL, perl 외 수많은 것들이 포함되어있습니다. 컴파일된 바이너리 패키지는 http://www.sunfreeware.com 에서 다운로드할 수 있습니다.

리눅스와 솔라리스를 비교 하는 몇몇 글이 존재 하지만 대부분은 이전 버전들을 비교하는 글이었습니다. 이런 글들은 웹에서 "리눅스 솔라리스 비교 " 로 검색해 볼 수 있습니다. Seal Rock Research White Paper (pdf) 은 솔라리스10 과 리눅스 2.6 버젼에 대해 다루고있습니다. Migrating Solaris Applications to Linux 은 솔라리스 어플리케이션을 리눅스로 포팅 하는 것에 대해 몇장에 걸쳐서 다루고 있습니다.

솔라리스와 리눅스는 많은 관리상의 차이점이 존재하며, 한 OS의 각 배포판별로도 많은 차이점이 있습니다 . 솔라리스10은 솔라리스 이전 버젼에 비해 가장 크게 변화된 "서비스 관리 프레임워크(SMF;Service Management Framework)" 라는 기능을 가지고 있습니다. 시스템 관리상의 차이점은 이 글에서 다루지 않지만 개발자에게 영향을 미치는 경우 추후 다루도록 하겠습니다.

시스템 콜과 라이브러리

리눅스에 존재하는 대부분의 시스템 콜과 라이브러리는 솔라리스에도 존재합니다. 이번 문단에서는 두 시스템 간에 각각 다른 시스템 콜과 라이브러리 루틴에 대해 다룹니다.

솔라리스는 시스템 콜의 리스트를 /usr/include/sys/syscall.h 에 보관 합니다. 리눅스는 동일한 정보를 /usr/include/asm/unistd.h 에 보관 합니다. (주목할 점은 리눅스와 솔라리스 둘다 unistd.h syscall.h 파일을 가지며 종종 동일한 내용을 가질때도 있다는 사실입니다.)

시스템 콜에 대한 문서는 솔라리스, 리눅스에서 모두 /usr/share/man/man2 에 존재합니다. (솔라리스는 /usr/man 에서 동일한 곳으로 심볼릭 링크가 걸려 있습니다.) 라이브러리 루틴은 메뉴얼의 다양한 섹션에 문서화되어있습니다. 리눅스, 솔라리스의 라이브러리 섹션에 대한 개관을 위해 man intro.3 를 보도록 합니다. 주목할 점은 솔라리스는 라이브러리 루틴을 리눅스보다 더 잘 분류해 놓았다는 점입니다. 예를 들어 aio_read() 는 솔라리스에서 aio_read(3RT) 에 분류되어 있지만 리눅스에서는 aio_read(3)에 분류되어 있습니다. 결과적으로 솔라리스에서 aio_read() 를 사용하는 프로그램을 컴파일할 때 개발자는 반드시 실시간 라이브러리 -lrt 를 통해 실시간 라이브러리를 컴파일/링크 할때 포함시켜야 하지만 리눅스에서는 그럴 필요가 없습니다.

리눅스와 솔라리스는 둘다 200개 이상의 서로 다른 라이브러리를 가지고 있고 라이브러리 내에 50,000 개 정도의 함수를 가지고 있습니다.

다음의 표는 리눅스와 솔라리스의 몇몇 라이브러리를 목록화한 것입니다. 주의할 점은 이것이 전체 목록은 아니라는 것이고 이러한 라이브러리들은 기본 인스톨이 된 시스템에서는 각각 따로 다운로드 및 설치를 해야 한다는 것입니다.

표 1: 리눅스와 솔라리스의 몇몇 라이브러리들
 
 
솔라리스
리눅스
설명
libc
libc
표준 C 라이브러리 (POSIX, SysV, ANSI, 등등.) 솔라리스의 libc man 참조.
libucb
libc
UCB (University California Berkeley) 호환 바이너리
libmalloc
libc
몇가지 서로 다른 malloc 라이브러리가 존재함; 기본 함수는 libc 에 존재.
libsocket
libc
소켓 라이브러리 (리눅스에서는 libc 에 존재).
libxnet
libc
X/Open 네트워킹 라이브러리
libresolv
libresolv
DNS 루틴들 (솔라리스에서는, inet_* routines)
libnsl
libnsl/libc
네트워크 서비스 라이브러리 (리눅스 - nis/nis+ 루틴들)
librpc
librpc
RPC 함수들
libslp
libslp
Service Location Protocol
libsasl
libsasl
간단한 인증과 보안 레이어
libaio
libaio
비동기 I/O 라이브러리
libdoor
 
Door 지원 (door_create(), door_return(), 등등.)
librt
librt
POSIX 실시간 라이브러리
libcfgadm
 
설정 관리 라이브러리
libcontract
 
계약 관리 라이브러리 (솔라리스에서 man contract.4 참조)
libcpc
 
CPU 성능 카운터 라이브러리 (리눅스에서는 아마 커널 모듈을 설치해야 할 것임)
libdat
 
직접 접근 전송 라이브러리 (http://www.datcollaborative.org)
libelf
libelf
ELF 지원 라이브러리
libm
libm
수학 라이브러리

다음 섹션에서는 몇가지 시스템 콜과 라이브러리들에 대해 좀 더 자세히 알아 봅니다. 시스템 간의 차이점에 대해 집중하겠습니다.

소켓과 네트워킹

대부분의 소켓, 네트워킹 코드는 반드시 사용하는 OS에 따라 다시 재컴파일 되어야 하고 반드시 실행이 가능해야 합니다. 이 섹션은 솔라리스와 리눅스에서 보편적으로 사용되는 네트워크에 관련된 시스템 콜과 라이브러리를 비교합니다.

socket()

socket() 루틴, 이에 덧붙여서 AF_UNIX, AF_INET, 와 AF_INET6 도메인 매개변수들은 솔라리스와 리눅스에서 추가적인 값을 각각 가지고 있습니다. 솔라리스에서 AF_NCA 도메인은 소켓에 네트워크 캐쉬 혹은 가속기 (nca(1) 참조)를 지정하여 사용 할 수 있도록 합니다. 대부분의 어드레스 패밀리(도메인들)은 리눅스, 솔라리스에 둘다 존재 합니다. 주의: 솔라리스에 /usr/include/sys/socket.h 그리고 리눅스에 /usr/include/linux/socket.h 를 비교하여 가능한 어드레스 패밀리를 참고 하시기 바랍니다. 그러나 개발자는 몇몇 이러한 도메인을 지원하는 코드를 작성 혹은 다운로드 받아야 할지도 모릅니다.

리눅스는socket(2) 맨 페이지에 기술된 대로 몇가지 추가적인 도메인을 가지고 있습니다. 기술된 추가적인 도메인은 다음과 같습니다:

  • AF_IPX - Novell IPX 프로토콜(아마도 오로지 수세를 위한 것임?).
  • AF_NETLINK - 커널/유저 인터페이스 디바이스로 유저가 커널 모듈에 접근 할 수 있도록 허락해 줌. 주의: 솔라리스에서는 다른 방법으로 똑같은 일을 할 수 있는 방법이 존재 함(리눅스에서도 마찬가지).
  • AF_X25 - X25 프로토콜. 솔라리스에서 이 도메인은 Solstice X.25 제품에 포함되 있음.
  • AF_AX25 - 아마츄어 라디오 AX.25 프로토콜.
  • AF_ATMPVC - ATM 상의 영구적인 가상 서킷.
  • AF_APPLETALK - 리눅스의 man ddp 참고. 솔라리스에도 존재 하지만 문서화 되어 있지는 않음.
  • AF_PACKET - 리눅스의 man packet.7 참고. Raw 패킷 인터페이스. 솔라리스에서는 NIC 디바이스를 열고 use getmsg(2)/putmsg(2) 를 사용하여 DLPI를 통한 raw 패킷을 전송/수신 할 수 있음. (DLPI에 대한 자세한 설명은 Data Link Provider Interface (DLPI), Version 2 를 참조).
bind()

리눅스 멘 페이지 (man bind.2) 는 AF_INETAF_UNIX 사이의 어드레스 패밀리 차이점에 대한 정보를 포함하고 있음. 솔라리스는 man bind.3socket 을 참조.

listen()

리눅스와 솔라리스 둘다 backlog 매개 변수는 (listen()의 두번째 매개변수) 수락되길 기다리고 있는 접속된 큐의 길이를 나타 내고 있음. 리눅스 맨 페이지에서는 이렇게 설명 하고 있고 솔라리스 멘 페이지에서는 그냥 "지연되고 있는 연결 큐"라고 설명되어 있습니다.

accept()

리눅스는 3가지의 연결 기반 소켓 타입을 지원합니다: SOCK_STREAM, SOCK_SEQPACKET, 그리고 SOCK_RDM 반면 솔라리스에서는 오직 SOCK_STREAM 만 기술 되어 있습니다. 리눅스 구현에서는 몇가지 소켓 플래그를 상속 하지 않습니다. 이 것이 구현의 차이점을 불러 왔을 것입니다.

connect()

리눅스 멘페이지는 (man connect.2) SOCK_SEQPACKET 를 기술 하고 있지만 솔라리스에서는 기술되어 있지 않습니다. 리눅스는 struct sockaddr 내의 sa_familyAF_UNSPEC 으로 세팅해서 주소에 연결 함으로써 비연결 소켓과 connect() 을 구분하고 있습니다. 이러한 동작은 솔라리스에는 기술되어 있지 않습니다.

send()/recv()

다른 소켓 라이브러리 함수들과 같이, 이러한 함수들은 두 시스템에서 거의 동일하게 동작합니다. 리눅스는 멘페이지에서 몇가지 추가적인 flags 매개 변수를 가지고 있습니다.

shutdown()

솔라리스와 리눅스간에 주목할만한 차이점은 없습니다.

네트워킹 예제

몇가지 차이점이 들어나는 어플리케이션을 확인 하는 것이 유용할 것입니다. tracedump 프로그램은 패킷 캡춰 라이브러리 (libpcap) 를 이용하여 유저 레벨에서의 이더넷 패킷을 읽어 들입니다. 이 코드는 raw 이더넷이 솔라리스, 리눅스 간에 상당히 다름을 알아 낼 수 있습니다. (libpcap 은 FreeBSD, HP-UX, AIX 같은 서로 다른 시스템 간의 차이점을 알아 내는데 유용할 수 있습니다.) libpcap에서 응용 가능한 코드는 pcap-linux.cpcap-dlpi.c에 존재합니다. DLPI 코드는 솔라리스, HP-UX, AIX, 그리고 다른 운영체제를 위해 사용 됩니다. 리눅스는 표준 socket 호출을 통해 raw 소켓 패킷을 읽어 들일 수 있는 방법을 제공합니다. 솔라리스는 getmsg(2) putmsg(2) 를 호출해서 DLPI 패킷을 주고 받습니다.

다음의 코드는 솔라리스의 네트워크 인터페이스에서 유저-레벨 패킷을 캡춰 하는 방법을 나타내고 있습니다. 바로 뒤에 리눅스에서 동일한 작업을 하는 코드가 따라 옵니다. 이 코드는 libpcap 라이브러리의 매우 엄청난 간략화 버젼입니다.

#include <sys/types.h>
#include <sys/dlpi.h>
#include <sys/stream.h>
#include <stdio.h>

#include <errno.h>
#include <stropts.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    register char *cp;
    int fd;
    dl_info_ack_t *infop;
    union DL_primitives dlp;
    dl_info_req_t inforeq;
      dl_bind_req_t    bindreq;
    dl_attach_req_t attachreq;
    dl_promiscon_req_t promisconreq;
    struct    strbuf    ctl, data;
    int    flags;
    char buffer[8192];
    dl_error_ack_t *edlp;

    fd = open(argv[1], O_RDWR);  /* for instance, /dev/elxl0 */

    /* attach to a specific interface */
    attachreq.dl_primitive = DL_ATTACH_REQ;
    attachreq.dl_ppa = 0;  /* assume we want /dev/xxx0 */
    ctl.maxlen = 0;
    ctl.len = sizeof(attachreq);
    ctl.buf = (char *)&attachreq;
    flags = 0;
    /* send attach req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get ok ack, may contain error */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    memset((char *)&bindreq, 0, sizeof(bindreq));
    /* the following bind might not need to be done */
    bindreq.dl_primitive = DL_BIND_REQ;
    bindreq.dl_sap = 0; 
    bindreq.dl_max_conind = 1;
    bindreq.dl_service_mode = DL_CLDLS;
    bindreq.dl_conn_mgmt = 0;
    bindreq.dl_xidtest_flg = 0;
    ctl.maxlen = 0;
    ctl.len = sizeof(bindreq);
    ctl.buf = (char *)&bindreq;
    flags = 0;
    /* send bind req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get bind ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_PHYS;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_SAP;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    /* read and echo to stdout whatever comes to us */
    while (1) {
      data.buf = buffer;
      data.maxlen = sizeof(buffer);
      data.len = 0;
      ctl.buf = (char *) &dlp;
      ctl.maxlen = sizeof(dlp);
      ctl.len = 0;
      flags = 0;
      getmsg(fd, &ctl, &data, &flags);
      write(1, "\nCTL:\n", 6);
      write(1, ctl.buf, ctl.len);
      write(1, "\nDAT:\n", 6);
      write(1, data.buf, data.len);
    }
}

솔라리스 코드는 DLPI 요청을 만들고 DLPI 응답을 받아서 인터페이스에게 인터페이스에 도착하는 모든 패킷의 카피를 만들도록 요청 합니다.

리눅스의 코드는 훨씬 간단합니다. socket(2) 콜은 raw 패킷을 지정할 수 있도록 허락합니다. 리눅스는 DLPI나 STREAMS을 사용하지 않습니다.

#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if_arp.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    int    sock_fd = -1;
    struct sockaddr_ll    sll, from;
    struct packet_mreq    mr;
    socklen_t    fromlen;
    int        packet_len;
    char        buffer[8192];

    sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

    memset(&sll, 0, sizeof(sll));
    sll.sll_family    = AF_PACKET;
    sll.sll_ifindex    = 0;
    sll.sll_protocol    = htons(ETH_P_ALL);

    bind(sock_fd, (struct sockaddr *) &sll, sizeof(sll));

    while (1) {
      fromlen = sizeof(from);
      packet_len = recvfrom(
        sock_fd, buffer, sizeof(buffer), MSG_TRUNC,
        (struct sockaddr *) &from, &fromlen);
      write(1, buffer, packet_len);
    }
}

프로세스/프로세서 관리

솔라리스와 리눅스에서프로세스 는 프로그램의 동작하고 있는 실체 입니다. 솔라리스와 리눅스(2.6)에서 프로세스는 주소 공간을 위한 컨테이너 이자 하나 혹은 이상의 쓰레드 입니다. 시스템의 모든 프로세스들은 특수한 프로세스 ID(PID)를 가지고 있고 프로세스가 종료 된 후에도 일정 기간동안 유일성이 유지됩니다. 프로세스들은 fork(2) 와 이와 비슷한 동작을 하는 것들에 의해 만들어 집니다. 리눅스에서 프로세스(그리고 쓰레드)는 clone(2)을 사용하여 생성될 수 있습니다. 그러나 pthread_create(3) 가 좀더 포팅이 용이 합니다. 솔라리스에서 기술되지 않은 lwp_create() 시스템 콜은 어떠한 면에서 clone(2)과 비슷합니다.

vfork() 는 두 시스템에서 모두 비슷하게 동작합니다. 솔라리스는 fork1() forkall() 을 가지고 있습니다. fork1() 의 경우, 자식 프로세스는 오직 fork() 콜을 실행 시키는 쓰레드를 가지도록 합니다; forkall() 의 경우, 부모 프로세스 안의 모든 쓰레드는 자식에게 복사 됩니다. 기본 fork 는 fork1() 입니다. forkall() 은 반드시 명시적으로 사용되어야 합니다. forkall() 은 리눅스에서는 존재 하지 않습니다.

ps -elfL 커맨드는 리눅스와 솔라리스 둘다에서 프로세스 내의 쓰레드를 확인 하는데 사용될 수 있습니다. 두 시스템 모두 LWP 의 숫자와 프로세스내의 각 쓰레드의 lwpid 를 보여 줍니다. 알아둘 점은 리눅스에서 lwpid 는 프로세스들 간에서도 유일한 숫자 입니다. 솔라리스에서 lwpid는 프로세스 안에서만 유일합니다. 리눅스에서 멀티쓰레드 프로세스의 프로세스 ID는 사실 쓰레드 그룹의 ID입니다. 쓰레드 그룹 ID는 메인 쓰레드의 프로세스 ID와 동일 합니다. Sending a signal to any lwpid 에 시그널(kill(1)/kill(2)을 이용해서) 을 보내는 것은 프로세스에 시그널을 보내는 것과 동일 합니다. 솔라리스에서 사용자는 pid 로 시그널을 보냅니다. 두경우에서 만약 기본 동작이 취해진다면, 프로세스는 일반적으로 종료되고 쓰레드는 모두 중지 됩니다. ps(1) 맨페이지에서 자세한 사항을 참고 바랍니다.

리눅스와 솔라리스는 둘다 프로세서에 프로세스를 바인딩 하는 구문을 제공합니다. 리눅스는 각 프로세서를 상호배타적으로 사용한다는 가정하에 바인딩을 허락 합니다. 솔라리스는 배타적인 사용을 위해 프로세서의 셋에 바인딩 하는 것을 허락합니다. (CPU fencing 이라 불림) 그러나 그룹을 상호배타적인 용도로 바인딩 하는 것을 허락 하지 않습니다 (솔라리스 존은 제외). 리눅스는 CPU fencing 메카니즘을 가지고 있지 않습니다. 그러나 웹에서 관련된 구현은 찾을 수 있습니다. (예들 들어 bullopensource.org 사이트의 CPUSETS for Linux page ). 바인딩 작업을 하는 리눅스 시스템 콜은 sched_setaffinity(2)sched_getaffinity(2) 입니다. 솔라리스에서는 다음과 같습니다:

  • processor_bind(2) 는 프로세서에 LWP 혹은 프로세를 바인드/언바인드
  • pset_create(2) 프로세서 셋을 셋팅
  • pbind(1) psrset(1) 은 커맨드 라인 인터페이스를 제공

완벽한 설명을 위해 ps(1) 커맨드의 리눅스, 솔라리스 에서의 출력이 Threads 섹션에 기록되어 있습니다.

리눅스와 솔라리스 시스템에서 모든 형태의 exec 시스템 콜은 execve(2) 를 결과적으로 호출 하도록 되어 있습니다. 솔라리스 문서는 같은 맨 페이지에 6가지의 exec(2) 종류를 제공하고 있습니다. 리눅스의 exec(3) 맨 페이지에서는 execv, execl, execle, execlp, 그리고 execvp 가 기록되어 있스빈다. 분리된 페이지로 execve(2) 를 다루고 있습니다.

/proc 파일 시스템은 리눅스와 솔라리스 상에서 약간 상이 합니다. 두개의 시스템에서 모두 /proc 는 현재 시스템에서 동작하고 있는 프로세스들의 ID들을 파일 이름으로 가지고 있는 파일들을 포함 합니다. 각 PID 로 명명된 파일들은 디렉토리가 됩니다. 리눅스의 /proc 는 다양한 다른 프로세스 이름과 더불어 다양한 다른 이름의 디렉토리들을 포함합니다. 이러한 것들의 대부분은 프로세서, 디바이스, 시스템 통계를 다루고 있습니다. 리눅스에서 사용자는/proc 을 통해 프로세스, 프로세서, 디바이스, 메인 아키텍쳐 등의 정보를 얻습니다. 솔라리스에는 이러한 종류의 정보를 보통 커맨드를 통해 얻습니다. 예를 들어 prtconf(1) 는 솔라리스의 머신 설정 정보를 얻는데 사용될 수 있습니다. 리눅스에서는 /proc 에 존재하는 파일을 조사 함으로써 정보를 얻습니다.

솔라리스에서 프로세스가 사용하는 가상 주소 공간에 대한 정보는 pmap(1) 을 사용하여 얻을 수 있고 리눅스에서는 /proc/pid/maps 파일을 봄으로써 얻을 수 있습니다. 솔라리스의 pmap(1) 과 리눅스의 proc(5) 맨페이지를 참고 하시기 바랍니다.

<-- on solaris, address space of this instance of bash -->
bash-3.00$ pmap -x $$  
1043:    /usr/bin/bash -i
 Address  Kbytes     RSS    Anon  Locked Mode   Mapped File
08045000      12      12       4       - rw---    [ stack ]
08050000     528     468       -       - r-x--  bash
080E3000      76      72       8       - rwx--  bash
080F6000     124     108      40       - rwx--    [ heap ]
FED8E000       4       4       -       - rwxs-    [ anon ]
FEDA0000       4       4       -       - rwx--    [ anon ]
FEDB0000     760     660       -       - r-x--  libc.so.1
FEE7E000      24      24       8       - rw---  libc.so.1
FEE84000       8       8       -       - rw---  libc.so.1
FEE90000      24       8       4       - rwx--    [ anon ]
FEEA0000     524     324       -       - r-x--  libnsl.so.1
FEF33000      20      20       4       - rw---  libnsl.so.1
FEF38000      32       -       -       - rw---  libnsl.so.1
FEF50000      44      40       -       - r-x--  libsocket.so.1
FEF6B000       4       4       -       - rw---  libsocket.so.1
FEF70000       4       4       4       - rwx--    [ anon ]
FEF80000     144     132       -       - r-x--  libcurses.so.1
FEFB4000      28      24       -       - rw---  libcurses.so.1
FEFBB000       8       -       -       - rw---  libcurses.so.1
FEFC0000       4       4       -       - r-x--  libdl.so.1
FEFC7000     140     140       -       - r-x--  ld.so.1
FEFFA000       4       4       4       - rwx--  ld.so.1
FEFFB000       8       8       4       - rwx--  ld.so.1
-------- ------- ------- ------- -------
total Kb    2528    2072      80       -
bash-3.00$ 

리눅스에서 동일한 정보는 아래의 그림 1 을 참고 바랍니다. 알아둘 점은 리눅스는 라이브러리의 완전한 경로 이름을 보여 준다는 것입니다.(결과는 라이브러리 이름을 보여주기 위해 약간 수정 되었음) 솔라리스에서 라이브러리의 완전한 경로 이름을 얻기 위해서는 pldd(1)를 사용합니다 .

 
사용자 삽입 이미지
그림 1: 리눅스에서 프로세스에 의해 사용된 가상 공간 주소를 검사


쓰레드

리눅스와 솔라리스는 POISIX 쓰레드를 지원 합니다. 리눅스는 The Native POSIX Thread Library for Linux 를 통해서 솔라리스는 표준 C 라이브러리의 일부로 제공하고 있습니다. Multithreaded Programming Guide 에 특별히 Chapter 5 Programming with the Solaris Software,를 참고하시기 바랍니다. Multithreading in the Solaris Operating Environment 문서도 추천할 만 합니다.

POSIC 쓰레드에 덧붙여서 솔라리스는 "솔라리스 쓰레드"를 제공합니다. threads(5) 맨 페이지는 POSIX 쓰레드 라이브러와 솔라리스 쓰레드 라이브러리의 유사점과 차이점에 대해 설명하고 있습니다. 구현은 상호 운용이 가능하고 동일한 어플리케이션에서 조금의 주의를 기울이면 같이 사용할 수 있습니다. 다음의 내용은 맨 페이지에서 바로 가져온 것입니다.

동일점

libpthread 와 libthread 라이브러리에 존재하는 대부분의 함수들은 각각의 라이브러리에 동일한 역할을 하는 함수를 가지고 있습니다. 세마포어 이름을 제외한 POSIX 함수의 이름은 "pthread" 프리픽스를 가지고 있습니다. POSIX와 솔라리스의 비슷한 함수 들은 유사한 끝부분을 가지고 있습니다. 전형적으로 POSIX 함수와 솔라리스 함수들은 같은 숫자의 인수들을 가지고 있습니다.

차이점
  • POSIX 쓰레드가 좀더 포팅이 용이 합니다.
  • POSIX 쓰레드는 설정이 가능한 속성 오브젝트에 따라 각 쓰레드에 특성을 가진 쓰레드를 만드는 것이 가능합니다.
  • POSIX 쓰레드는 쓰레드 취소를 구현하고 있습니다.
  • POSIX 쓰레드는 스케쥴링 알고리즘을 준수하고 있습니다.
  • POSIX pthreads 는 fork(2) 콜을 위한 클린-업 핸들러를 허용하고 있습니다.
  • 솔라리스 쓰레드는 중지/재개가 가능합니다.
  • 솔라리스 쓰레드는 견고한 뮤텍스 락을 가진 인터프로세스를 구현하고 있습니다.
  • 솔라리스 쓰레드는 데몬 쓰레드를 구현하여 프로세스를 종료할때 대기하지 않아도 됩니다.

아래의 프로그램은 간단한 멀티 쓰레드 프로그램입니다. 각 OS 에서 멀티 쓰레드 어플리케이션이 동작하는데에는 큰 차이가 없음을 알 수 있습니다. 물론 바닥에 있는 구현방법에는 몇가지 차이점이 있을 것입니다.

#include <pthread.h>
#include <stdio.h>

void *fcn(void *);

int
main(int argc, char *argv[])
{
    pthread_t tid;

    pthread_create(&tid, NULL, fcn, NULL);
    (void) printf("main thread id = %x\n", pthread_self());
    pthread_join(tid, NULL);
}

void *
fcn(void *arg)
{
    printf("new thread id = %x\n", pthread_self());
}

다음을 이용해서 솔라리스 플랫폼에서 프로그램을 컴파일 하고 수행 시킵니다:

bash-3.00$ cc simplepthread.c -o simplepthread
bash-3.00$ ./simplepthread
main thread id = 1
new thread id = 2
bash-3.00$ 

솔라리스에서 gcc 를 사용 하면 똑같은 결과를 얻을 수 있을 것입니다. 리눅스에서는 다음과 같이 나타 납니다:

max@linux:~/source> cc simplepthread.c
/tmp/cc8u7kZs.o(.text+0x1e): In function `main':
simplepthread.c: undefined reference to `pthread_create'
/tmp/cc8u7kZs.o(.text+0x4a):simplepthread.c: undefined reference
       to `pthread_join'
collect2: ld returned 1 exit status
max@linux:~/source> cc simplepthread.c -lpthread -o simplepthread
max@linux:~/source> ./simplepthread
main thread id = 4015c6c0
new thread id = 4035cbb0
max@linux:~/source> 

리눅스에서 POSIX 쓰레드 라이브러니는 반드시 명시적으로 링크되어야 합니다. 알아둘 점은 솔라리스 9 혹은 그 전에 버젼 역시 명시적으로 링크되어야 한다는 것입니다. 솔라리스10에서 POSIX 쓰레드는 표준 C 라이브러리에 존재 합니다. (libc.so). 또 한가지 알아 둘 점은 솔라리스는 1부터 꾸준히 증가하는 숫자를 쓰레드 ID로 지정 한다는 것입니다. 리눅스는 pthread 구조체의 사용자영역 가상 주소를 사용 합니다(구조체는 내부적으로 쓰레드 라이브러리에서 사용됨).

쓰레드는 두 시스템 모두에서 ps(1) 커맨드 혹은 /proc 파일 시스템을 통해 보여질 수 있습니다. 그림 2는 솔라리스의 ps(1) 커맨드 출력을 나타 내고 그림 3은 리눅스에서의 출력을 나타 냅니다. 여기서 알 수 있는 것은 똑같은 옵션을 주었을때 두 머신에서의 출력은 유사 하다는 것입니다.

사용자 삽입 이미지
그림 2: 솔라리스에서의 ps(1) 커맨드 출력


 
사용자 삽입 이미지
그림 3: 리눅스에서의 ps(1) 커맨드 출력


이 커맨드는 상태, 유저, PID, 부모 PID, LWP ID, LWP의 숫자 (유저 프로세스를 위한 숫자. 이것은 쓰레드의 숫자를 의미함), 스케줄링 클래스, 스케줄링 우선순위, 유저영역 가상 사이즈, 대기 채널, 시작 시간, tty, 수행 시간, 그리고 커맨드등을 보여 줍니다. 리눅스 ADDR 를 리포트 하지 않습니다. 그리고 솔라리스는 커널이 프로세스를 유지하는데 사용하는 proc_t 자료 구조의 (커널) 가상 어드레스를 보여 줍니다. 리눅스는 WCHAN 을 심볼로 보여 주고 솔라리스는 그것을 주소로 보여 줍니다. 솔라리스에서 WCHAN 컬럼은 쓰레드가 블록됐을때 동기화 변수의 주소를 나타 냅니다. 리눅스에서 WCHAN 은 쓰레드가 sleep 상태 일때의 루틴을 나타냅니다. 솔라리스에서 똑같은 정보를 얻기 위해서는 mdb -k에서 ::threadlist -v 를 입력 합니다.

알아둘점은 64-비트 커널이 돌아가는 머신(SPARC 혹은 AMD64 아키텍쳐) 에서 ADDR WCHAN 필드는 물음표를 나타낼 것입니다. 이 두 변수의 값을 보기 위해서는 ps -e -o addr,wchan,comm 을 입력합니다.

보통 사용자는 어플리케이션 쓰레드가 무엇을 하는지에 대해 관심이 많을 것입니다. 이럴때에는 pstack(1) 을 이용 합니다. 리눅스에서는 pstack 을 이용합니다. 그러나 반드시 이것은 다운로드 받아져야 합니다. http://rpmfind.net/linux/RPM/ 를 참조 바랍니다. 주의할 점은 이것은 오직 하나의 쓰레드의 백트레이스 스택 많을 제공 한다는 것입니다(쓰레드 ID가 인수의 하나로 넘겨짐) 만약 모든 쓰레드의 백트레이스를 원한다면 쓰레드 ID를 하나의 분리된 인수로 넘겨 주어야 합니다.

 <-- get user-level stack(s) of a process on Solaris -->
bash-3.00$ pstack `pgrep mozilla-bin` 
21528: /usr/sfw/bin/../lib/mozilla/mozilla-bin -UILocale en-US
-----------------  lwp# 1 / thread# 1  --------------------
 fef68967 pollsys  (896dac8, 9, 0, 0)
 fef2b2aa poll     (896dac8, 9, ffffffff) + 52
 fe793242 g_main_context_iterate () + 39d
-----------------  lwp# 2 / thread# 2  --------------------
 fef68967 pollsys  (fbf5bd04, 1, 0, 0)
 fef2b2aa poll     (fbf5bd04, 1, ffffffff) + 52
 fede047d _pr_poll_with_poll (816fa0c, 1, ffffffff, fbf5bf64,
                                 fc0558aa, 816fa0c) + 2d5
 fede05f1 PR_Poll  (816fa0c, 1, ffffffff) + 11
 fc0558aa __1cYnsSocketTransportServiceEPoll6M_i_ (816f6b8) + 58
 fc055f7d __1cYnsSocketTransportServiceDRun6M_I_ (816f6b8) + 18f
 fc3d1262 __1cInsThreadEMain6Fpv_v_ (816eb60) + 32
 fede1693 _pt_root (816fcc0) + 9e
 fef67b30 _thr_setup (feec2400) + 51
 fef67f40 _lwp_start (feec2400, 0, 0, 0, 0, 0)
-----------------  lwp# 4 / thread# 4  --------------------
 fef67f7b lwp_park (0, fa87deb8, 0)
 fef620bb cond_wait_queue (825cfec, 816b8d0, fa87deb8, 0) + 3e
 fef62462 cond_wait_common (825cfec, 816b8d0, fa87deb8) + 1e9
 fef62691 _cond_timedwait (825cfec, 816b8d0, fa87df38) + 4a
 fef62722 cond_timedwait (825cfec, 816b8d0, fa87df38) + 27
 fef62761 pthread_cond_timedwait (825cfec, 816b8d0,
                                    fa87df38) + 21
 feddc598 pt_TimedWait (825cfec, 816b8d0, f1c) + b8
 feddc767 PR_WaitCondVar (825cfe8, f1c) + 64
 fc3d417e __1cLTimerThreadDRun6M_I_ (81e5108) + 16e
 fc3d1262 __1cInsThreadEMain6Fpv_v_ (820d690) + 32
 fede1693 _pt_root (820e6b0) + 9e
 fef67b30 _thr_setup (fb520400) + 51
 fef67f40 _lwp_start (fb520400, 0, 0, 0, 0, 0)
bash-3.00$ 

리눅스에서의 동일한 결과가 아래에 있습니다. 흥미로운 것은 Mozilla나 xemacs 같은 프로그램은 리눅스에서 strip 되 있고 솔라리스에서는 strip 되있지 않습니다.

max@linux:~> cd /proc/`pgrep mozilla`/task
max@linux:/proc/3991/task> pstack *

3991: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (8803488, 8, ffffffff, 8803488, 9, 400fbea0) + 40
0x404b0a6d: ???? (8129258, 4035236c, 57f, 4011e4e6, 4048de14,
                   403513c4) + 20
0x404b0d07: ???? (814b898, 814b898, 0, 0, 415a8f64, 814b898) + 30
0x401dc11f: ???? (8106350, bfffee80, bfffede8, 807673e, 8084cf4, 0)
0x415c4006: ???? (8106350, 0)
0x414fbae4: ???? (8105ee8, 0, 8079c2c, bfffee90, 80a67b8,
                      40ad841c) + 1f0
0x08059b7c: ???? (80e7f08, bffff058, 40017068, 14, 4081ccf8,
                       1) + 90
0x08055a47: ???? (1, bffff134, bffff13c, 4081ccf8, 406eebd0,
                     400168c0) + 40
0x405f2500: ???? (8055840, 1, bffff134, 80557b0, 8055740, 
                     4000d330) + 40000ed8

4001: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (413eb7f0, 1, ffffffff, 18, 413eb7f8, 0) + 230
0x400c7439: ???? (818911c, 1, ffffffff, 40c5a0a8, ffffffff,
                   8188dec)
0x40bc8a52: ???? (8188dc8, 8188df4, 1, 8188dec, 8188f7c, 1) + 10
0x40bc8bcb: ???? (8188dc8, 413ebbb0, 40102ce0, 400d5238,
                   8189478, 0)
0x40a8da6b: ???? (81893f8, 8189478, 4000ca40, 40102be8, 0, 0)
0x400cb7a6: ???? (8189478, 413ebac4, 0, 0, 0, 0) + 54
0x400fa9dd: ???? (413ebbb0, 0, 0, 0, 0, 0) + bec144d4

4004: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (40656756, 400d5238, 81ed160, 81ed2d0, 41ffba08,
                   400c5721) + 170fd55
crawl: Input/output error
Error tracing through process 4004
0x1afcdbf8: ????max@linux:/proc/3991/task> 

솔라리스 쓰레드는 기본적으로 유저 스택 사이즈를 1MB로 지정 합니다. 리눅스에서는 2MB 입니다.(수세 9.1)

동기화

두 운영체제는 POSIX 동기화 메카니즘을 지원합니다(예: 뮤텍스, 조건 변수, 읽기/쓰기 락, 세마포어, 베리어등) 기본적으로 메카니즘은 뮤텍스에 바탕을 두고 있습니다. 솔라리스에서 사용자-레벨의 뮤텍스는 "adaptive" 스핀 락을 사용해서 구현 됩니다. 리눅스에서 메카니즘은 "futex" 혹은 fast user level mutex 입니다. 두 메카니즘 모두 비경합 조건에서 커널에 들어가는 것을 회피 하고 반드시 비교할수 있을 만한 성능과 동작을 제공해야 합니다.

솔라리스 사용자-레벨의 "adaptive" 스핀 뮤텍스는 Multithreading in the the Solaris Operating Environment (pdf) 에 자세한 설명이 있습니다. 리눅스의 futex는 Futexes Are Tricky (pdf) 에 자세한 설명이 있습니다.

솔라리스 메카니즘인 lwp_park() lwp_unpark() 그리고 리눅스 메카니즘인 futex_up() futex_down() 은 어플리케이션에서 사용될 수 있습니다. 그러나 필자는 어떠한 소스 코드 예제도 찾지 못했습니다. 아마도 POSIX API들을 사용 하는 것이 가장 좋을 것입니다. 만약 POSIX 라킹 메카니즘에 상대적인 속도 차이를 비교하고자 한다면 (혹은 다양한 다른 라이브러리 루틴, 시스템 콜의 성능 같은) 필자는 libmicro 마이크로 벤치마크의 카피를 얻을 것을 관장하고 그것을 솔라리스와 리눅스에서 사용해 보길 권장 합니다. (libmicro는 여기 에서 다운로드 받으실 수 있습니다)

메모리 관리

커널의 메모리 다루는 방법에 대한 차이를 설명하는 대신 필자는 유저 레벨에서 몇가지 다른 메모리 할당 라이브러리가 존재 하고 대부분 두 OS 에서 사용 할 수 있음 만을 밝히려고 합니다. 유저레벨 메모리 할당자의 비교는 SDN의 A Comparison of Memory Allocators in Multiprocessors 에서 찾아 보실 수 있습니다. http://gee.cs.oswego.edu/dl/html/malloc.html 에 "A Memory Allocator" 는 리눅스에서 사용되는 (조금 지난) 메모리 할당자들에 대한 설명을 포함 하고 있습니다. 또한 소스 코드에서도 관련 커멘트를 찾아 볼 수 있습니다.

타이머

어플리케이션 레벨에서 솔라리스와 리눅스는 둘다 timer_create(), timer_delete(), 그리고 nanosleep() 를 포함한 POSIX 타이머 루틴을 제공합니다. 솔라리스는 추가적으로 CLOCK_HIGHRES 라는 타이머를 제공하며 이것은 최적의 하드웨어 소스를 사용하도록 시도 하고 nano 초 수준의 정밀함을 제공 할 것입니다. CLOCK_HIGH_RES 타이머는 아마 미슷한 수준의 정밀성을 리눅스에서 제공할 것입니다. 그러나 커널 패치의 일부로 설치 되어야 합니다.(http://high-res-timers.sourceforge.net/ 에서 고 정밀도 타이머 프로젝트의 홈페이지를 찾으실 수 있습니다). 다음의 예제 코드는 CLOCK_HIGHRES 타이머를 사용하여 유저가 지정한 간격, 유저가 지정한 시간 동안 타이머가 동작 하도록 합니다. 간격은 nano 초 수준으로 지정 되고 동작 시간은 초 수준으로 지정 됩니다. 프로그램이 실행을 완료 하면 타이머가 몇번 동작 했는지와 타이머가 "overrun" 했는지 숫자로 보여 줍니다. 여기서 "overrun" 값이란 타이머가 처음 동작한 시간(시그널이 생성되도록 함)과 시그널이 처리되는 시간 (timer_getoverrun(3RT) 참조) 과의 차이를 의미 합니다. 간격을 너무 짧게 줄 경우 시스템이 정지해 버릴 수도 있습니다.

#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

#include <errno.h>

#define DURATION 120    /* default time to run in seconds */

  /* default .5 seconds in nanosecs */
#define INTERVAL (1000*1000*500)

void* timer_fcn(void* arg);
void* signaler_thd(void* arg);

/* Program globals */
extern int errno;
int duration = DURATION;
int interval = INTERVAL;

int
main(int argc, char *argv[]) 
{
   sigset_t mask;
   pthread_t wtid = 0;
   pthread_t stid = 0;
   int rval;
   int n;

   if (argc >=2) {
       errno = 0;
       if (argc == 2)
         duration = strtol(argv[1], NULL, 0);
       else if (argc == 3) {
         interval = strtol(argv[1], NULL, 0);
         duration = strtol(argv[2], NULL, 0);
       }
       if (errno || argc > 3 || interval <= 0
          || duration <= 0) {
           fprintf(stderr, "Usage: %s [[interval] duration]\n",
                  argv[0]);
           fprintf(stderr, "interval nsecs, duration seconds\n");
           exit(1);
       }
   }
     
   /* mask SIGALRM signals */
   sigemptyset(&mask);
   sigaddset(&mask, SIGALRM);
   sigaddset(&mask, SIGUSR1);
   rval = pthread_sigmask(SIG_BLOCK, &mask, NULL);
   if(rval != 0) {
      printf("%s: pthread_sigmask failed, errno = %d.\n",
             argv[0], rval);
      exit(1);
   }

   rval = pthread_create(&wtid, NULL, timer_fcn, NULL);
   if (rval != 0) {  /* Waiter create call create failed */
    perror ("Waiter create");
    printf ("Waiter create call failed: %d.\n", rval);
    exit (1);
    }


   /* Do signaler thread */
   rval = pthread_create(&stid, NULL, signaler_thd, &mask);
   if (rval != 0) {  /* Signaler call create failed */
    printf ("Signaler call create failed: %d.\n", rval);
    exit (1);
   }

   /* Wait for waiter and signaler to finish */    
   rval = pthread_join(stid, NULL);
   if (rval != 0) {  /* Signaler call join failed */
    printf ("Signaler call join failed: %d.\n", rval);
    exit (1);
   }

   rval = pthread_join(wtid, NULL);
   if (rval != 0) {  /* Waiter call join failed */
    printf ("Waiter call join failed: %d.\n", rval);
    exit (1);
   }

   printf("done\n");
   exit(0);
}

pthread_mutex_t mp;
pthread_cond_t cv;
int time_expired = 0;
int timerentered;
int timeroverrun;
timer_t itimerid;

void *
timer_fcn(void *arg)
{
  struct itimerspec value;
  struct sigevent event;

  value.it_interval.tv_sec = 0;
  value.it_interval.tv_nsec = interval;  /* nsec intervals  */
  value.it_value.tv_sec = 1;  /* starting in 1 second */
  value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

  event.sigev_notify = SIGEV_SIGNAL;
  event.sigev_signo = SIGALRM;
  event.sigev_value.sival_int = 0;
  

  if (timer_create(CLOCK_HIGHRES, &event,
     &itimerid) == -1) {
       perror("timer_create failed");
       exit(1);
  }

  /* the second arg can be set to TIMER_ABSTIME */
  if (timer_settime(itimerid, 0, &value, NULL) == -1) {
      /* else time value is relative to when the call is made */
    perror("timer_settime failed");
    exit(1);
  }

  pthread_mutex_lock(&mp);
  while (time_expired == 0)
    pthread_cond_wait(&cv, &mp);
  printf("timerentered = %d\n", timerentered);
  printf("timeroverrun = %d\n", timeroverrun);
  pthread_mutex_unlock(&mp);
  exit(0);
}

int timerset;

void *
signaler_thd(void *arg)
{
    int signo;
    
    while (1) {
      signo = sigwait(arg);
      if (signo == SIGALRM) {
       if (!timerset) {
        struct itimerspec value;
        struct sigevent event;

        timer_t endtimerid;

        ++timerset;
        value.it_interval.tv_sec = 0;
        value.it_interval.tv_nsec = 0;
        value.it_value.tv_sec = duration; /*wait duration secs*/
        value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

        event.sigev_notify = SIGEV_SIGNAL;
        event.sigev_signo = SIGUSR1;
        event.sigev_value.sival_int = 0;
  

        if (timer_create(CLOCK_HIGHRES, &event,
         &endtimerid) == -1) {
           perror("timer_create failed");
           exit(1);
        }

        /* the second arg can be set to TIMER_ABSTIME */
        if (timer_settime(endtimerid, 0, &value, NULL)
          == -1) {
          perror("timer_settime failed");
          exit(1);
        }
       } else {  /* if (!timerset) */
        ++timerentered;
        timeroverrun += timer_getoverrun(itimerid);
       }
      } else {  /* SIGUSR1 */

       struct itimerspec value;
       struct sigevent event;


       /* cancel the interval timer */
       value.it_interval.tv_sec = 0;
       value.it_interval.tv_nsec = 0;  /* nanosecond intervals */
       /* setting the following to 0 should stop the timer */
       value.it_value.tv_sec = 0;
       value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

       event.sigev_notify = SIGEV_SIGNAL;
       event.sigev_signo = SIGALRM;
       event.sigev_value.sival_int = 0;
  
       pthread_mutex_lock(&mp);
       if (timer_settime(itimerid, 0, &value, NULL) == -1) {
        perror("timer_settime failed");
        exit(1);
       }

       ++time_expired;
       pthread_cond_signal(&cv);
       pthread_mutex_unlock(&mp);
      }
   }
}

컴파일 된 코드의 실행 예가 있습니다.

  <-- realtime library and best optimization -->
bash-3.00$ cc timerex1.c -lrt -o timerex1 -O -fast
bash-3.00$ ./timerex1  <-- only root can use high res timer
timer_create failed: Not owner
bash-3.00$ su
Password: 
  <-- default interval is .5 seconds, duration is 120 seconds -->
# ./timerex1  
timerentered = 240  <-- timer fired every .5 seconds
timeroverrun = 0
# ./timerex1 1000000 10  <-- interval is 1 msec for 10 secs
timerentered = 9912
timeroverrun = 88
# priocntl -e -c RT ./timerex1 1000000 10  <-- run it real time
timerentered = 10000  <-- timer fired once each msec for 10 secs
timeroverrun = 0
# ./timerex1 100000 10  <-- interval is 100 usecs for 10 seconds
timerentered = 99615  <-- we missed a few
timeroverrun = 386
# priocntl -e -c RT ./timerex1 100000 10  <-- try real time 
timerentered = 99871  <-- almost 1 every 100 microseconds
timeroverrun = 129
# ./timerex1 10000 10  <-- interval is 10 microseconds
timerentered = 485905  <-- here we miss over half
timeroverrun = 514125  <-- (sig handler takes > 10 usecs?)
 <-- using RT 1 usec interval causes hang on my machine -->

# priocntl -e -c RT ./timerex1 1000 10 

IPC

솔라리스와 리눅스 둘다 시스템 V IPC (공유 메모리, 메세지 큐, 세마포어)를 지원 합니다. 두 시스템 모두 파이프와 실 시간 공유 메모리 작업(shm_open(), shm_unlink(), 등등)을 지원합니다. 두 시스템 모두 tmpfs 파일 시스템을 지원합니다(파일을 위해 메모리와 스왑 공간을 사용) 솔라리스는 tmpfs 내에 /tmp, /var/run, 그리고 /etc/svc/volatile 을 위치 시킵니다 리눅스는 /dev/shm 을 사용합니다. 두 시스템 모두 다른 마운트 지점을 추가 하는 것을 허용 합니다.

솔라리스에서 tmpfs 를 사용 하는 과정이 설명 되어 있습니다; 리눅스를 위한 과정은 아래에 설명 됩니다. 알아둘 점은 솔라리스의 "swap"은 메모리와 디스크를 둘다 사용 합니다. (디스크는 필요시에만) 다시 말해서 /tmp 파일에 생성되는 파일은 메모리에 저장 됩니다. 메모리가 꽉 찰 경우 pageout 데몬이 /tmp 에서 데이타를 디스크에 스왑 스페이스 옮기게 됩니다.

# mkdir /foo
<-- create a tmpfs file system using swap on /foo
# mount -F tmpfs swap /foo  
# df -h /foo
Filesystem         size   used  avail capacity  Mounted on
swap           652M     0K   652M     0%    /foo
# df -h /tmp
Filesystem         size   used  avail capacity  Mounted on
swap           652M    52K   652M     1%    /tmp
# 

아래에 리눅스상에서 비슷한 과정을 설명합니다.

linux:/home/max # mkdir /foo
 <-- tmpfs also uses swap space and memory -->
linux:/home/max # mount tmpfs /foo -t tmpfs 
linux:/home/max # df -h /foo
Filesystem        Size  Used Avail Use% Mounted on
tmpfs         248M     0  248M   0% /foo
linux:/home/max # df -h /dev/shm
Filesystem        Size  Used Avail Use% Mounted on
tmpfs         248M   16K  248M   1% /dev/shm
linux:/home/max # 

이 글의 앞부분에서 언급한대로 libmicro 벤치마크를 수행하여 두 시스템의 상대적인 성능을 비교해 보는 것은 꽤 흥미로울 것입니다.

시그널 핸들링

솔라리스와 리눅스는 시그널을 비슷하게 처리 합니다. 솔라리스에 존재하는 몇몇 시그널은 리눅스에 존재 하지 않고 그 반대도 마찬가지 ?니다. 또한 몇몇 시그널은 다른 시그널 번호를 사용 합니다. 두가지 모두 signal() 위에 sigaction(2) 을 사용하여 시그널을 캐치 하고 멀티 쓰레드 어플리케이션에서 sigwait() 를 사용 하여 비동기 시그널을 처리 합니다. 리눅스에서 sigwait(3) 메뉴얼 페이지는 BUGS 섹션을 가지고 있습니다. 리눅스 시그널 핸들링은 POSIX 표준과는 다릅니다. POSIX에서 비동기 적으로 전달 되는 시그널(프로세스외 외부적으로 전달 되는 시그널)은 현재 블럭된 시그널을 가지고 있지 않는 모든 쓰레드에 의해 처리 됩니다. 리눅스에서 비동기 시그널은 어떤 특정한 쓰레드(시그널은 kill(1)을 통해 특정 쓰레드로 전달될 수 있음)에 전달 됩니다. 솔라리스는 이 경우 POSIX 표준을 구현 합니다. 즉 프로세스의 특정한 쓰레드에 직접적으로 시그널을 전달 할 수 있는 방법은 없습니다. 하나의 방법으로는 프로세스에 특정한 쓰레드로가 아니라 kill(1) 을 통해 프로세스로 전달 하는 것이 있습니다.

http://lsbbook.gforge.freestandards.org/sig-handling.html 에 있는 "Building Applications with the Linux Standard Base"는 몇가지 이러한 차이점들을 기술 하고 있습니다. 주의할 점은 이 페이지는 완벽하게 정확하지는 않다는 것입니다. 예를 들어 이 페이지에서 리눅스에서는 "버스 에러"가 없다는 것을 나타내기 위해 SIGBUSSIGUNUSED 로 지정한다고 설명되어 있습니다. 리눅스의 mmap(2) 맨 페이지에서 설명하고 있는 바로는 mmap 이 사용 되었단 파일에서의 올바른 위치와 부응하지 않는 메모리 범위를 엑세스 할때 SIGBUS 를 받게 된다고 되어 있습니다. (솔라리스도 똑같이 동작함).

솔라리스와 리눅스 두 OS 모두 시그널은 커널에서 유저 모드로 쓰레드가 돌아갈때 pending 된 시그널이 발견 되었을 시에 처리 됩니다. 두 시스템 모두 SIGKILLSIGSTOP 은 다른 시그널에 비교해서 우선순위가 높습니다. 그렇지 않다면 솔라리스에서 시그널은 문서화되지 않은 순서대로 처리 됩니다(낮은 시그널 숫자가 먼저 처리 됨). 리눅스에서 시그널은 전달된 순서대로 처리 됩니다. (SIGKILLSIGSTOP 은 예외).

솔라리스에서 수행중인 프로세스의 시그널 설정을 보기 위해서는 psig 를 사용합니다.

bash-3.00$ psig $$  <-- signal disp for current shell
954:	/usr/bin/bash -i
HUP	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
INT	caught	sigint_sighandler	0
QUIT	ignored
ILL	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
TRAP	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
ABRT	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
EMT	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
FPE	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
KILL	default
BUS	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
SEGV	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
SYS	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
PIPE	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
ALRM	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
TERM	ignored
USR1	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
USR2	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
CLD	blocked,caught	0x807d4d7 	0
PWR	default
WINCH	caught	0x807e182   0  <-- not all syms are present
URG	default
POLL	default
STOP	default
TSTP	ignored
CONT	default
TTIN	ignored
TTOU	ignored
VTALRM	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
PROF	default
XCPU	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
XFSZ	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
WAITING	default
LWP	default
FREEZE	default
THAW	default
CANCEL	default
LOST	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
XRES	default
JVM1	default
JVM2	default
RTMIN	default
RTMIN+1	default
RTMIN+2	default
RTMIN+3	default
RTMAX-3	default
RTMAX-2	default
RTMAX-1	default
RTMAX	default
bash-3.00$ 

필자가 얘기 할 수 있는 것은 리눅스에서 이러한 작업을 쉽게 할 수 있는 방법은 없습니다. 그러나 몇몇 개발자들이 아마 커널 패치/모듈을 구현 해서 개발자가 정보를 얻을 수 있도록 했습니다.

결론

일반적으로 리눅스 혹은 솔라리스에서 개발자가 POSIX-호환의 어플리케이션을 개발 하고 있다면 어플리케인션 반드시 간단한 재컴파일 과정을 통해 각 OS로 포팅 되어야 합니다. 물론 많은 어플리케이션은 POSIX 와 부합하지 않는 부분을 가지고 있습니다. 예를 들어 디바이스ioctl(2) 은 OS에 따라 다르게 되어 있습니다.(그리고 물론 디바이스에 따라)

솔라리스를 위한 문서를 얻는 것은 이성적으로 매우 직관적입니다. 왜냐하면 모든 문서가 http://docs.sun.com에 존재하기 때문입니다. 물론 리눅스를 위한 문서를 얻는 과정도 간단하지만 (웹검색을 통해) 종종 어려울 경우도 있습니다. 개발자는 리눅스가 전형적으로 동일한 작업을 하기 위해 많은 방법이 있음을 발견하게 될 것입니다.(예를 들어 쓰레드의 구현 차이 등) 필자의 개인적은 인상은 리눅스의 문서는 종종 소스 코드 자체가 될 수도 있습니다. 만약 소스 코드에 대한 전체 접근이 가능할 경우 이것은 괜찮은 방법입니다. 개발자는 모든 소스 코드에 대한 접근 권한을 물론 가지고 있지만 이것이 한 장소에 모여 있지는 않습니다. 사실 그것은 여러군데에 퍼져 있는 것처럼 보입니다. 썬의 소스는 현재 (http://www.opensolaris.org) 한군데에 모여 있습니다.

이 글은 두 시스템에서 사용 가능한 툴을 모두 사용하였지만 자세한 부분 까지 파고 들지는 않았습니다. 썬이 오픈솔라리스로 전환하기 전에 리눅스의 발표는 소스 코드에 대한 가시성 면에서 봤을때 항상 어떻게 작업이 돌아 가는지 알 수 있도록 소스 코드가 항상 공개 되있으므로 솔라리스에 비해 한발짝 앞서 있었습니다. 이제는 오픈솔라리스의 DTrace 같은 툴을 통해 리눅스가 솔라리스를 따라와야 하는 상황입니다. 리눅스의 변화 속도를 볼때 필자가 확신 할 수는 없지만 그 시간이 오래 걸리지는 않을 것입니다. 필자는 각 시스템의 서로의 장점과 실수를 배움으로써 상호 보완관계로 발전하기를 바랍니다.

출처 : SDN Korea

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 
« Previous : 1 : ... 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : ... 33 : Next »