<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>geeks-log</title>
        <link>https://velog.io/</link>
        <description>Work as though your strength were limitless. &lt;S. Bernhardt&gt;</description>
        <lastBuildDate>Fri, 10 May 2024 01:43:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>geeks-log</title>
            <url>https://velog.velcdn.com/images/gigis-note/profile/d02ddb68-c7c1-490d-9bc0-3ea00eef4ebf/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. geeks-log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gigis-note" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Property-based injection(속성기반주입)]]></title>
            <link>https://velog.io/@gigis-note/Property-based-injection%EC%86%8D%EC%84%B1%EA%B8%B0%EB%B0%98%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@gigis-note/Property-based-injection%EC%86%8D%EC%84%B1%EA%B8%B0%EB%B0%98%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Fri, 10 May 2024 01:43:34 GMT</pubDate>
            <description><![CDATA[<h1 id="property-based-injection속성-기반-주입">Property-based injection(속성 기반 주입)</h1>
<p>생성자 기반 주입이 아닌 속성 기반 주입에 대해 알아볼 것이다. 그 전에 알아두어야 하는 것은 만일 클래스가 또다른 클래스를 상속하지 않는다면 웬만해선 생성자 기반 주입을 사용하도록 공식문서에서는 권장하고 있다.</p>
<h3 id="언제-property-based-주입을-사용하면-좋은가">언제 property-based 주입을 사용하면 좋은가?</h3>
<p>최상위 클래스가 하나 혹은 여러개의 provider에 의존하는 경우 원래라면 이를 상속하는 모든 하위 클래스들의 내부 생성자에서 super()를 호출하여 provider를 전부 전달해야 한다.</p>
<p>이러한 귀찮은 작업을 하지 않기 위해 속성 수준(멤버 변수 필드)에서 <strong>@Inject()</strong> 데코레이터를 사용할 수 있다.</p>
<pre><code class="language-javascript">import { Injectable, Inject } from &#39;@nestjs/common&#39;;

@Injectable()
export class HttpService&lt;T&gt; {
  @Inject(&#39;HTTP_OPTIONS&#39;)
  private readonly httpClient: T;
}</code></pre>
<p><a href="https://cdragon.tistory.com/entry/NestJS-Provider-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-Custom-Provider">출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[크래프톤 정글] 5개월간의 여정]]></title>
            <link>https://velog.io/@gigis-note/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4-%EC%A0%95%EA%B8%80-5%EA%B0%9C%EC%9B%94%EA%B0%84%EC%9D%98-%EC%97%AC%EC%A0%95</link>
            <guid>https://velog.io/@gigis-note/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4-%EC%A0%95%EA%B8%80-5%EA%B0%9C%EC%9B%94%EA%B0%84%EC%9D%98-%EC%97%AC%EC%A0%95</guid>
            <pubDate>Tue, 05 Mar 2024 12:13:32 GMT</pubDate>
            <description><![CDATA[<h3 id="포기는-없다">포기는 없다!</h3>
<p>내가 정글에 오면서 세웠던 단 하나의 목표가 끝까지 수료하기였는데 모든 과정을 포기하지 않고 잘 수료했기에 굉장히 만족하고 나에게 칭찬한다. 솔직히 과정 자체가 굉장히 컴팩트하게 많은 것들을 배워야하고 스케줄도 살인적이여서 빨래할 시간도 부족하다.</p>
<p>참여하신 다른 분들도 힘들어했는데 특히 20대를 캐나다에서 보낸 나는 초반에 더욱 적응하기 힘든 환경이었던 것 같다. </p>
<p>야자를 한 적이 없던 나는 이러한 단체 생활도 처음이고, 
여중 여고였기에 남자 무리도 처음이었고, 
항상 내가 막내였다가 나보다 어린 여자 무리가 처음인것 등 모든게 적응이 너무 안되서,
첫 2개월 정도는 잠을 제대로 잔 적이 없었고 먹는 것도 잘 못해서 살이 10kg이 넘게 빠졌었다.</p>
<p>잠을 잘 못자니까 ADHD 증상이 너무 심해져서 일상생활이 어려울 정도였다. 그러니 당연히 정글의 커리큘럼을 따라가기에 굉장히 벅찼다. 한번은 한 문장을 읽는데에 한 시간이 넘게 걸렸었다.</p>
<p>성격도 많이 변했었다. 원래 굉장히 활발한 성격이라 많은 사람들과 이야기하고 친하게 지내는데, 굉장히 내성적으로 변해서 그렇게 싫어하던 혼밥을 하게 되면 내심 기쁠 정도였다.</p>
<p>원래 공부던 운동이던 뭐든 한 집단에서 열심히 하면 항상 상위권에 있던 나였기 때문에 무언가를 잘 못하는 나 자신을 정면으로 바라보면서 이 상황을 피하고 싶었다. 나의 100% 120%를 끌어내서 잘하고 싶고, 나도 남들을 도와주고 싶은데 나는 평소의 성능의 50%도 나오지 않았다. 내가 나를 받아들이는 것도 힘들고 남들이 나를 이런식을 인식하는 것도 정신적으로 견디기 힘들었다.</p>
<p>이 지경에 이르니 심각하게 건강을 생각해서 진지하게 퇴소를 고려했었는데, 버티고 싶었다. 내 생에 다시는 오지 않을 기회라 여겼고 나는 간절했다.</p>
<p>그래서 근처 병원에 가서 약 복용을 시작했고 나에게 맞는 약을 찾기까지 시간이 오래 걸리고 부작용에 힘들었지만 열심히 약을 먹고 열심히 적응해나가면서 극복하게 되었다.</p>
<p>사실 나에게는 이런 내부적인 어려움들이 있었기 때문에 정글의 미친 커리큘럼이나 나만무를 하면서 겪었던 체력적 어려움과 야근들은 상대적으로 그렇게까지 힘들다고 느껴지지 않았다.</p>
<p>오히려 어느정도 적응하고 나니 정글에서 동료들과 공부하고 일하는게 즐거웠다.</p>
<p>그렇게 나는 견뎌내어 결국엔 팀프로젝트까지 성공적으로 마칠 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/3f75c5ed-f392-40e1-a507-c72664d9aab2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[크래프톤 정글] 나만의무기 프로젝트 회고(5W)]]></title>
            <link>https://velog.io/@gigis-note/%EB%82%98%EB%A7%8C%EC%9D%98%EB%AC%B4%EA%B8%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A05W</link>
            <guid>https://velog.io/@gigis-note/%EB%82%98%EB%A7%8C%EC%9D%98%EB%AC%B4%EA%B8%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A05W</guid>
            <pubDate>Tue, 05 Mar 2024 11:55:36 GMT</pubDate>
            <description><![CDATA[<h2 id="팀이-어떤-것들을-했는지">팀이 어떤 것들을 했는지</h2>
<p>우리 팀은 <strong>맛스페이스</strong> 라는 맛집 추천 웹서비스를 만들었다. 친구들 혹은 동료들끼리 함께 갈 식당을 정할 때 서로 원하는 취향과 먹고 싶은 음식을 맞추기 위해 시간을 많이 사용한다. 그래서 우리는 각자 취향의 교집합 혹은 중간 지점을 구해주면 어떨까 하는 발상에서 시작되었다.</p>
<p>기능을 한 줄로 요약하면 <U>개인의 취향에 맞는 식당을 추천</U>해주고, <U>식당과 식당의 중간 지점에 있는 식당들을 추천</U>해준다. 자세한건 아래 노션페이지에서 확인할 수 있다.</p>
<p>[github link]
(<a href="https://github.com/Moojuck-KJ3/backend-python">https://github.com/Moojuck-KJ3/backend-python</a>)
<a href="https://www.notion.so/aca822e2ff304d919aa8a945efa6f80c?pvs=4">팀 소개 노션 페이지</a></p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/cfd9a1f1-5d6e-45d4-bd6c-331d969fb56c/image.png" alt=""></p>
<h2 id="내가-어떤-것들을-했는지">내가 어떤 것들을 했는지</h2>
<h3 id="추천-파이프라인-설계-및-알고리즘-구현">추천 파이프라인 설계 및 알고리즘 구현</h3>
<ul>
<li>selenium과 konlpy를 활용하여 데이터 수집 및 가공</li>
<li>word2vec을 파인튜닝하여 상용모델 Fasttext사용 대비 유사성 3.6배 가량 향상시킴</li>
<li>KNN알고리즘 사용하여 벡터 간 인접한 벡터를 구하여 서로 다른 식당 사이에 존재하는 식당 구하는 기능 개발<h3 id="백엔드-api-개발fastapi-restful">백엔드 API 개발(FastAPI-RESTful)]</h3>
</li>
<li>Python으로 백엔드 주요 기능 개발(연관 단어 맵핑 기능, 개인 식당 추천 기능, 교집합 식당 추천 기능)</li>
<li>FastAPI로 RESTful API 개발</li>
<li>python 백엔드 서버 Docker로 배포</li>
</ul>
<h3 id="추천-파이프라인-설계-및-알고리즘-구현python">추천 파이프라인 설계 및 알고리즘 구현(python)</h3>
<p>처음엔 단순하게 식당들이 가지고 있는 키워드 태그들을 가지고 각 식당을 벡터화 시켜서 식당 데이터셋을 꾸리고 A식당과 B식당 각각 특정 거리에 있는 이웃 식당들을 구하고 겹치는 교집합 이웃 식당들을 반환해주면 되겠다 라고 생각했는데, 실제로 해보니 겹치는게 존재하지 않는 경우가 많았다.</p>
<p>그래서 A벡터와 B벡터의 거리를 계산하고 그 중간 지점에서부터 이웃 식당들을 구하는 방식으로 변경했다. 거리를 계산하는데는 유클리드 알고리즘을 사용하였고 중간 지점에서부터 인접한 벡터들을 구할 때는 KNN알고리즘을 사용하였다. </p>
<p>벡터 데이터셋에서 특정 벡터로부터 인접한 벡터들을 구할 때 코사인유사도를 사용하지 않고 KNN을 사용한 이유는 코사인 유사도 알고리즘이 데이터셋이 비교적 작고 차원이 높은 경우에 특히 유용하다고 알려져 있지만 코사인 유사도 알고리즘은 유사도 임계값을 설정하여 인접한 벡터를 결정하기 때문에 특정 백터 주변에 인접한 백터가 없는 경우에는 해당 알고리즘으로 인접한 백터를 구할 수 없기 때문에 아무리 멀어도 그나마 가까이 인접해 있는 백터를 알아낼 수 있는 KNN알고리즘을 사용하였다.</p>
<p>서비스는 총 4명의 사용자가 각자 선택한 식당을 넣으면 조합된 식당을 확인할 수 있는 기능이 있는데 이 때에도 알고리즘을 비슷하게 위의 방식과 비슷하게 짰다. A, B, C, D 의 식당이 있을 때 A와 B의 식당의 중간값과 C와 D식당의 중간값, 그리고 이들의 중간 값을 4명의 중간값, 센터값이라고 했을 때, 센터와 A의 중간값, 센터와 B의 중간값, 이런식으로 식당을 추천하였다.</p>
<p>그런데 아쉬운 점은 결국 내가 추천하는 식당은 백터 A와 B 사이의 중간 지점에 있는 백터 즉, A와 B로부터 가장 먼 백터였다. 그러니까, A와 B가 멀다면 이들의 중간 지점이 엉뚱하게 보일 수 있다는 것이다.
<img src="https://velog.velcdn.com/images/gigis-note/post/5af3c3ed-347b-41a0-aa28-76cf29f881ad/image.png" width="300" height="300"></p>
<p>그래서 완전 중간에 인접해 있는 백터들이 아니라 어느정도 떨어져 있는 백터들을 추천해 주었어야 했으면 더 합리적으로 보일 수 있었을 것 같다. (그랬더라면 시연때 최소한 4명의 중간 식당으로 디저트 가게가 나오는 참사는 막을 수 있었을 것 같기도...ㅠㅠ)</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/93f46317-abd2-471f-a881-97fb197f0b21/image.png
" width="300" height="300"></p>
<p>지금 코드 구현된 방식은 가장 가까이 인접한 벡터를 우선적으로 보여주는데 위와 같이 보여주고 싶으면 5번인덱스부터 7번인덱스 사이에 골라서 보여주는 방식으로 간단하게 바꿔볼 수도 있을 것 같다.</p>
<p>생각치도 못했는데 해보지 못해 아쉬운 점이 또 있는데 이미 잘 알려진 성능평가방법을 활용하여 알고리즘의 성능을 평가해보지 못한 것이다. 추후에 알고리즘을 개선한다면 순위 기반의 Precision, Recall, MAP, NDCG의 지표를 활용하여 알고리즘의 성능이 개선되는 정도를 확인하며 개발할 수 있을 것 같다.</p>
<h3 id="자체-언어-모델-구현">자체 언어 모델 구현</h3>
<p>단어를 백터라이징 하기 위해서는 언어모델이 필요한데 처음에 모르는 단어에 대한 대응이 word2vec 보다 뛰어나고 노이즈가 많은 코퍼스에서 강점을 가지는 fasttext를 선택하여 사용하였다. 그런데 실제로 돌려보니 유사성이 생각보다 좋지 않아서 사용하기 어려운 정도였다. 유사성은 숫자가 낮을 수록 유사한 것이라고 보는데, 유사하다고 생각하는 식당들이 덜 유사하다고 생각되는 식당들보다 유사성이 떨어지는 결과가 나온 것이다. 
(유사성은 유클리드거리의 거리로 판단했다.)</p>
<p>예를들어, 아래의 두 식당의 경우 사용한 분위기 태그가 비슷하니까 숫자가 낮을 것으로 예상한다.</p>
<blockquote>
<p>나폴리회관 강남역점,&quot;[&#39;깔끔한&#39;, &#39;시끌벅적한&#39;]
청담이상 강남역점,&quot;[&#39;예쁜&#39;, &#39;시끌벅적한&#39;]
<strong>유클리드 거리: 1.205982344248324</strong></p>
</blockquote>
<p>그런데 유사성이 떨어져보이는 아래의 두 식당이 위의 식당보다 숫자가 더 높아야하는데 오히려 더 낮게 나오는 현상을 확인할 수 있었다.</p>
<blockquote>
<p>나폴리회관 강남역점,&quot;[&#39;깔끔한&#39;, &#39;시끌벅적한&#39;]
대진도원참치 강남역점 [&#39;조용한&#39;, &#39;고급스러운&#39;]
<strong>유클리드 거리: 0.6086120995497456</strong></p>
</blockquote>
<p>그래서 이대로 이 모델을 사용할 수 없는 상황이었고, 다른 모델을 사용해볼까 하다가 수집해놓은 리뷰데이터들을 가지고 모델을 학습시키면 어떨까 하고 바로 실험해 보았는데 fasttext보다 기대한대로 유사성이 높게 잘 나오는 것을 확인할 수 있었다.</p>
<blockquote>
<p>나폴리회관 강남역점,&quot;[&#39;깔끔한&#39;, &#39;시끌벅적한&#39;]
청담이상 강남역점,&quot;[&#39;예쁜&#39;, &#39;시끌벅적한&#39;]
<strong>유클리드 거리: 5.451761920716737</strong></p>
</blockquote>
<blockquote>
<p>나폴리회관 강남역점,&quot;[&#39;깔끔한&#39;, &#39;시끌벅적한&#39;]
대진도원참치 강남역점 [&#39;조용한&#39;, &#39;고급스러운&#39;]
<strong>유클리드 거리: 7.012567671092597</strong></p>
</blockquote>
<h3 id="문장을-단어단위로-tockenize연관단어-추출">문장을 단어단위로 tockenize/연관단어 추출</h3>
<p>사용자가 원하는 음식점의 카테고리나, 식당의 분위기에 대한 정보를 입력받을 때 음성인식으로 받기로 해서 파이썬 서버에서 문장을 단어 단위로 토크나이즈하고 연관 단어를 추출하여 사용자에게 보여주는 것을 구현하였다. 토크나이즈는 코앤엘파이를, 연관 단어는 word2vec의 similarity 함수를 사용하여 우리가 정의한 키워드 중에 유사성이 높은 단어들을 보여주는 형식으로 구현하였다. </p>
<h3 id="데이터-수집가공">데이터 수집/가공</h3>
<p>오픈API로 가져올 수 있는 데이터는 한계가 있었고 공공데이터도 생각보다 우리가 필요로하는 데이터의 한계가 있었다. 그래서 어쩔 수 없이 불법적이지만 크롤링도 실력이지 않나..하는 합리화를 되새기면서 크롤링을 시작하였다.</p>
<p>나는 사실 이번에 처음 데이터 크롤링을 시도해보았는데 생각보다 까다로운 작업이었다. 셀레니움으로 시도하였는데 잘 되는듯 하다가도 난관에 부딛히기 일쑤였다. 시간이 꽤 오래리고 있었는데 다른 백엔드 동료는 API   </p>
<h3 id="추천-관련-백엔드-api-명세개발-fastapi">추천 관련 백엔드 API 명세/개발 (FastAPI)</h3>
<p>Django, Flask, FastAPI 중에 FastAPI를 선택한 이유는 첫번째, 가볍고 (장고 탈락~) 두번째, 사용자수가 증가하고 있기 때문에 FastAPI를 선택하였다. fastAPI는 실제로 사용해보니 정말로 빠르고 가벼워서 만족도가 굉장히 높았다. 그리고 프로젝트 규모가 굉장히 작다보니 사용하기 아주 적절하지 않았나 싶다. </p>
<h2 id="가장-힘들었던-트러블">가장 힘들었던 트러블</h2>
<h3 id="단연-환경설정-😵💫">단연 환경설정 😵‍💫</h3>
<p>이번에 파이썬 환경설정 때문에 정말 많은 나의 머리카락이 뽑혔다. 정확하게 얘기하자면 로컬에서 잘 작동하는데 우분투 서버에서 빌드가 되지 않는 현상이 문제였다.</p>
<p>파이썬 버전이나 다른 패키지들의 버전이 안맞는게 있어서 그런가 싶어서 문서를 찾아보고 버전을 업그레드 해보았다가, 다운그레이드도 해보았다가, 그래도 안되서 지웠다가(파이썬을 지우는 작업도 간단한 작업이 아니다.) 다시 깔아도 보고 했지만 그래도 여전히 되지는 않았다...</p>
<p>이 작업을 하필이면 설에 하느라 동료가 본가에 가있느라 혼자 끙끙싸맸다. 너무 안풀려서 이제 도커라이징이 잘못되었나 싶어서 주변 사람들에게도 다 물어보고 그렇게 2-3일을 씨름하다가 어떤 외국 인터넷 사이트에서 어떤 사람이 파이썬 패키지를 시스템에 깔면 안된다? 이런 말을 보고 아 설마 내 시스템 환경에 잘못 깔려있나 싶어서 내 맥에 usr안에 들어가서 파이썬 .bash_profile 이랑 .bashrc 를 뒤져보는데 환경변수에 이상한 것들이 많이 연결되어있었다. </p>
<p>아하 이거구나 싶어서 어떤게 문제인가 환경변수를 하나씩 지워보면서 확인하고 있는데 어떤걸 지우니까 아얘 내 맥에 깔려있는 모든 프로그램들을 사용할 수가 없게 되었다. 심지어는 vi도 되지 않아서 파일을 수정하지도 못하는 지경에 이르렀다. 또 끙끙 싸매다가 결국 고쳐냈다. </p>
<p>그렇지만 여전히 원인을 알 수가 없어서 동료의 로컬에서 시도해 보았는데 잘 안되길래 freeze &gt; requiremets.txt를 하고 필요한 패키지들이 자동으로 깔리고 난 뒤 다시 시도해보니까 그렇게 안되어던 빌드가 잘 되는것을 확인할 수 있었다.</p>
<h2 id="스스로-부족했다고-느끼는-부분">스스로 부족했다고 느끼는 부분</h2>
<h3 id="체력관리">체력관리</h3>
<p>나만무 기간에 B형 독감에 걸렸었는데 건강관리도 정말 중요하다고 느꼈다. 아무리 바빠도 하루에 30정도는 운동을 하는 동료를 보면서 체력관리를 어떻게 해야하는지 배웠다.
제일 잘 알려져있는 같은 시간에 먹고 자기가 제일 중요한데 바쁘다보니 끼니를 거르거나 야근을 하느라 잠을 줄인다거나 하면서 몸의 균형이 깨지면서 면역 시스템이 무너지는 것을 경험했다.</p>
<blockquote>
<p>action plan🔥</p>
</blockquote>
<ol>
<li>아무리 바빠도 하루에 30분 걷거나 운동하기</li>
<li>같은 시간에 자고 일어나기<br></li>
</ol>
<p>-&gt; 위의 두가지를 지킬 수 있는 쉬운 방법!! <br><strong>매일 10시에 자고 5시에 일어나서, 일어나자마자 30분 산책하기!</strong></p>
<h3 id="시간--일정-관리">시간 / 일정 관리</h3>
<p>정글에 들어온 뒤로 나는 ADHD증상이 심해져서 약까지 복용하고 있었지만 시간관리와 일정관리가 정말 힘들었다. 스스로 현재 중요하지 않은 일을 하고 있다는 것을 알면서도 우선순위대로 일을 처리하지 못하는 나를 보면서 자괴감이 많이 들었는데 이는 흔히 ADHD 환자가 잘 못하는 것이라고 한다.</p>
<p>하지만 나는 사람이 자신의 약점을 잘 알고 개선시키려는 의지가 있다면 오히려 보통 사람들보다 더 나아질 수 있다고 생각하기 때문에 나의 약점을 장점으로 가져가려 한다.</p>
<blockquote>
<p>action plan🔥</p>
</blockquote>
<ol>
<li>아침 산책 후 오늘의 <strong>일정 정리</strong>하는 시간 5분 갖기(우선순위 정렬)</li>
<li>야근 안하기 위해 <strong>업무시간에 최대한 집중</strong>하기</li>
<li>아무리 일이 많아도 <strong>9시까지 공부/일 마무리</strong>하기  <em>(잘 되고 있어서 아쉽다면 다음날 계속 할 수 있는 동력이 되고, 잘 안되고 있었다면 자고 일어나면 더 잘 될것이니!)</em></li>
<li>하루 일과를 마무리하기 전에 <strong>다음날 해야하는 일 정하기</strong></li>
</ol>
<h3 id="f들과-피드백-주고받기">F들과 피드백 주고받기</h3>
<p>나는 ENTP/INTP 이고 T와 F가 거의 6:4 정도라서 나름 F친화적인 T라 스스로를 여겨왔지만, 업무에 있어서는 나도 몰랐던 나의 T성향이 굉장히 나오는듯 했다.</p>
<p>예를들어, 회의때 서로의 피드백을 주고 받을 때 나는 어떤 아이디어가 별로라고 생각되면 &#39;이건 이런이런 이유에서 별로인것 같아요. 그보다 이렇게 하면 더 좋을 것 같은데 어떻게 생각하시나요?&#39; 라고 나의 의견을 먼저 말하는 편이다.</p>
<p>T들과 소통할 때는 문제가 없었는데 이번 팀원들은 나를 제외하고 모두 F 사람들이었기에 문제가 생겼다.</p>
<p>어떤 팀원이 나의 피드백 방식에 상처를 조금 받았다고 말해주었다. (그 당시에는 이 정도에 상처받는다고? 어이없어 했지만 사실 말해줬다는 사실에 그 팀원에게 참 감사하다)</p>
<p>그래서 일단 칭찬하고 나의 의견을 조심스럽게 제시하는 쪽으로 자연스럽게 나의 소통방식이 바뀌었다. 예를들어, &#39;오! 좋은데요? 혹시 이렇게하면 더 좋을까요?&#39; 혹은 고개를 끄덕이며 &#39;음~ 좋네요~&#39; 라고 듣고 난 뒤에 조심스럽게 &#39;좋은데 다만 우려되는 부분으로 ~게 있긴 한 것 같아요&#39; 라고 한다던가.</p>
<p>이렇게 바뀐 부분은 소통에서 조금 더 시간을 잡아먹어서 생산성이 이전 방식 보다는 떨어질 수 있지만 우리는 로봇이 아니고 인간과 대화하며 일을 하기 위해서는 조금 불편하더라도 서로 존중하고 조심해야 하는게 맞는 것 같다.</p>
<p>이번에 나와 참 다른 성격의 팀원들을 만나서 어려운 점도 많았짐나 결국 내가 이렇게나 발전할 수 있었기에 너무 감사하다.</p>
<hr>
<p>끝으로 나만무 최종발표때 정성훈 멘토님💛과 찍은 사진을 첨부한다!
<img src="https://velog.velcdn.com/images/gigis-note/post/67aa2880-6a57-40e4-b246-764ac0650ca7/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2/13 나만무 트러블슈팅]]></title>
            <link>https://velog.io/@gigis-note/213</link>
            <guid>https://velog.io/@gigis-note/213</guid>
            <pubDate>Tue, 13 Feb 2024 01:33:10 GMT</pubDate>
            <description><![CDATA[<h2 id="trouble-shooting">trouble shooting</h2>
<p>우분투에서 서버 실행시켰때 안되는 이유 찾기</p>
<p>이 태스크가 3일이나 걸렸다. 도대체 아무리 새로 가상환경을 만들고 깔고 파이썬을 지웠다가 깔았다가 다운그레이드 했다가 우분투를 새로 팠다가 다시 세팅해도 되질 않았다. </p>
<p>그러다 우연히 어떤 외국 레딧에서 시스템 전역변수에 파이썬 패키지 깔면 안된다 라는 말 보고 설마 하고 내 시스템 환경변수 들어가서 보니까 이상하게 전역변수에 파이썬 관련 패키지가 너무 많았다. </p>
<p>그런데 그렇다 하더라도 가상환경 위에서 개발하면 시스템 환경과는 별개로 분리되어야 하는 것 아닌가 하는 찜찜함이 있지만 무튼 테스트를 위해 환경변수 설정을 잠깐 없에고 서버를 실행시켜보니 역시나 돌아가지 않았다. </p>
<p>일단 합리적 의심이라 정리하고 급한대로 다른 컴퓨터에서 실행시키고 필요한 패키지들을 pip freeze &gt; requirements.txt 로 뽑고 도커로 만뒤 우분투에서 도커로 배포해보니 정상작동하였다. 하하...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1228-개인과제 환경설정]]></title>
            <link>https://velog.io/@gigis-note/1228-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@gigis-note/1228-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 28 Dec 2023 17:43:49 GMT</pubDate>
            <description><![CDATA[<p>이번 개인 과제는 2주짜리이고 프레임워크에 친숙해지기 위해 게시판 만들기가 되겠습니다.</p>
<p>이번에는 지난번에 연습해보지 못해 아쉬웠던 깃크라켄, 깃이슈와 PR을 연동하여 브랜치를 관리하는 방법을 연습 삼아 시도해 볼 예정입니다. </p>
<p>깃 이슈를 생성하고 PR생성해서 이슈를 참조하고 main branch에 sub branch를 merge하고 병합된 이슈를 close하는 과정이 아래 블로그에 잘 나와있어서 참고했습니다.
<a href="https://minny27.tistory.com/50">https://minny27.tistory.com/50</a></p>
<p>이번 과제는 벨로그에 따로 정리하지 않고 기능별로 이슈를 발행해서 이슈안에 documentation을 할 예정입니다. </p>
<blockquote>
<p>오늘 환경설정 한 이슈와 그 안에 document 한 내용 <a href="https://github.com/geeks-lab/practicing_mern/issues/1">https://github.com/geeks-lab/practicing_mern/issues/1</a></p>
</blockquote>
<p>언어를 스프링과 노드중에 선택해야 하는데 한지 오래되긴 했지만 익숙한 스프링을 할까 아니면 몇번 안해본 노드를 할까 고민을 하다가, 노드를 이번에 해야겠다 생각했습니다. 이유는 다음과 같습니다.</p>
<ol>
<li>내가 회사를 다니면서 혼자 개인프로젝트를 하면 어떤 걸 쓸까 생각했을 때 아무래도 스프링보다는 노드로 빠르게 구현하는게 편할 것 같아서.</li>
<li>이직할 때 아무래도 나는 외국이나 스타트업으로 가지 않을까 해서. 거기는 자바보다 노드를 쓰는 비율이 크지 않나해서.</li>
</ol>
<p>그런데 말입니다... 그냥 게시판은 너무 재미가 없지 않겠습니까? 하하하 그래서 인프런을 구경하다가 너무 구미가 당기는 수업을 발견했습니다. <a href="https://www.inflearn.com/course/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B4%80%EB%A6%AC-%ED%92%80%EC%8A%A4%ED%83%9D">강의링크</a></p>
<p>강의 내용은 이미지 관리 노하우(AWS Lambda를 사용하여 파일 읽기 속도 개선해서 이미지 로딩을 빠르게)가 뾰족한 파트이고 거기에 내가 공부해야하는 Node.js, React을 사용해서 기본적인 기능들(DB연동, Authentication, pagenation, Infinite Scroll, etc) 다루기 때문에 굉장히 재밌을 것 같습니다😎</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/0087d54d-a494-4266-bfcc-2b5b47647f69/image.gif" alt=""></p>
<p>이렇게 이미지 로딩속도가 느린 경험을 누구나 한번쯤은 해봤을 것 입니다. 이커머스와 같은 특정 도메인에서는 이러한 페이지 로딩 시간이 크리티컬하기 때문에 이미지 파일을 잘 처리하고 관리하는 게 서비스 개발에 무척 중요한 요인이라고 합니다. 2주간 이 문제를 개선할 수 있는 방법을 이번에 기술적 챌린지로 가져가보면 너무 좋을 것 같습니다.</p>
<ul>
<li>Node.js, Express</li>
<li>React, React Hooks</li>
<li>MongoDB</li>
<li>AWS S3, CloudFront, presignedUrl</li>
<li>이미지 리사이징 &amp; AWS Lambda</li>
<li>IntersectionObserver를 이용한 Infinite Scroll</li>
<li>Session 기반 Authentication &amp; Authorization</li>
</ul>
<p>위의 기술을 사용해서 아래의 것들을 구현해볼 것입니다.(짱신나!)
<img src="https://velog.velcdn.com/images/gigis-note/post/356ad5e7-a469-4cdc-8b04-e56a557fb42d/image.png" alt=""></p>
<p>강의를 잘 따라가면 아래와 같은 사진첩 서비스를 만들 수 있다고 하기 때문에 저기에서 약간만 변경해서 게시판 서비스를 만들면 될 것 같습니다.
<img src="https://velog.velcdn.com/images/gigis-note/post/bb4201cd-d764-473d-93a0-eca9008ab19d/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1228-WIL(PintOS 회고 Fafare🎉)]]></title>
            <link>https://velog.io/@gigis-note/1228-WILPintOS-%ED%9A%8C%EA%B3%A0-Fafare</link>
            <guid>https://velog.io/@gigis-note/1228-WILPintOS-%ED%9A%8C%EA%B3%A0-Fafare</guid>
            <pubDate>Thu, 28 Dec 2023 06:24:34 GMT</pubDate>
            <description><![CDATA[<p>지난 5주동안 PintOS 과제를 진행하였다.
<a href="https://velog.io/@gigis-note/posts?tag=pintos">https://velog.io/@gigis-note/posts?tag=pintos</a></p>
<p>week 07-09 git repo
<a href="https://github.com/hnjog/pintos-kaist/tree/geeks-lab">https://github.com/hnjog/pintos-kaist/tree/geeks-lab</a></p>
<p>week 10-11 git repo
<a href="https://github.com/sylee6529/PintOS-VM/tree/geeks-lab">https://github.com/sylee6529/PintOS-VM/tree/geeks-lab</a></p>
<ol>
<li><p>Oh MY 나 자신, 칭찬합니다!</p>
<ul>
<li>C언어, 포인터 개념에 친숙해진 것.</li>
<li>디버깅 툴이나 방법 자체에 익숙해진 것. (부끄럽게도 그동안 break point걸고 이동하거나 깊게 파고들면서 까지의 디버깅을 해 본적이 없던 것 같다)</li>
<li>다른 분들의 공부 방식을 보고 내 공부 방식을 발전 시킬 수 있었던 것(혼자 공부 했더라면 불가능 했을 부분)</li>
<li>부끄러워도 내가 겪고 있는 문제를 동료에게 오픈하고 도움을 요청하는 것이 는 것.(나와 같은 프로젝트를 하고 있는 동료는 내 문제를 이미 겪었을 가능성이 높기 때문에 더욱 그랬다)</li>
<li>PintOS과제가 워낙 악명이 높기도 하고 지난 기수 분들이 너무 힘들었다고 하시니까 두려움이 컸는데, 그리고 하면서 실제로도 힘들기도 했지만 생각했던 것 만큼 힘들지...는 않았던 것 같다. 물론 끝까지 블로그나 동료 코드를 보지 않았더라면 이렇게 말하진 못했을 것 같지만^^. 어찌됐든 개인적인 일들, 건강 이슈등이 있었음에도 불구하고 끝까지 포기하지 않고 끝낸 것!!! 제일 칭찬해.</li>
</ul>
</li>
<li><p>다음에 더 발전시킬 수 있는 부분들!</p>
<ul>
<li>시간, 건강 관리를 더 열심히 촘촘하게 했더라면 시간을 좀 더 활용할 수 있었을텐데 사실 이 부분이 스스로 제일 아쉽다. 같은 반의 어떤 분은 헬스장에 다니면서도 할 일을 다른 분들보다 더 많이 하시는 걸 보면서 반성을 했다. 시간이 없다는 것은 변명이다. 지난 주차들을 되돌아보면 쓸데없이 흘려 보냈던 시간들이 많았다.<ul>
<li>🔥 <span style="color:red">Action Plan</span> : 일어나는 시간, 퇴근하는 시간, 운동하는 시간 만이라도 스스로 정하고 루틴화하기. </li>
</ul>
</li>
<li>도커로 해볼걸 시도하다가 너무 쉽게 포기함.</li>
<li>깃 크라켄이나 깃 데스크탑으로 기능별로 브랜치를 만들어서 브랜치 관리를 연습을 해볼 수 있었는데 못한 것. <ul>
<li>추가로, 이슈를 발행해서 트러블 슈팅을 정리하고 커밋 메세지에 이슈를 달아서 document 할 걸 그랬다.(왜냐하면 블로그에 적는거는 나중에 찾아볼때 한계가 있을 것 같다. 최소한 벨로그는 불편함)</li>
</ul>
</li>
<li>동료에게 도움 많이 못드린 것<ul>
<li>이건 건강 이슈 때문에 내가 교육장에 오랜 시간 있지 못해서 그랬던 것도 있지만 그래도 좀 더 열심히 빨리 해서 도움을 드렸더라면 덜 아쉬웠을 것 같다. </li>
</ul>
</li>
<li>초반 project 1,2 에서는 큰 흐름을 먼저 파악하고 코드를 써내려간 것 같은데 특히 project3 에서는 당장 테스트 케이스만 통과하기 위해 코드를 쓰지 않았나 하는 아쉬움이 남는다. 나 스스로 그림 그려보며 추상화를 하며 큰 그림을 이해하는 작업이 꼭 필요한 것 같다.</li>
</ul>
</li>
<li><p>끝나고 더 공부해보고 싶은 것들</p>
<ul>
<li>권영진 교수님 OS 특강 열려있을 때 다시한번 보고 정리해보기</li>
<li>키워드 면접 질문 대비용으로 아무것도 안보고 설명해보기 <ul>
<li>스레드(싱글, 멀티), 프로세스, 스케줄링, 세마포어, 락, race condition, 데드락, 페이지, 세그멘테이션, </li>
</ul>
</li>
<li>운영체제 교재였던 Three Easy Pieces 재밌어보이는데 읽어봐야겠다.</li>
<li>csapp도 못읽었던 부분들 읽어봐야겠다.</li>
<li>C에서 헤더파일이 어떤 방식으로 읽히는지 원리 파악해보기<ul>
<li>이 날 헤더파일 때문에 고생했었다. <a href="https://velog.io/@gigis-note/1130-trouble-shootingpintos-project-1.-donations">https://velog.io/@gigis-note/1130-trouble-shootingpintos-project-1.-donations</a></li>
</ul>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[1227-TIL]]></title>
            <link>https://velog.io/@gigis-note/1227-TIL</link>
            <guid>https://velog.io/@gigis-note/1227-TIL</guid>
            <pubDate>Wed, 27 Dec 2023 08:57:02 GMT</pubDate>
            <description><![CDATA[<h1 id="stack-growth">Stack growth</h1>
<p>스택그로스는 <a href="https://e-juhee.tistory.com/entry/Pintos-KAIST-Project-3-Stack-Growth-Pintos%EC%9D%98-%EC%8A%A4%ED%83%9D-%ED%99%95%EC%9E%A5-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98">이 레퍼런스</a>를 참고해서 구현했더니 바로 관련 테스트케이스가 통과했다.</p>
<p>다만 조건문중에 아주 복잡한 부분이 있어서 짚고 넘어갔다.</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/16591795-1f01-4f9c-ac4e-44d63333f153/image.png" alt=""></p>
<p>스택 확장으로 처리할 수 있는 폴트인 경우를 표현하기 위해 <code>(USER_STACK - (1 &lt;&lt; 20) &lt;= rsp - 8 &amp;&amp; rsp - 8 == addr &amp;&amp; addr &lt;= USER_STACK)</code> 이렇게 된 모습이다.</p>
<p>연산자 우선순위에 의해서</p>
<ol>
<li><code>1 &lt;&lt; 20</code>: 비트 왼쪽 시프트 연산은 가장 높은 우선순위를 갖는데, 이는 1을 20만큼 왼쪽으로 비트 이동시키는 연산입니다.</li>
<li><code>USER_STACK - (1 &lt;&lt; 20)</code>: 뺄셈 연산은 비트 시프트 연산보다 우선순위가 낮습니다. 따라서 USER_STACK에서 (1 &lt;&lt; 20)을 뺀 값이 계산됩니다.</li>
<li><code>rsp - 8</code>: 뺄셈 연산은 비트 시프트 연산보다 우선순위가 낮기 때문에, rsp에서 8을 뺀 값이 계산됩니다.</li>
<li><code>addr == rsp - 8</code>: 비교 연산은 뺄셈보다 우선순위가 높으므로, rsp - 8과 addr이 같은지 비교합니다.</li>
<li><code>USER_STACK - (1 &lt;&lt; 20) &lt;= rsp - 8</code>: 이전 단계에서 계산된 값과 USER_STACK - (1 &lt;&lt; 20)을 비교합니다.</li>
<li><code>addr &lt;= USER_STACK</code>: 비교 연산은 뺄셈보다 우선순위가 높으므로, addr과 USER_STACK을 비교합니다.</li>
<li>마지막으로 논리 <code>AND (&amp;&amp;)</code> 연산자를 사용하여 앞선 모든 비교를 연결합니다.</li>
</ol>
<hr>
<p>현재 전체 테스트케이스 결과 모습
<img src="https://velog.velcdn.com/images/gigis-note/post/1336a03e-b3e1-4109-aaaa-078b046c67bb/image.png" alt=""></p>
<hr>
<h1 id="특이-케이스-처리">특이 케이스 처리</h1>
<p>아래의 특수 테스트 케이스를 해결해야 한다.</p>
<p>FAIL tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc</p>
<p>이건 동료 <a href="https://github.com/sylee6529/PintOS-VM/issues/2">document</a> 의 도움을 받아 해결했다.</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/f5bcb1aa-2984-4ff1-8c4d-5711ecd99bdf/image.png" alt=""></p>
<p>현재까지 상황 27 of 141 tests failed.</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/416a02d4-7651-4b73-8b61-9ef182fc27cf/image.png" alt=""></p>
<p>page-merge-seq
page-merge-par
page-merge-stk</p>
<p>이 세개 fail 떴었다가 다시 돌리니까 두개 fail 뜬다...
<img src="https://velog.velcdn.com/images/gigis-note/post/47d0aee0-7160-4152-bc0f-4c81f49e6c4d/image.png" alt=""></p>
<p>왜이럴까? 일단 제쳐두고 mmap하자.</p>
<h1 id="memory-mapped-files">Memory Mapped Files</h1>
<h3 id="syscall_handelr">syscall_handelr()</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/2b7386e4-e4e5-46a7-828e-9ab8880e6cb5/image.png" alt=""></p>
<h3 id="mmap">mmap()</h3>
<pre><code class="language-c">void *mmap(void *addr, size_t length, int writable, int fd, off_t offset) {
    if (!addr || addr != pg_round_down(addr)) return NULL;  // addr 없는 경우

    if (offset != pg_round_down(offset))
        return NULL;  // offset이 page aligned 되지 않은 경우

    if (!is_user_vaddr(addr) || !is_user_vaddr(addr + length))
        return NULL;  // addr이 user 영역이 아닌 경우

    if (spt_find_page(&amp;thread_current()-&gt;spt, addr))
        return NULL;  // spt에 있는 경우

    struct file *f = process_get_file(fd);
    if (f == NULL) return NULL;  // file이 없는 경우

    if (file_length(f) == 0 || (int)length &lt;= 0)
        return NULL;  // file의 길이가 0 혹은 음수인 경우

    return do_mmap(addr, length, writable, f,
                   offset);  // 파일이 매핑된 가상 주소 반환
}</code></pre>
<h3 id="do_mmap">do_mmap()</h3>
<blockquote>
<p>매핑을 진행할 때는 간단하게 (페이지 주소 addr + PGSIZE에 위치하는) 다음 페이지에 이어서 매핑하면 되지만, 매핑을 해제할 때는 몇 번째 페이지까지 해제해야 하는지를 알아야 한다. 따라서 총 몇 페이지를 사용해서 매핑이 이루어졌는지에 대한 정보도 page 구조체에 함께 저장해 둔다. <a href="https://e-juhee.tistory.com/entry/Pintos-KAIST-Project-3-Memory-Mapped-Files">출처</a></p>
</blockquote>
<pre><code class="language-c">void *do_mmap(void *addr, size_t length, int writable, struct file *file,
              off_t offset) {
    struct file *f = file_reopen(file);
    // this is for returning mapped virtual address when the mapping is suceeded
    void *start_addr = addr;
    // Total number of page for mapping
    int total_page_count = length &lt;= PGSIZE  ? 1
                           : length % PGSIZE ? length / PGSIZE + 1
                                             : length / PGSIZE;

    size_t read_bytes = file_length(f) &lt; length ? file_length(f) : length;
    size_t zero_bytes = PGSIZE - read_bytes % PGSIZE;

    ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(addr) == 0);     // page alignment for the upage
    ASSERT(offset % PGSIZE == 0);  // page alignment for the ofs

    while (read_bytes &gt; 0 || zero_bytes &gt; 0) {
        /* 이 페이지를 채우는 방법을 계산합니다.
        파일에서 PAGE_READ_BYTES 바이트를 읽고
        최종 PAGE_ZERO_BYTES 바이트를 0으로 채웁니다. */
        size_t page_read_bytes = read_bytes &lt; PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

        struct lazy_load_arg *lazy_load_arg =
            (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
        lazy_load_arg-&gt;file = f;
        lazy_load_arg-&gt;ofs = offset;
        lazy_load_arg-&gt;read_bytes = page_read_bytes;
        lazy_load_arg-&gt;zero_bytes = page_zero_bytes;

        // vm_alloc_page_with_initializer를 호출하여 대기 중인 객체를
        // 생성합니다.
        if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable,
                                            lazy_load_segment, lazy_load_arg))
            return NULL;

        struct page *p = spt_find_page(&amp;thread_current()-&gt;spt, start_addr);
        p-&gt;mapped_page_count = total_page_count;

        /* Advance. */
        // 읽은 바이트와 0으로 채운 바이트를 추적하고 가상 주소를 증가시킵니다.
        read_bytes -= page_read_bytes;
        zero_bytes -= page_zero_bytes;
        addr += PGSIZE;
        offset += page_read_bytes;
    }

    return start_addr;
}</code></pre>
<hr>
<h1 id="trouble-shooting">Trouble shooting</h1>
<h3 id="mmap-read-overlap">mmap-read, overlap</h3>
<p>위 테케 포함 중간중간 mmap 관련 테스트 실패</p>
<h3 id="try">try</h3>
<p>syscall.c 의 syscall_handler에서 
SYS_MMAP에서 mmap함수 결과를 rax 레지스터에 담았다</p>
<p>before
<img src="https://velog.velcdn.com/images/gigis-note/post/6cf64c0d-9354-412a-85da-b232b4e43fb0/image.png" alt=""></p>
<p>after
<img src="https://velog.velcdn.com/images/gigis-note/post/1e59fb49-439c-4239-a4d1-37281b98b976/image.png" alt=""></p>
<hr>
<p>현재상황 </p>
<p>mmap-inherit 빼고 mmap 관련 테케 성공</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/184cd50f-2625-4164-a67a-11aacd483c09/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1226-TIL]]></title>
            <link>https://velog.io/@gigis-note/1226-TIL</link>
            <guid>https://velog.io/@gigis-note/1226-TIL</guid>
            <pubDate>Wed, 27 Dec 2023 04:24:16 GMT</pubDate>
            <description><![CDATA[<h1 id="project2-debugging-before-copykill">project2 debugging (before copy,kill)</h1>
<p>여태까지 상황</p>
<h2 id="issue-1-timer-ticks-message">Issue #1: &quot;Timer: #ticks&quot; message</h2>
<h3 id="이쯤에서-다시-원인분석">이쯤에서 다시 원인분석</h3>
<p>현재 상황은 테케를 돌리면 이렇게 뜨는 상황이다.
<img src="https://velog.velcdn.com/images/gigis-note/post/b8033535-2ac6-409a-83c8-c9e03f5018cf/image.png" alt=""></p>
<h3 id="try-1">try #1</h3>
<p>디버깅을 해봤다. </p>
<ul>
<li><p>지금 나는 exec도 못들어가고 있는 상황.</p>
</li>
<li><p>init.c 의 main 함수를 돌고 나서 child process인 test가 돌아가는 건데 나는 main함수도 못돌고 있다.</p>
</li>
<li><p>근데 죽는 지점이 매번 다르다…?🙃</p>
</li>
<li><p>run_actions 에서 여기서 죽음 <img src="https://velog.velcdn.com/images/gigis-note/post/0148b6a2-7ee8-489c-b9c9-608ac8758739/image.png" alt=""></p>
</li>
<li><p>이번엔 fsutil_put()에서 죽음🙃
<img src="https://velog.velcdn.com/images/gigis-note/post/fd3d3620-0915-48cd-8811-8a8590386288/image.png" alt=""></p>
</li>
<li><p>이번엔 lock_release()에서 죽음🙃
<img src="https://velog.velcdn.com/images/gigis-note/post/969321c7-269e-4f10-af55-9967d54302f2/image.png" alt=""></p>
</li>
<li><p>디버깅을 해봐도 죽는 지점이 계속 달라서 혼란의 연속이었다.</p>
</li>
<li><p>그러다 구세주 프린트신공 마찬옥님 등장.</p>
</li>
<li><p>그도 고군분투를 하다 갑자기 프린트문이 찍히면 안되는 곳에서 찍혀서 이러는것이 아니냐는 합리적의심 후 쓸모없는 프린트문 모두 제거.</p>
<ul>
<li>해당 오류가 사라져 버렸다…!🥹</li>
<li><del>포기하지 않고 문제를 찾아버린 마찬옥님께 감사의 인사를…</del>🙏</li>
</ul>
</li>
</ul>
<h2 id="issue-2-vmc-에서-threadsmmuh-library-추가-안함">Issue #2: vm.c 에서 &quot;threads/mmu.h&quot; library 추가 안함</h2>
<h3 id="try">try</h3>
<p>vm.c에서 #include &quot;threads/mmu.h&quot; 추가</p>
<h3 id="cause">cause</h3>
<p>pml4관련 함수를 vm_do_claim_page()에서 pml4_set_page() 이렇게 버젓이 사용하고 있는데 라이브러리 선언을 위에 안했어서 함수 사용을 실질적으로 못하고 있었다.</p>
<h2 id="issue-3-anon_initializer에서--return-ture-안함">Issue #3: anon_initializer()에서  return ture 안함</h2>
<p><code>pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-single:args-single --swap-disk=4 -- -q   -f run &#39;args-single onearg&#39;</code>
했을 때</p>
<h3 id="try-2">try</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/3ae2e33a-6b06-4a9f-999e-b6c99e1ea2bf/image.png" alt=""></p>
<p>여기서 return true;를 해주지 않았어서 추가했다.</p>
<h3 id="cause-1">cause</h3>
<p>anon_initializer()에서 true를 반환해주지 않으면 vm_alloc_page_with_initializer()에서 uninit_new에서 false 값이 넘어가기 때문에 초기화가 제대로 이루어지지 않아서 해당 부분에서 문제가 생겼던 것이다. </p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/47ced129-56e8-4582-91e1-4c24e21c5de0/image.png" alt=""></p>
<h2 id="issue-4-switch-type---switch-vm_typetype">Issue #4: switch (type) -&gt; switch (VM_TYPE(type))</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/76b3410c-c096-4bc6-85f4-c354b8daf4bd/image.png" alt=""></p>
<h3 id="try-3">try</h3>
<p><code>vm_alloc_page_with_initializer()</code> 함수에서 스위치문 안이 의심스러웠다. 처음엔 <code>switch (VM_TYPE(type))</code> 했다가 다른 사람 코드에 <code>switch (type)</code> 되어 있는걸 보고 바꿨던게 문제가 될 수도 있겠어서 원래코드로 바꿨다.</p>
<p>before :<code>switch (type)</code> 
after : <code>switch (VM_TYPE(type))</code></p>
<p>역시 이 문제가 맞았었다.</p>
<h3 id="cause-2">cause</h3>
<p>그냥 type과 VM_TYPE(type)이 어떻게 값이 찍히는지 프린트문으로 찍어봤다.
<img src="https://velog.velcdn.com/images/gigis-note/post/df7f2217-33d1-43f7-9b6f-3ad82b7de37c/image.png" alt=""></p>
<p>근데 같다..?
<img src="https://velog.velcdn.com/images/gigis-note/post/0ab51223-b393-457a-a3ac-11bdccd3a43b/image.png" alt=""></p>
<p>하지만 맨끝에 7번째에는 다르다. VM_TYPE(type)은 0x1이고 type은 0x9로 찍힌다.</p>
<p>마지막에 그렇게 나오는 이유는 type이 유저스택의 스택에 있는 페이지라서 type이 0x9로 찍힌 것이다. 그 이유는 우리가 setup_stack()에서 스택을 할당할 때 이렇게 하기 때문에 VM_MARKER_0을 넣어주었었기 때문이다.
<img src="https://velog.velcdn.com/images/gigis-note/post/027e9209-055c-4e3d-ad60-c92c422f0293/image.png" alt="">
(VM_MARKER_0 이 1000 -&gt;8임)</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/10cfdc98-cbea-4131-97e9-61219a660759/image.png" alt=""></p>
<p>VM_TYPE()은 이렇게 define 되어있다.
<img src="https://velog.velcdn.com/images/gigis-note/post/c269cec4-a05b-457c-82f2-3bd15805ebc5/image.png" alt=""></p>
<p>0x9는 이진수로 나타내면 1001이고, 7은 이진수로 나타내면 0111
이걸 AND 연산하면 0001 이다.</p>
<p>따라서, VM_TYPE()으로 감싸줘야 0x1이 된다.</p>
<p>오늘 꽤 오랜시간 나의 에러 해결에 큰 도움을 주신 마찬옥님께 다시한번 감사의 인사를 올린다.</p>
<hr>
<p>여기까지 했을 때 일단 fork 전까지 성공했다. 
이제 copy kill 해야하고 stack growth 한 다음에 mmap하면 ...된다...하하</p>
<h1 id="project2-debugging-copykill-구현">project2 debugging: copy,kill 구현</h1>
<pre><code class="language-c">/* Copy supplemental page table from src to dst */
// 자식 프로세스 생성할때 spt()
bool supplemental_page_table_copy(struct supplemental_page_table *dst UNUSED,
                                  struct supplemental_page_table *src UNUSED) {
    struct hash_iterator i;
    hash_first(&amp;i, &amp;src-&gt;spt_hashmap);
    while (hash_next(&amp;i)) {
        struct page *src_page =
            hash_entry(hash_cur(&amp;i), struct page, hash_elem);
        enum vm_type type = src_page-&gt;operations-&gt;type;
        void *upage = src_page-&gt;va;
        bool writable = src_page-&gt;writable;

        // type이 uninit이면
        if (type == VM_UNINIT) {  // uninit page 생성 &amp; 초기화
            vm_initializer *init = src_page-&gt;uninit.init;
            void *aux = src_page-&gt;uninit.aux;
            vm_alloc_page_with_initializer(VM_ANON, upage, writable, init, aux);
            continue;
        }

        // type이 uninit이 아니면
        if (!vm_alloc_page(type, upage, writable))  // uninit page 생성 &amp; 초기화
            return false;

        // vm_claim_page으로 요청해서 매핑 &amp; 페이지 타입에 맞게 초기화
        if (!vm_claim_page(upage)) return false;

        // 매핑된 프레임에 내용 로딩
        struct page *dst_page = spt_find_page(dst, upage);
        memcpy(dst_page-&gt;frame-&gt;kva, src_page-&gt;frame-&gt;kva, PGSIZE);
    }
    return true;
}

void hash_page_destroy(struct hash_elem *e, void *aux) {
    struct page *page = hash_entry(e, struct page, hash_elem);
    destroy(page);
    free(page);
}

/* Free the resource hold by the supplemental page table */
void supplemental_page_table_kill(struct supplemental_page_table *spt UNUSED) {
    /* TODO: Destroy all the supplemental_page_table hold by thread and
     * TODO: writeback all the modified contents to the storage. */
    hash_clear(&amp;spt-&gt;spt_hashmap,
               hash_page_destroy);  // 해시 테이블의 모든 요소를 제거
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1223-TIL]]></title>
            <link>https://velog.io/@gigis-note/1223-TIL</link>
            <guid>https://velog.io/@gigis-note/1223-TIL</guid>
            <pubDate>Tue, 26 Dec 2023 11:26:29 GMT</pubDate>
            <description><![CDATA[<h1 id="trouble-shooting">Trouble Shooting</h1>
<h2 id="issue-1">issue 1</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/f7b69623-5f71-4cc2-90f9-73034938862c/image.png" alt=""></p>
<p>annonymous 관련 코드 짠 후 돌려보니 돌리자마자 바로 커널 패닉🫠</p>
<p>밑에서 세번째 줄에 filesys/fsutil.c:160 in fsutil_get(): db: open failed</p>
<p>왜 저기서 터지는지 모르겠다.</p>
<p>debugging 해보고 싶은데 launch.json에 문제가 있다면서 안돌아간다. (내일 팀원한테 어떻게 하는지 물어봐야지)</p>
<p>할 수 있는 걸 해보자.</p>
<p>콜스택을 백트레이스 해봤다.
<img src="https://velog.velcdn.com/images/gigis-note/post/b785125e-f9e9-49a9-b455-20d31feaff53/image.png" alt=""></p>
<h3 id="try-1">try #1</h3>
<p>동료가 vm_get_frame()에서 frame에 NULL을 넣어줬던거를 말록으로 바꿨더니 문제가 해결됐다고 해서 나도 해보았다.</p>
<p>before
<img src="https://velog.velcdn.com/images/gigis-note/post/5d655604-1c71-4acf-bc35-4052fd8f8f2b/image.png" alt=""></p>
<p>after
<img src="https://velog.velcdn.com/images/gigis-note/post/c7f38148-7168-4077-8674-62540fcbcf66/image.png" alt=""></p>
<p>fsutil_get()에서 터지는 문제는 넘어가고 is_thread()에서 터지는 현상이 나타났다.</p>
<h2 id="issue-2">issue 2</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/eec2074b-a224-4ae7-87b0-15e8202d695d/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/35c589cd-7341-4f0f-89a2-d881c5fc38e0/image.png" alt=""></p>
<h3 id="try-1-1">try #1</h3>
<p>현재 내 코드에 따르면 vm_claim_page가 성공하면 user_stack과는 상관없이 무조건 true 리턴하는 형식이라 setup_stack의 success와 vm_claim_page의 success를 분리시켜야 할 것 같다고 했다.</p>
<p>아래와 같이 바꿔봤다.</p>
<p>before
<img src="https://velog.velcdn.com/images/gigis-note/post/c0baae3a-cb89-42f8-bf64-585e513202ed/image.png" alt=""></p>
<p>after
<img src="https://velog.velcdn.com/images/gigis-note/post/d24d0cb9-4d23-440a-9268-40e27be968fd/image.png" alt=""></p>
<h3 id="try-2">try #2</h3>
<p>비록 결과는 똑같이 is_thread에서 커널 패닉이 나지만.. 유의미한 코드 수정이였다.</p>
<p>주석친 부분이 page_fault() 에서 project 2 작업할 때 넣었던 코드인데 이게 project 3에서 문제가 될 수 있을 것 같아서 주석처리를 해보았다.</p>
<pre><code class="language-c">static void
page_fault (struct intr_frame *f) {
    bool not_present;  /* True: not-present page, false: writing r/o page. */
    bool write;        /* True: access was write, false: access was read. */
    bool user;         /* True: access by user, false: access by kernel. */
    void *fault_addr;  /* Fault address. */

    /* Obtain faulting address, the virtual address that was
       accessed to cause the fault.  It may point to code or to
       data.  It is not necessarily the address of the instruction
       that caused the fault (that&#39;s f-&gt;rip). */

    fault_addr = (void *) rcr2();

    /* Turn interrupts back on (they were only off so that we could
       be assured of reading CR2 before it changed). */
    intr_enable ();


    /* Determine cause. */
    not_present = (f-&gt;error_code &amp; PF_P) == 0;
    write = (f-&gt;error_code &amp; PF_W) != 0;
    user = (f-&gt;error_code &amp; PF_U) != 0;

    // int PHYS_BASE = 0x80040000;
    // int VIRTUAL_BASE = 0x00000000;
    // // printf(&quot;page fault at %p\n&quot;, fault_addr);
    // /* Handle kernel-mode page faults. */
    // if (!user) {
    //     // printf(&quot;not user\n&quot;);
    //     if (is_kernel_vaddr(fault_addr)) {
    //         // printf(&quot;is kernel vaddr\n&quot;);
    //         if (fault_addr &gt;= PHYS_BASE) {
    //             // printf(&quot;fault addr &gt;= phys base\n&quot;);
    //             exit(-1);
    //         }
    //         else {
    //             // printf(&quot;fault addr &lt; phys base\n&quot;);
    //         }

    //     }

    //     else {
    //         // printf(&quot;not kernel vaddr\n&quot;);
    //         exit(-1);
    //     }
    // }

    // /* Handle user-mode page faults. */
    // else {
    //     // printf(&quot;user\n&quot;);
    //     if (is_kernel_vaddr(fault_addr)) {
    //         // printf(&quot;is kernel vaddr\n&quot;);
    //         exit(-1);
    //     }
    //     else {
    //         // printf(&quot;not kernel vaddr\n&quot;);
    //         if (fault_addr &lt;= VIRTUAL_BASE) {
    //             // printf(&quot;fault addr &lt;= virtual base\n&quot;);
    //             exit(-1);
    //         } else {
    //             // printf(&quot;fault addr &gt; virtual base\n&quot;);
    //         }
    //     }
    // }

#ifdef VM
    /* For project 3 and later. */
    if (vm_try_handle_fault (f, fault_addr, user, write, not_present))
        return;
#endif

    /* Count page faults. */
    page_fault_cnt++;

    /* If the fault is true fault, show info and exit. */
    printf (&quot;Page fault at %p: %s error %s page in %s context.\n&quot;,
            fault_addr,
            not_present ? &quot;not present&quot; : &quot;rights violation&quot;,
            write ? &quot;writing&quot; : &quot;reading&quot;,
            user ? &quot;user&quot; : &quot;kernel&quot;);
    kill (f);
}</code></pre>
<p>오! 주석처리 하니 일단 is_thread()에서 터지는 문제는 넘어가서 해당 코드는 삭제해두었다.</p>
<h2 id="issue-3">issue 3</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/c1bc7e48-b2e0-4a01-b76e-519e4385641c/image.png" alt=""></p>
<p>이제는 page_fault발생하고 나서 exception.c의 kill()에서 문제가 터진다.</p>
<p><code>Kernel PANIC at ../../userprog/exception.c:97 in kill(): Kernel bug - unexpected interrupt in kernel</code></p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/29a4ba65-95e0-4ab8-9b02-8ad4c6562034/image.png" alt=""></p>
<h3 id="try-1-2">try #1</h3>
<ul>
<li><p>va를 매개변수로 받아서 그걸로 page find 해야하는데 여기서 넘어오는 va가 값이 page단위가 아니라 페이지 중간 값일 수 있어서 페이지 단위로 라운드 해줘야 찾을 수 있기 때문에 pg_round_down()함수로 주소를 맞춰줬다.</p>
</li>
<li><p>page에 palloc을 해주는 이유를 모르겠어서 뺐다. </p>
</li>
<li><p>page에 NULL을 넣어주고 시작했는데 NULL을 넣어줄 필요성을 못느껴 뺐다.</p>
</li>
</ul>
<p>before
<img src="https://velog.velcdn.com/images/gigis-note/post/a9a00b47-b33e-4228-b3f7-08b6ac033cbe/image.png" alt=""></p>
<p>after
<img src="https://velog.velcdn.com/images/gigis-note/post/509b6a06-f443-4794-904a-10e74aae3a7d/image.png" alt=""></p>
<p>결과는 에러가 해결되지 않고 똑같은 모습이였다.</p>
<p>에러를 다시 보았다. exception.c의 에러가 나는 곳으로 이동하니 이러한 주석이 쓰여있다.</p>
<blockquote>
<p>Kernel&#39;s code segment, which indicates a kernel bug.
               Kernel code shouldn&#39;t throw exceptions.  (Page faults
               may cause kernel exceptions--but they shouldn&#39;t arrive
               here.)  Panic the kernel to make the point.
               커널 코드는 예외를 던져서는 안된다. 페이지 폴트는 커널 익셉션을 야기할 수 있다. 하지만 여기에 와서는 안된다. </p>
</blockquote>
<h3 id="try-2-1">try #2</h3>
<p>하..vm_alloc_page_with_initializer()의 함수를 찬찬히 뜯어보다가 if문 괄호가 닫히기 전에 err: return false;를 하고있는 모습을 보고 띠용했다. 보는 순간 직감한다. 이것이 이 문제의 근원이라는 것을...
돌려보니 역시나 커널패닉이 드디어 해결되었다.</p>
<p>before
<img src="https://velog.velcdn.com/images/gigis-note/post/1f379e05-f021-4014-b45b-ac7b70f0af46/image.png" alt=""></p>
<p>after
<img src="https://velog.velcdn.com/images/gigis-note/post/80bc10c6-bd3b-44e5-ad99-06da3b6978d6/image.png" alt=""></p>
<p>result
<img src="https://velog.velcdn.com/images/gigis-note/post/452b1d8c-1c11-4dec-8918-a5b5088ab102/image.png" alt=""></p>
<p>그렇지만 테스트케이스는 FAIL 이었다.</p>
<h2 id="issue-4">issue 4</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/e785d154-54f5-47a5-afb1-9c82f0a746d1/image.png" alt=""></p>
<ul>
<li>spt_find_page()에서 hash_find()하고 free 안해주고 있어서 free 해줬다</li>
</ul>
<h3 id="try-1-3">try #1</h3>
<ul>
<li>spt_insert_page() 에서 insert하기 전에 넣으려 하는 가상주소가 존재하지 않는지 검사하는 코드가 없어서 추가하고 succ 를 리턴하는 형식으로 바꿨다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/823090c6-e266-439a-b22a-d611bcff7b07/image.png" alt=""></p>
<p>원래 <code>return hash_insert(&amp;spt, &amp;page-&gt;hash_elem) == NULL ? true : false;</code> 이렇게 리턴해줬다가 위처럼 바꿨더니 다시 커널 패닉이 exception.c kill()에서 난다🫠 동료에게 물어봤더니 spt에 존재하는지 체크 예외처리를 해주지 않아도 괜찮다고 해서 다시 돌려놨다.</p>
<h3 id="a-few-fixes-1">a few fixes #1</h3>
<p>음 이건 해당 에러를 고치기 위해서가 아니라 코드를 보다 고쳐 놓아야 할듯 해서 고쳐놓는다.</p>
<p>syscall.c 의 check_address()에서 lazy loading 으로 바뀌었기 때문에 pml4에서 보이지 않더라도 정상적인 상황일 수 있기 떄문에 예외처리를 살짝 수정해 주어야 한다.</p>
<pre><code class="language-c">void check_address(void *addr) {
    struct thread *t = thread_current();

    /*  이전 코드
    if (!is_user_vaddr(addr) || addr == NULL || pml4_get_page(t-&gt;pml4 , addr)
    == NULL) {     exit(-1);
    }
    */

    if (!is_user_vaddr(addr) || addr == NULL) {
        exit(-1);
    }

    if (pml4_get_page(t-&gt;pml4, addr) == NULL) {
        // pml4에 없고, spt에 있으면 정상적인 lazy loading 상황 -&gt; 에러 아님!
        if (!spt_find_page(&amp;t-&gt;spt, addr)) {
            exit(-1);
        }
    }
}</code></pre>
<h3 id="a-few-fixes-2">a few fixes #2</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/2b86b518-ef36-469b-9f79-2afd10f75bf9/image.png" alt=""></p>
<p>spt_insert_page(spt, p); 이렇게 해주고 있었다.
hashmap에 넣어줘야 하는게 맞기에 find하는 함수들과 insert하는 함수들의 spt를 &amp;spt-&gt;spt_hashmap 으로 수정.</p>
<h3 id="이쯤에서-다시-원인분석">이쯤에서 다시 원인분석</h3>
<p>현재 상황은 테케를 돌리면 이렇게 뜨고,
<img src="https://velog.velcdn.com/images/gigis-note/post/b8033535-2ac6-409a-83c8-c9e03f5018cf/image.png" alt=""></p>
<ul>
<li><p>지금 나는 exec도 못들어가고 있는 상황.</p>
</li>
<li><p>init.c 의 main 함수를 돌고 나서 child process인 test가 돌아가는 건데 나는 main함수도 못돌고 있다.</p>
</li>
<li><p>run_actions 에서 죽음.</p>
</li>
<li><p>여기서 죽음 <img src="https://velog.velcdn.com/images/gigis-note/post/0148b6a2-7ee8-489c-b9c9-608ac8758739/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/fd3d3620-0915-48cd-8811-8a8590386288/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/969321c7-269e-4f10-af55-9967d54302f2/image.png" alt=""></p>
</li>
</ul>
<p>롸?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1221-TIL(pintos project 3 - Anon 구현)]]></title>
            <link>https://velog.io/@gigis-note/1221-TILpintos-project-3-Anon-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@gigis-note/1221-TILpintos-project-3-Anon-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 26 Dec 2023 04:41:29 GMT</pubDate>
            <description><![CDATA[<ul>
<li>1221 목요일</li>
<li><input checked="" disabled="" type="checkbox"> memory management 빠진 부분 확인</li>
<li><input checked="" disabled="" type="checkbox"> 빠진부분 작성</li>
<li><input disabled="" type="checkbox"> anon page 구현</li>
</ul>
<h1 id="memory-management-missing-part">memory management missing part</h1>
<pre><code class="language-c">&lt;vm.c?&gt;
/* Return true on success */
bool vm_try_handle_fault(struct intr_frame *f UNUSED, void *addr UNUSED,
                         bool user UNUSED, bool write UNUSED,
                         bool not_present UNUSED) {
    struct supplemental_page_table *spt UNUSED = &amp;thread_current()-&gt;spt;
    if (spt == NULL) return false;

    return vm_claim_page(addr);
}</code></pre>
<p>이렇게 vm_claim_page(addr); 하면 해당 함수에서 spt에서 va에 해당하는 page 찾아서 <code>return vm_do_claim_page(page);</code> 이렇게 vm_do_claim_page함수를 호출한다.</p>
<hr>
<h1 id="anonymous-page">Anonymous page</h1>
<p>Anonymous page 구현 들어가기 전에 page 초기화 흐름을 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/1baf6364-b1db-4f3c-998a-7db933204f64/image.png" alt="">
출처: <a href="https://e-juhee.tistory.com/entry/Pintos-KAIST-Project-3-Anonymous-Page-Lazy-Loading%EC%9C%BC%EB%A1%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%9A%A8%EC%9C%A8%EC%84%B1-%EB%86%92%EC%9D%B4%EA%B8%B0">링크텍스트</a></p>
<p>anonymous page 시작해보자.</p>
<h2 id="team-todo-list-on-anonymous-page">team todo list on Anonymous Page</h2>
<ul>
<li>목표: Page Type에 따라 알맞게 처리하고, lazy loading을 구현한다.</li>
<li>구현<ul>
<li>page type에 따른 initializer 호출</li>
<li>struct anon_page에 필요한 멤버 추가 가능</li>
<li>처음은 무조건 uninit 페이지로 생성, 페이지 구조만 할당</li>
<li>lazy loading을 위해 load_segment while문 로직, setup_stack 로직 수정</li>
<li>Page Fault Handler에서 Valid한 Fault인지 체크
[여기까지 하면 fork 제외 Project2 테케 모두 통과]</li>
<li>fork를 고려한 SPT copy, kill 함수 구현</li>
<li>uninit_destroy, anon_destroy 구현
[ 여기까지 하면 Project2 테케 모두 통과 ]</li>
</ul>
</li>
</ul>
<h3 id="1-vm_alloc_page_with_initializer">1) vm_alloc_page_with_initializer</h3>
<pre><code class="language-c">bool
vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writable,
        vm_initializer *init, void *aux) {

    ASSERT (VM_TYPE(type) != VM_UNINIT)

    struct supplemental_page_table *spt = &amp;thread_current ()-&gt;spt;

    /* Check wheter the upage is already occupied or not. */
    if (spt_find_page (spt, upage) == NULL) {
        struct page *p = (struct page *)malloc(sizeof(struct page));
        bool (*page_initializer)(struct page *, enum vm_type, void *);
        switch (VM_TYPE(type))
        {
        case VM_ANON:
            page_initializer = anon_initializer;
            break;
        case VM_FILE:
            page_initializer = file_backed_initializer;
            break;
    }
    uninit_new(p, upage, init, type, aux, page_initializer);
    p-&gt;writable=writable; // 순서 중요. 필드 수정을 uninit_new 이후에 해야함.
    return spt_insert_page(spt, p);
err:
    return false;
}</code></pre>
<h3 id="2-load_segment">2) load_segment</h3>
<h4 id="when-is-this-function-called">When is this function called?</h4>
<p>inside of the load function(fuc loads the executable file to the current thread) when the process is executed.</p>
<h4 id="what-does-this-function-do">what does this function do?</h4>
<ul>
<li>loads the file&#39;s content to the <code>upage</code>.</li>
<li>Be awere that this function shouldn&#39;t load a file&#39;s content directly, but hand over the ingredients for the actual loading such as function and arguments to &quot;lazy_load_segment()&quot; </li>
</ul>
<h3 id="struct-lazy_load_arg">struct lazy_load_arg</h3>
<p>Need a structure that includes the information for the loading to write load_segment.</p>
<pre><code class="language-c">&quot;include/vm/file.h&quot;
struct lazy_load_arg {
    struct file* file;
    uint32_t read_bytes;
    uint32_t zero_bytes;
    off_t ofs;
    void *open_addr;
    void *close_addr;
};</code></pre>
<pre><code class="language-c">&quot;userprog/process.c&quot;
static bool load_segment(struct file *file, off_t ofs, uint8_t *upage,
                         uint32_t read_bytes, uint32_t zero_bytes,
                         bool writable) {
    ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(upage) == 0);
    ASSERT(ofs % PGSIZE == 0);

    while (read_bytes &gt; 0 || zero_bytes &gt; 0) {
        /* Do calculate how to fill this page.
         * We will read PAGE_READ_BYTES bytes from FILE
         * and zero the final PAGE_ZERO_BYTES bytes. */
        size_t page_read_bytes = read_bytes &lt; PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

        struct lazy_load_arg *lazy_load_arg =
            (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
        lazy_load_arg-&gt;file = file;
        lazy_load_arg-&gt;ofs = ofs;                      // location will start to read
        lazy_load_arg-&gt;read_bytes = page_read_bytes; // bytes the page need to read
        lazy_load_arg-&gt;zero_bytes = page_zero_bytes; // bytes need to be filled with 0
        void *aux = NULL;
        if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable,
                                            lazy_load_segment, aux))
            return false;

        /* Advance. update for the repeat */
        read_bytes -= page_read_bytes;
        zero_bytes -= page_zero_bytes;
        upage += PGSIZE;
        ofs += page_read_bytes;
    }
    return true;
}</code></pre>
<h3 id="3-lazy_load_segment">3) lazy_load_segment</h3>
<h4 id="when-is-this-function-called-1">When is this function called?</h4>
<p>When the first page fault is called</p>
<h4 id="what-does-this-function-do-1">what does this function do?</h4>
<ul>
<li>This function is responsible for loading, practically. </li>
<li>As the physical frame mapping is done before this function is called, just need to do loading the content to the physical frame.</li>
<li>The Argument aux is the info that is set on the <code>lazy_load_arg</code>, which is the information that needs to find the file </li>
</ul>
<pre><code class="language-c">static bool lazy_load_segment(struct page *page, void *aux) {
    void *kva_ = page-&gt;frame-&gt;kva;

    struct lazy_load_arg *lla = (struct lazy_load_arg *)aux;
    // set ofs as file&#39;s position
    file_seek(lla-&gt;file, lla-&gt;ofs);
    // reading(loading) the content to the physical frame
    if (file_read(lla-&gt;file, kva_, lla-&gt;read_bytes) != (int)(lla-&gt;read_bytes)) {
        palloc_free_page(kva_);
        return false;
    }
    memset(kva_ + lla-&gt;read_bytes, 0, lla-&gt;zero_bytes);
    return true;
}</code></pre>
<h3 id="4-setup_stack">4) setup_stack</h3>
<h4 id="when-is-this-function-called-2">When is this function called?</h4>
<p>Inside of load function when the process is running</p>
<h4 id="what-does-this-function-do-2">what does this function do?</h4>
<p>Creates a page of stack at the point downwards as PGSIZE from USER_STACK where the stack&#39;s start point.</p>
<p>Note that the lazy loading is not necessary for the first stack page. Becuase when the process is executed, this address is accessed right away after this function is called in order to add <code>commmand line args</code> to the stack.</p>
<p>Therefore, we don&#39;t have to wait for the fault, but get a physical frame right away.</p>
<p>When a page is allocated, suppliment marker can be used to indicate the page is stack page by using <code>VM_MARKER_0</code> with the type. </p>
<p>Lastly, change the rsp to <code>USER_STACK</code> so that the <code>argument_stack</code> function can push the args from the rsp.</p>
<pre><code class="language-c">&quot;userprog/process.c&quot;
static bool setup_stack(struct intr_frame *if_) {
    bool success = false;
    void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);

    /* TODO: Map the stack on stack_bottom and claim the page immediately.
     * TODO: If success, set the rsp accordingly.
     * TODO: You should mark the page is stack. */
    /* TODO: Your code goes here */
    if(vm_alloc_page(VM_ANON|VM_MARKER_0, stack_bottom, 1)) {
        // map the physical frame to the allocated page.
        success = vm_claim_page(stack_bottom);
        if (success) {
            if_-&gt;rsp = USER_STACK;
        }
    }
    return success;
}</code></pre>
<h3 id="5-vm_try_handle_fault">5) vm_try_handle_fault</h3>
<h4 id="when-is-this-function-called-3">When is this function called?</h4>
<h4 id="what-does-this-function-do-3">what does this function do?</h4>
<p>when the page fault is occured, this function gets the control.</p>
<blockquote>
<p>이 함수에서 할당된 물리 프레임이 존재하지 않아서 발생한 예외일 경우에는 매개변수인 not_present에 true를 전달받는다.
그럴 경우, SPT에서 해당 주소에 해당하는 페이지가 있는지 확인해서 존재한다면 해당 페이지에 물리 프레임 할당을 요청하는 vm_do_claim_page 함수를 호출한다.
이 함수에서는 위에서 설정한대로 각 페이지에 맞는 초기화 함수가 호출된다.
not_present 값이 false인 상황을 살펴보면,
물리 프레임이 할당되어 있지만 page fault가 일어난 것이므로 그 경우는 read-only page에 write를 한 경우가 된다. 따라서 not_present가 false인 경우는 예외로 처리하면 된다. (return false)
not_present가 true인 경우에도, read-only page에 write 요청을 할 경우가 생길 수 있으므로 이에 대한 예외 처리를 추가한다.</p>
</blockquote>
<pre><code class="language-c">    /* Return true on success */
    bool vm_try_handle_fault(struct intr_frame * f UNUSED, void *addr UNUSED,
                             bool user UNUSED, bool write UNUSED,
                             bool not_present UNUSED) {
        struct supplemental_page_table *spt UNUSED = &amp;thread_current()-&gt;spt;

        if (addr == NULL | is_kernel_vaddr(addr)) return false;
        // if the physical page doesn&#39;t exit
        if (not_present) {
            page = spt_find_page(spt, addr);
            if (page == NULL) return false;
            if (write == 1 &amp;&amp;
                page-&gt;writable ==
                    0) {  // if it&#39;s asking to write in unwritable page
                return false;
            }
            return vm_do_claim_page(page);
        }
        return false;
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1220 - TIL(Memory Management 구현)]]></title>
            <link>https://velog.io/@gigis-note/1220</link>
            <guid>https://velog.io/@gigis-note/1220</guid>
            <pubDate>Wed, 20 Dec 2023 13:24:48 GMT</pubDate>
            <description><![CDATA[<h1 id="task-list">Task list</h1>
<h2 id="memory-management">Memory Management</h2>
<ul>
<li>목표: 물리 메모리 로드 대신, supplement page table으로 메모리를 관리한다</li>
<li>구현<ul>
<li>Page Fault Handler 수정: kill 하지말고 spt에서 page를 찾고, 있으면 pte를 추가</li>
<li>struct thread, page, supplement_page_table, frame에 필요한 멤버 추가 가능</li>
<li>spt 관련 기본 함수 구현 (init, copy, kill 3가지)</li>
<li>종료 시 SPT를 참조하여 어떤 리소스를 해제할 지 결정</li>
<li>spt 관련 page 연산 함수 구현 (find, insert, remove)</li>
<li>frame 관련 함수 구현 (get, claim, do_claim)</li>
<li>Free Frame이 없으면(swap slot이 가득차면), victim page 선택(eviction policy 구현)<h2 id="anonymous-page">Anonymous Page</h2>
</li>
</ul>
</li>
<li>목표: Page Type에 따라 알맞게 처리하고, lazy loading을 구현한다.</li>
<li>구현<ul>
<li>page type에 따른 initializer 호출</li>
<li>struct anon_page에 필요한 멤버 추가 가능</li>
<li>처음은 무조건 uninit 페이지로 생성, 페이지 구조만 할당</li>
<li>lazy loading을 위해 load_segment while문 로직, setup_stack 로직 수정</li>
<li>Page Fault Handler에서 Valid한 Fault인지 체크
[여기까지 하면 fork 제외 Project2 테케 모두 통과]</li>
<li>fork를 고려한 SPT copy, kill 함수 구현</li>
<li>uninit_destroy, anon_destroy 구현
[ 여기까지 하면 Project2 테케 모두 통과 ]<h2 id="stack-growth">Stack Growth</h2>
</li>
</ul>
</li>
<li>목표: 스택이 현재 크기보다 커지면(Stack Access) 추가 페이지를 할당한다.</li>
<li>구현<ul>
<li>스택 포인터의 잘못된 access를 어디에서 체크할건지 고민</li>
<li>page fault handler에서 vm_stack_groth 호출, 구현 (최대 1MB 제한)<h2 id="memory-mapped-files">Memory Mapped Files</h2>
</li>
</ul>
</li>
<li>목표: File-backed Page를 구현한다.</li>
<li>구현<ul>
<li>lazy하게 do_mmap, do_munmap 구현</li>
<li>exit될 때 매핑 해제</li>
<li>file_init, initializer, destroy 구현<h2 id="swap-inout">Swap In/Out</h2>
</li>
</ul>
</li>
<li>목표: 메모리 할당량이 가득 차면, 프레임을 디스크로 교체한다.</li>
<li>구현<ul>
<li>Anon일 경우 anon_init, initializer 수정
swap_in, swap_out 구현(out을 먼저)</li>
<li>file-backed인 경우, 매핑된 파일에 다시 기록됨 init, initializer 수정
backed_swap_in, backed_swap_out 구현</li>
</ul>
</li>
</ul>
<hr>
<h2 id="memory-management-1">Memory Management</h2>
<p>현재 상태는 페이지 테이블만 있음(pml4).</p>
<h3 id="1-implementing-supplemental-page-table">1. Implementing Supplemental Page Table</h3>
<p>supplemental page table 구조체가 비어있다. 어떠한 정보가 필요한가?</p>
<p>페이지 폴트와 자원 관리를 처리해야함. 그럼 hash table이 일단 필요하겠다. hash 구조체 구경하기
<img src="https://velog.velcdn.com/images/gigis-note/post/85eb6a42-04a3-4203-8993-08e903da5d8c/image.png" alt=""></p>
<ul>
<li>Hash 구조체를 supplemental_page_table 구조체 안에 추가<pre><code class="language-c">&lt;vm.h&gt;
struct supplemental_page_table { 
  struct hash spt_hash;
};</code></pre>
</li>
<li>hash.h 에 주석을 보면 <code>Instead, each structure that can potentially be in a hash must embed a struct hash_elem member.</code> 이라고 되어있어서 page에 hash_elem 구조체 추가함<pre><code class="language-c">&lt;vm.h&gt;
struct page { 
  &#39;&#39;&#39;
  struct hash_elem hash_elem;
  &#39;&#39;&#39;
};</code></pre>
</li>
</ul>
<p>그리고 세가지 function을 손봐야한다.</p>
<h4 id="11-supplemental_page_table_init">1.1 supplemental_page_table_init()</h4>
<ul>
<li><p>void supplemental_page_table_init (struct supplemental_page_table *spt);</p>
<ul>
<li>spt를 initialize 하기 위해 hash_init이라는 함수를 사용하고 싶은데 인자로 hash_hash_func라는 hash 값을 구해주는 함수포인터와 hash_less_func라는 해쉬 엘리먼트들의 크기를 비교해주는 것들을 넘겨야한다.
<code>bool
hash_init (struct hash *h, hash_hash_func *hash, hash_less_func *less, void *aux)</code>
그렇다면 hash_hash_func와 hash_less_func를 참고해서 page_hash, page_less함수를 만들자.
만들었으니 hash_init함수를 사용해서 한줄로 초기화를 해버리자. </li>
<li><pre><code class="language-c">&lt;vm.c&gt;
/* Computes and returns the hash value for hash element E, */
unsigned
page_hash (const struct hash_elem *he, void *aux UNUSED) {
const struct page *p = hash_entry(he, struct page, hash_elem);
// hash_bytes: returns a hash of the size(sec arg) bytes in BUF(first arg) 
return hash_bytes (&amp;p-&gt;va, sizeof(p-&gt;va));
}
/* Returns true if A is less than B, or false if A is greater than or equal to B */
bool
page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) {
const struct page *pa = hash_entry(a, struct page, hash_elem);
const struct page *pb = hash_entry(b, struct page, hash_elem);
return pa-&gt;va &lt; pb-&gt;va;
}
/* Initialize new supplemental page table */
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
hash_init(spt, page_hash, page_less, NULL);
}</code></pre>
<h4 id="12-spt_find_page">1.2 spt_find_page()</h4>
</li>
</ul>
</li>
<li><p>struct page *spt_find_page (struct supplemental_page_table *spt, void *va);</p>
<ul>
<li><p>hash_find() 함수 사용하자. 주석 설명
Finds and returns an element equal to E in hash table H, or a
null pointer if no equal element exists in the table.
그렇다면 spt_find_page에서 인자로 받아오는 spt가 hash이니까 첫번째 매개변수로 넘기고, va를 아니까 page 할당받아서 va넣고 그 페이지의 hash_elem 을 넘기자.</p>
<pre><code class="language-c">/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
struct page *page = NULL;

page = palloc_get_page(PAL_USER);
page-&gt;va = va;

struct hash_elem *e;
e = hash_find(&amp;spt, &amp;page-&gt;hash_elem);

return e!=NULL ? hash_entry(e, struct page, hash_elem):NULL;
}</code></pre>
<h4 id="13-spt_insert_page">1.3 spt_insert_page()</h4>
</li>
</ul>
</li>
<li><p>bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);</p>
<ul>
<li><p>hash_insert(&amp;spt, &amp;page-&gt;hash_elem) 이렇게 쓰면 될것같은데 존재하지 않을 경우에만이라는 조건을 어떻게 넣을 수 있을까. 
hash_insert 함수의 주석을 보고 아이디어를 얻었다.
<code>Inserts NEW into hash table H and returns a null pointer, if no equal element is already in the table. If an equal element is already in the table, returns it without inserting NEW.</code>
그럼 없던 데이터를 넣을 땐 null pointer를 반환하니까 함수 값이 널일 때만 실행 되게 하면 되겠다. </p>
<pre><code class="language-c">/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
                 struct page *page UNUSED) {
int succ = false;

return hash_insert(&amp;spt, &amp;page-&gt;hash_elem) == NULL ? true : false;
}</code></pre>
<h3 id="2-frame-management">2. Frame Management</h3>
</li>
</ul>
</li>
</ul>
<p>Implement <code>vm_get_frame</code>, <code>vm_claim_page</code> and <code>vm_do_claim_page</code> in <code>vm/vm.c</code>.</p>
<h4 id="21-vm_get_frame">2.1 vm_get_frame</h4>
<pre><code class="language-c">/* Gets a new physical page from the user pool by calling palloc_get_page.
 * When successfully got a page from the user pool, also allocates a frame,
 * initialize its members, and returns it.
 * 사용자 풀에서 페이지를 성공적으로 가져오면, 프레임을 할당하고 해당 프레임의
 * 멤버를 초기화한 후 반환한다. 페이지 할당을 실패할 경우, PANIC (&quot;todo&quot;)로
 * 표시한다. (swap out을 구현한 이후 변경한다.)
 *
 * palloc() and get frame. If there is no available page, evict the page
 * and return it. This always return valid address. That is, if the user pool
 * memory is full, this function evicts the frame to get the available memory
 * space.*/
static struct frame *vm_get_frame(void) {
    struct frame *frame = NULL;
    /* TODO: Fill this function. */
    void *kva = palloc_get_page(PAL_USER);

    if (kva == NULL) {
        PANIC(&quot;todo&quot;);
    }
    // 프레임을 할당하고 해당 프레임의 멤버를 초기화한 후 반환한다
    frame = malloc(size_of(struct frame));
    frame-&gt;kva = kva;

    ASSERT(frame != NULL);
    ASSERT(frame-&gt;page == NULL);
    return frame;
}</code></pre>
<h4 id="22-vm_do_claim_page">2.2 vm_do_claim_page</h4>
<blockquote>
<p>git book 설명
Claims, meaning allocate a physical frame, a page. You first get a frame by calling vm_get_frame (which is already done for you in the template). Then, you need to set up the MMU. In other words, add the mapping from the virtual address to the physical address in the page table. The return value should indicate whether the operation was successful or not.</p>
</blockquote>
<p>프레임을 vm_get_frame으로 얻으라고 해서 얻고 frame이랑 page랑 밑에 함수에서 엮어주길래 나도 엮어주고 까지는 했는데 그 다음에서 <code>add the mapping from the virtual address to the physical address in the page table</code>
그니까 page table에서 가상주소에서 물리주소로 맵핑해줘라인데 page table에 어떻게 해줘야할지 난감했다.</p>
<p>page table 관련 함수가 threads/mmu.c에 있어서 함수를 뒤지다가 pml4_set_page() 함수의 주석을 보고 이놈으로 결정했다. 주석은 이렇다.</p>
<blockquote>
<p>Adds a mapping in page map level 4 PML4 from user virtual page UPAGE to the physical frame identified by kernel virtual address KPAGE.</p>
</blockquote>
<p>근데 pml4가 어디에 있느냐 말이다. 아 thread 구조체 안에 있다.</p>
<pre><code class="language-c">/* Claim the PAGE and set up the mmu. */
static bool vm_do_claim_page(struct page *page) {
    struct frame *frame = vm_get_frame();

    /* Set links */
    frame-&gt;page = page;
    page-&gt;frame = frame;

    // add the mapping from the virtual address to the physical address in the
    // page table (pml4_set_page())
    struct thread *curr = current_thread();
    pml4_set_page(curr-&gt;pml4, page-&gt;va, frame-&gt;kva, page-&gt;writable);

    return swap_in(page, frame-&gt;kva);
}</code></pre>
<h4 id="23-vm_claim_page">2.3 vm_claim_page</h4>
<blockquote>
<p>Claims the page to allocate va. You will first need to get a page and then calls vm_do_claim_page with the page.</p>
</blockquote>
<pre><code class="language-c">/* Claim the page that allocate on VA. */
bool vm_claim_page(void *va UNUSED) {
    struct page *page = NULL;
    // spt에서 va에 해당하는 page 찾기
    page = spt_find_page(&amp;thread_current()-&gt;spt, va);
    return vm_do_claim_page(page);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1219-TIL(task listing, Memory Management 구현, quiz)]]></title>
            <link>https://velog.io/@gigis-note/1219-TILtask-listingquiz</link>
            <guid>https://velog.io/@gigis-note/1219-TILtask-listingquiz</guid>
            <pubDate>Tue, 19 Dec 2023 04:21:53 GMT</pubDate>
            <description><![CDATA[<h1 id="task-listing">Task listing</h1>
<h2 id="memory-management">Memory Management</h2>
<ul>
<li>목표: 물리 메모리 로드 대신, supplement page table으로 메모리를 관리한다</li>
<li>구현<ul>
<li>Page Fault Handler 수정: kill 하지말고 spt에서 page를 찾고, 있으면 pte를 추가</li>
<li>struct thread, page, supplement_page_table, frame에 필요한 멤버 추가 가능</li>
<li>spt 관련 기본 함수 구현 (init, copy, kill 3가지)</li>
<li>종료 시 SPT를 참조하여 어떤 리소스를 해제할 지 결정</li>
<li>spt 관련 page 연산 함수 구현 (find, insert, remove)</li>
<li>frame 관련 함수 구현 (get, claim, do_claim)</li>
<li>Free Frame이 없으면(swap slot이 가득차면), victim page 선택(eviction policy 구현)<h2 id="anonymous-page">Anonymous Page</h2>
</li>
</ul>
</li>
<li>목표: Page Type에 따라 알맞게 처리하고, lazy loading을 구현한다.</li>
<li>구현<ul>
<li>page type에 따른 initializer 호출</li>
<li>struct anon_page에 필요한 멤버 추가 가능</li>
<li>처음은 무조건 uninit 페이지로 생성, 페이지 구조만 할당</li>
<li>lazy loading을 위해 load_segment while문 로직, setup_stack 로직 수정</li>
<li>Page Fault Handler에서 Valid한 Fault인지 체크
[여기까지 하면 fork 제외 Project2 테케 모두 통과]</li>
<li>fork를 고려한 SPT copy, kill 함수 구현</li>
<li>uninit_destroy, anon_destroy 구현
[ 여기까지 하면 Project2 테케 모두 통과 ]<h2 id="stack-growth">Stack Growth</h2>
</li>
</ul>
</li>
<li>목표: 스택이 현재 크기보다 커지면(Stack Access) 추가 페이지를 할당한다.</li>
<li>구현<ul>
<li>스택 포인터의 잘못된 access를 어디에서 체크할건지 고민</li>
<li>page fault handler에서 vm_stack_groth 호출, 구현 (최대 1MB 제한)<h2 id="memory-mapped-files">Memory Mapped Files</h2>
</li>
</ul>
</li>
<li>목표: File-backed Page를 구현한다.</li>
<li>구현<ul>
<li>lazy하게 do_mmap, do_munmap 구현</li>
<li>exit될 때 매핑 해제</li>
<li>file_init, initializer, destroy 구현<h2 id="swap-inout">Swap In/Out</h2>
</li>
</ul>
</li>
<li>목표: 메모리 할당량이 가득 차면, 프레임을 디스크로 교체한다.</li>
<li>구현<ul>
<li>Anon일 경우 anon_init, initializer 수정
swap_in, swap_out 구현(out을 먼저)</li>
<li>file-backed인 경우, 매핑된 파일에 다시 기록됨 init, initializer 수정
backed_swap_in, backed_swap_out 구현</li>
</ul>
</li>
</ul>
<hr>
<h2 id="kaist-list">kaist list</h2>
<p><a href="https://www.youtube.com/watch?v=8twIUEo1mIs">https://www.youtube.com/watch?v=8twIUEo1mIs</a></p>
<ul>
<li>Enable Demand paging/swapping</li>
<li>Enable Strck Growth<ul>
<li>Dynamic page allocation for page fault on stack</li>
</ul>
</li>
<li>Implement Memory mapped file<ul>
<li>Implement mmap() and munmap()</li>
<li>differentiate file backed page and anonymous page</li>
</ul>
</li>
<li>Enable Accewnssign user memory</li>
</ul>
<hr>
<h4 id="page_fault">page_fault()</h4>
<pre><code class="language-c">Userprog/exception.c
page_fault()
{

}</code></pre>
<ul>
<li><p>Check if the memory ref is valid</p>
<ul>
<li>valid<ul>
<li>vm page에 들어가야하는 내용 locate하기 
(공유페이지가 구현되어있다면, 페이지가 페이지 테이블에는 없는데 이미 페이지 프레임에 있을 수도 있다)</li>
</ul>
</li>
<li>invalid<ul>
<li>could be the invalid user address or Kernel address or permission error</li>
</ul>
</li>
</ul>
</li>
<li><p>Allocate page frame</p>
<ul>
<li>페이지 폴트가 나면 os는 모든 페이지 프레임을 스캔하고 하나의 페이지 프레임을 선택해서 디스크에서부터 완벽한 페이지를 load? allocate 해야한다 <img src="https://velog.velcdn.com/images/gigis-note/post/e5842ffe-3b81-4b48-bb57-aa3c3a8152ac/image.png" alt=""></li>
</ul>
</li>
<li><p>Fetch the data form the disk to the page frame</p>
</li>
<li><p>Update page table</p>
</li>
</ul>
<h4 id="additional-info-for-the-individual-virtual-pages">additional info for the individual virtual pages</h4>
<ul>
<li>Virtual page num</li>
<li>Read/Write permission</li>
<li>Type of virtual page<ul>
<li>(unwritable&amp;unmodifiable) a page of EyLF executable file</li>
<li>a page of general file</li>
<li>(anonymous) a page of swap area</li>
</ul>
</li>
<li>(memory mapped file) Reference to the file object(info about which file it is from?) and offset</li>
<li>amount of date in the page : the content size out of 4KB</li>
<li>location the swap area</li>
<li>In-momory flag: is it in memeory?</li>
</ul>
<h4 id="vm_entrydata-structure-set-of-attribute-each-page">vm_entry(data structure: set of attribute each page)</h4>
<h1 id="what-i-have-done">what I have done</h1>
<h2 id="memory-management-1">Memory Management</h2>
<ul>
<li>목표: 물리 메모리 로드 대신, supplemental page table으로 메모리를 관리한다</li>
<li>구현 list<ul>
<li><ol>
<li>Page Fault Handler 수정: kill 하지말고 spt에서 page를 찾고, 있으면 pte를 추가</li>
</ol>
</li>
<li><ol start="2">
<li>struct thread, page, supplement_page_table, frame에 필요한 멤버 추가 가능</li>
</ol>
</li>
<li><ol start="3">
<li>spt 관련 기본 함수 구현 (init, copy, kill 3가지)</li>
</ol>
</li>
<li><ol start="4">
<li>종료 시 SPT를 참조하여 어떤 리소스를 해제할 지 결정</li>
</ol>
</li>
<li><ol start="5">
<li>spt 관련 page 연산 함수 구현 (find, insert, remove)</li>
</ol>
</li>
<li><ol start="6">
<li>frame 관련 함수 구현 (get, claim, do_claim)</li>
</ol>
</li>
<li><ol start="7">
<li>Free Frame이 없으면(swap slot이 가득차면), victim page 선택(eviction policy 구현)</li>
</ol>
</li>
</ul>
</li>
</ul>
<p>현재 상태는 페이지 테이블만 있음(pml4).</p>
<h3 id="1번-page-fault-handler-수정-kill-하지말고-spt에서-page를-찾고-있으면-pte를-추가">1번 Page Fault Handler 수정: kill 하지말고 spt에서 page를 찾고, 있으면 pte를 추가</h3>
<h4 id="11-implementing-supplemental-page-table">1.1 Implementing Supplemental Page Table</h4>
<p>supplemental page table 구조체가 비어있다. 어떠한 정보가 필요한가?</p>
<p>페이지 폴트와 자원 관리를 처리해야함. 그럼 hash table이 일단 필요하겠다. hash 구조체 구경하기
<img src="https://velog.velcdn.com/images/gigis-note/post/85eb6a42-04a3-4203-8993-08e903da5d8c/image.png" alt=""></p>
<ul>
<li>Hash 구조체를 supplemental_page_table 구조체 안에 추가<pre><code class="language-c">&lt;vm.h&gt;
struct supplemental_page_table { 
  struct hash spt_hash;
};</code></pre>
</li>
<li>hash.h 에 주석을 보면 <code>Instead, each structure that can potentially be in a hash must embed a struct hash_elem member.</code> 이라고 되어있어서 page에 hash_elem 구조체 추가함<pre><code class="language-c">&lt;vm.h&gt;
struct page { 
  &#39;&#39;&#39;
  struct hash_elem hash_elem;
  &#39;&#39;&#39;
};</code></pre>
</li>
</ul>
<p>그리고 세가지 function을 손봐야한다.</p>
<ul>
<li><p>void supplemental_page_table_init (struct supplemental_page_table *spt);</p>
<ul>
<li>spt를 initialize 하기 위해 hash_init이라는 함수를 사용하고 싶은데 인자로 hash_hash_func?라는 것과 hash_less_func라는 function pointer들을 넘겨야한다.
<code>bool
hash_init (struct hash *h, hash_hash_func *hash, hash_less_func *less, void *aux)</code>
그렇다면 hash_hash_func와 hash_less_func를 참고해서 page_hash, page_less함수를 만들자.
만들었으니 hash_init함수를 사용해서 한줄로 초기화를 해버리자. </li>
<li><pre><code class="language-c">&lt;vm.c&gt;
/* Computes and returns the hash value for hash element E, */
unsigned
page_hash (const struct hash_elem *he, void *aux UNUSED) {
const struct page *p = hash_entry(he, struct page, hash_elem);
// hash_bytes: returns a hash of the size(sec arg) bytes in BUF(first arg) 
return hash_bytes (&amp;p-&gt;va, sizeof(p-&gt;va));
}
/* Returns true if A is less than B, or false if A is greater than or equal to B */
bool
page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) {
const struct page *pa = hash_entry(a, struct page, hash_elem);
const struct page *pb = hash_entry(b, struct page, hash_elem);
return pa-&gt;va &lt; pb-&gt;va;
}
/* Initialize new supplemental page table */
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
hash_init(spt, page_hash, page_less, NULL);
}</code></pre>
</li>
</ul>
</li>
<li><p>struct page *spt_find_page (struct supplemental_page_table *spt, void *va);</p>
<ul>
<li><p>hash_find() 함수 사용하자. 주석 설명
Finds and returns an element equal to E in hash table H, or a
null pointer if no equal element exists in the table.
그렇다면 spt_find_page에서 인자로 받아오는 spt가 hash이니까 첫번째 매개변수로 넘기고, va를 아니까 page 할당받아서 va넣고 그 페이지의 hash_elem 을 넘기자.</p>
<pre><code class="language-c">/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
struct page *page = NULL;

page = malloc(size_of(struct page)); // 얘는 왜이래
page-&gt;va = va;

struct hash_elem *e;
e = hash_find(&amp;spt, &amp;page-&gt;hash_elem);

return e!=NULL ? hash_entry(e, struct page, hash_elem):NULL;
}</code></pre>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);</p>
<ul>
<li><p>hash_insert(&amp;spt, &amp;page-&gt;hash_elem) 이렇게 쓰면 될것같은데 존재하지 않을 경우에만이라는 조건을 어떻게 넣을 수 있을까. 
hash_insert 함수의 주석을 보고 아이디어를 얻었다.
<code>Inserts NEW into hash table H and returns a null pointer, if no equal element is already in the table. If an equal element is already in the table, returns it without inserting NEW.</code>
그럼 없던 데이터를 넣을 땐 null pointer를 반환하니까 함수 값이 널일 때만 실행 되게 하면 되겠다. </p>
<pre><code class="language-c">/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
                 struct page *page UNUSED) {
int succ = false;

return hash_insert(&amp;spt, &amp;page-&gt;hash_elem) == NULL ? true : false;
}</code></pre>
</li>
</ul>
</li>
</ul>
<h1 id="quiz">quiz</h1>
<h2 id="1번-문제-tlb">1번 문제: TLB</h2>
<p>1.1 TLB 가 어떻게 페이지 테이블의 성능을 향상시키는지?</p>
<pre><code>1.1 TLB는 Table Load Buffer 의 약자로 가상주소를 물리주소로 변환하는 것을 위한 페이지 테이블의 성능을 향상시키기 위해 있는 것으로, cache 기능과 비슷하게 시간지역성을 활용해서 한번 페이지 테이블을 통해 가져온 데이터를 TLB에 업데이트하여 다음에 같은 페이지를 참조하게 되면 TLB를 먼저 확인해서 페이지 테이블을 통해 변환하는 작업을 하지 않아도 되게 하는 것이다.</code></pre><p>1.2 TLB miss 가 발생하면 시스템이 어떤 과정을 거쳐 메모리에 접근하는지?</p>
<p>1.2 TLB miss가 나면 크게 하드웨어가 처리하느냐, 소프트웨어가 처리하느냐에 따라 처리 방식이 다르다.
전통적으로는 하드웨어가 처리하는 방식인데 이는,</p>
<pre><code>1. 페이지 참조가 유효한지 확인하고
2. 페이지 테이블에서 사용가능한 page를 확인하고 (사용 가능한 free page가 없다면 eviction policy에 따라 victim 을 선정하고 swap disk 에 swap 한다)
3. backup disk 에서 완벽한 free frame을 load하고
4. 페이지 테이블과 TLB를 업데이트한다.</code></pre><p>소프트웨어가 처리하는 방식은 페이지 테이블을 거치지 않고 바로 가져와서 업데이트하는데 정확히는 기억이 나질 않는다. -&gt; 이부분 찾아보기 </p>
<blockquote>
<p>내가 잘못 생각했던거 TLB 미스나면 페이지테이블 참조하는건데..! 아는건데 잘못생각했었다.</p>
</blockquote>
<h3 id="정답">정답</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/011ff9a5-a00e-44cf-b3fb-0c791a5fefa1/image.png" alt=""></p>
<h2 id="2번-문제-belady의-역설">2번 문제: Belady의 역설</h2>
<p>paging 기법을 사용하는데 page fault 의 발생 빈도가 더 늘어나는 현상. 원인과 방지하는 해결방법.</p>
<h3 id="정답-1">정답</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/d009062a-f2e7-4fdb-b3df-3e51484adaae/image.png" alt=""></p>
<h2 id="3번-문제-thrashing-현상이란">3번 문제: Thrashing 현상이란</h2>
<p>page 부재가 너무 많이 발생하여 cpu 성능이 떨어지는 현상</p>
<blockquote>
<p>&quot;프로세스가 페이지 부재를&quot; 이라고 설명하면 더 좋겠다</p>
</blockquote>
<h3 id="정답-2">정답</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/ee1ad3ba-f219-44cd-afba-dadf11df8409/image.png" alt=""></p>
<h2 id="4번-문제-운영체제가-anonymous-page를-0으로-초기화-하는-이유">4번 문제: 운영체제가 anonymous page를 0으로 초기화 하는 이유</h2>
<p>anonymous page는 이미 페이지와 파일이 맵핑되어있는 file backed page 혹은 memory mapped page와는 다르게 페이지가 아무런 파일과도 맵핑되어 있지 않는 페이지를 말하는데 이를 초기화하지 않으면 페이지에 쓰레기 값이 들어가 있기 때문에 0으로 초기화 하는것이 좋다는 것으로 알고있다.</p>
<h3 id="정답-3">정답</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/e9bb8b20-9664-47b9-babb-b0a1011f487d/image.png" alt=""></p>
<h2 id="5번-문제-lru-캐시-알고리즘">5번 문제: LRU 캐시 알고리즘</h2>
<p>5-1. 요청순서 9번의 캐시상태를 쓰시오 -&gt; 5967
5-2. </p>
<p>clock 알고리즘 LRU와 비슷하게 성능을 낼 수 있는데 현실적으로 cost가 더 적어서 LRU대신 많이 쓰이는 것으로 알고 있다. </p>
<p>알고리즘 작동 방식은 정확하게는 모르겠다..
2  [1, 2, 3, 4] [1, 1, 1, 1]
5  [1, 5, 3, 4] [0, 1, 0, 0]
1  [1, 5, 3, 4] [1, 1, 0, 0]
2  [1, 5, 2, 4] [0, 0, 1, 0]
3  [1, 2, 3, 4] [1, 1, 1, 1]
4  [1, 2, 3, 4] [1, 1, 1, 1]
5  [1, 2, 3, 4] [1, 1, 1, 1]</p>
<h3 id="정답-4">정답</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/bcf0970e-7efd-4ee8-889a-95d3755ed106/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/9e3549cb-6b8e-401c-a2fc-d933a1de0ad4/image.png" alt=""></p>
<p><a href="https://www.youtube.com/watch?v=C26qsPwf-Js">https://www.youtube.com/watch?v=C26qsPwf-Js</a></p>
<p>이 아저씨가 푸는 방식으로 풀면 엄청 쉽다. </p>
<p>rule 몇가지만 유의하면서 풀면 된다.</p>
<ul>
<li><p>누굴 쫓아낼 필요 없을때</p>
<ul>
<li>frame에 넣고 ref를 1로 set (나머지 애들의 ref를 바꿀 필요는 없다)</li>
</ul>
</li>
<li><p>쫓아내야할 때 (second chance를 주는 것임을 기억하자) (page fault가 발)</p>
<ul>
<li>victim frame의 page를 보고 1이면 기회를 한번 더 주는 의미로 0으로 바꾸고 다음걸로 넘어간다. (0을 만날때까지 반복)</li>
<li>어떤 페이지의 ref가 0이면 킥하고 새로 넣은 page의 ref를 1로 set.<ul>
<li>나머지 프레임(아래로만 바꾸고 맨 위로 올라가진 않는다)들의 ref를 0으로 set</li>
<li>victim page를 하나 아래로 바꾼다.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1218-TIL(diff btw Linux and Pintos)]]></title>
            <link>https://velog.io/@gigis-note/1218-TILdiff-btw-Linux-and-Pintos</link>
            <guid>https://velog.io/@gigis-note/1218-TILdiff-btw-Linux-and-Pintos</guid>
            <pubDate>Tue, 19 Dec 2023 01:33:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>1218 월요일</li>
</ul>
</blockquote>
<ul>
<li><input checked="" disabled="" type="checkbox"> 권영진교수님 os강의</li>
<li><input checked="" disabled="" type="checkbox"> 동료학습(keywords, and git book) </li>
<li><input disabled="" type="checkbox"> filling the gap -&gt; 아파서 나중에</li>
<li><input disabled="" type="checkbox"> Gitbook/PPT 등 보고 Task 리스트업&amp;구현 순서 생각</li>
</ul>
<p><a href="https://joong-sunny.github.io/swjungle%20survive/week11-1/">https://joong-sunny.github.io/swjungle%20survive/week11-1/</a></p>
<p>충격적인 사실. 내가 아는거. 책에서 배운거. 그니까 리눅스에서의 할당과 pintos에서의 할당이 다르다고한다</p>
<p>말록으로 할당하면 힙 영역에 mmap으로 할당하면 memory mapped 영역에 할당한다.</p>
<p>근데 pintos 에서는 memory mapped 영역도 없고 힙도 없다. 유저 옵션을 주지 않으면 모두 커널 pool에 할당되게 된다.
<img src="https://velog.velcdn.com/images/gigis-note/post/00ab519b-9f16-4aac-a167-582d1b900d11/image.png" alt=""></p>
<p>하..미치겠다</p>
<h3 id="userstack-at-linux">userstack at Linux</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/9290efbe-4bff-4b5e-803f-ce9347849649/image.png" alt=""></p>
<h3 id="userstack-at-pintos">userstack at Pintos</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/dcbb3269-f7d1-46d3-9640-932e965500e5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/2b01d095-a789-4411-80fa-fb96ad5a8842/image.png" alt=""></p>
<h3 id="pintos에는-mapped-memory-영역이-없다">pintos에는 mapped memory 영역이 없다</h3>
<p>일단 내가 아는거는 이런시긍로 스택과 힙영역 사이에 shared memory영역이 즉, memory mapped 영역이 존재하는데 pintos에는 없다</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/f2405061-834e-4529-9db4-548d0bd29cc1/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/a64081f7-9831-46b7-afb7-773e96279e84/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1216-TIL(Keywords reg. VM)]]></title>
            <link>https://velog.io/@gigis-note/1216-TILKeywords-reg.-VM</link>
            <guid>https://velog.io/@gigis-note/1216-TILKeywords-reg.-VM</guid>
            <pubDate>Mon, 18 Dec 2023 04:58:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>1216 토요일 - 10시 30분 출근 (점심 2시간)</li>
</ul>
</blockquote>
<ul>
<li>[O] Keywords 정리</li>
<li><input disabled="" type="checkbox"> kaist gitbook Project3 FAQ, Appendix Memory Allocation, Virtual Address, Page Table  읽기</li>
<li><input disabled="" type="checkbox"> ostep or concepts 읽으면서 키워드 보충하기</li>
<li><input disabled="" type="checkbox"> TIL </li>
</ul>
<h1 id="keywords">keywords</h1>
<h2 id="virtual-memory">Virtual Memory</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/e56e8354-c1bc-4d6f-a827-ff73290eea41/image.png" alt=""></p>
<h3 id="benefits">benefits</h3>
<ul>
<li><p>메모리 크기
프로그램이 더이상 물리메모리의 크기에 제약을 받지 않는다.</p>
</li>
<li><p>시스템 성능 향상 (better CPU Util, throughput)
각 프로그램은 물리메모리에 크기를 적게 차지하기 때문에 더 많은 프로그램들이 동시에 실행될 수 있게되어 CPU 활용도와 처리량이 증가하게 된다. 이로 인해 응답시간, 반환 시간은 증가하지 않으면서도 시스템 성능 향상.</p>
</li>
<li><p>시스템 속도 향상
프로그램이 메모리에 로드하고 스왑하는 I/O 작업이 적게 필요하게 되어 각 프로그램은 더 빠르게 실행될 수 있다.</p>
</li>
</ul>
<h3 id="목표">목표</h3>
<ul>
<li><p>투명성(transparency)
운영체제는 실행 중인 프로그램이 가상 메모리의 존재를 인지하지 못하도록 가상 메모리 시스템을 구현해야 한다.</p>
</li>
<li><p>효율성(eiciency)
운영체제는 가상화가 시간과 공간 측면에서 효율적이도록 해야 한다. 시간적으로는 프로그램이 너무 느리게 실행되서는 안되고, 공간적으로는 가상화를 지원하기 위한 구조를 위해 너무 많은 메모리를 사용해서는 안된다.</p>
</li>
<li><p>보호(protection)
운영체제는 프로세스를 다른 프로세스로부터 보호해야하고 운영체제 자기 자신도 보호해야 한다. 프로세스가 탑재, 저장, 혹은 명령어 반입 등을 실행할 때 어떤 방법으로든 다른 프로세스나 운영체제의 메모리 내용에 접근하거나 영향을 줄 수 있어서는 안 된다.</p>
</li>
</ul>
<blockquote>
<p>우리가 c언어를 작성할 떄 볼 수 있는 주소는 물리주소일까 가상주소일까? 정답은 가상주소.
그렇다면 우리는 물리주소를 프로그래밍을 통해 볼 수 있을까? 정답은 NO. 오로지 운영체제 뿐이 명령어와 데이터가 탑재되어 있는 물리 메모리의 주소를 알 수 있다.</p>
</blockquote>
<h3 id="shared-libraries">shared libraries</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/96953610-ec79-4ee4-8005-14b1cac866e1/image.png" alt=""></p>
<p>논리 메모리와 물리 메모리를 나눔으로써 두개 이상의 프로세스들이 파일(standard C library)과 메모리를 공유할 수 있게 한다.
프로세스 생성 중에 fork() 시스템콜을 사용함으로써 페이지들을 공유함으로써 프로세스의 생성을 더 빠르게 한다.</p>
<h2 id="page-table">Page Table</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/4bc88864-d739-4d54-9475-c867eaa221b0/image.png" alt=""></p>
<ul>
<li><p>페이지테이블 관리 코드 @ <code>threads/mmu.c</code></p>
</li>
<li><p>페이지 테이블은 CPU가 가상 주소를 물리 주소로 변환하는 데 사용하는 데이터 구조. 페이지에서 프레임으로 변환하는 도구. <code>Virt Addr -&gt; Phys Addr</code></p>
<h2 id="translation-lookaside-buffer-tlb">Translation Lookaside Buffer (TLB)</h2>
</li>
<li><p>특수한 작은 고속 룩업 하드웨어 캐시를 사용함. (기능도 캐시랑 거의 비슷)</p>
</li>
<li><p>히트율이 높은 이유
시간 지역성(temporal locality) 으로 인해 TLB의 히트율이 높아진다. 시간 지역성이란 한번 참조된 메모리 영역이 짧은 시간 내에 재 참조되는 현상을 일컫는다. 다른 캐시와 마찬가지로 TLB의 성공 여부는 프로그램의 공간 지역성과 시간 지역성 존재 여부에 달려 있다. 만약 프로그램이 공간 혹은 시간 지역성을 보이는 경우, TLB 사용 효과가 더욱 두드러지게 나타날 것이다.</p>
</li>
<li><p>TLB가 가득 차 있으면 어떡하나?
기존 내역을 하나 골라서 새로 넣을 걸로 교체해야함(LRU, RR, or Random 정책들 중에서 골라서. tmi: LRU연산은 너무 비싸서 잘 안한다고 하고, 랜덤 정책은 은근 hit ratio가 평균값 나오기 때문에 자주 쓰인다고 함) </p>
</li>
<li><p>TLB miss 를 누가 처리하나? -&gt; either hardware or software depends on the operating system. </p>
<ul>
<li>by hardware (Intel x86 is the example of managed by HW that uses <code>multi-level page table</code>)<ul>
<li>(흐름 아래 그림 참고) </li>
<li>페이지 테이블에서 원하는 페이지 테이블 엔트리 찾는다</li>
<li>필요한 변환 정보를 추출한다</li>
<li>TLB를 갱신한다</li>
<li>TLB 미스가 발생한 명령어를 재실행한다</li>
</ul>
</li>
<li>by software (RISC라는 CISC보다 최근에 등장한 컴퓨터 구조로 RISC기반 컴퓨터에서는 TLB miss를 처리하는 과정을 살펴보자)<ul>
<li>TLB에서 주소 찾기 실패시 하드웨어가 예외 시그널 발생</li>
<li>시그널을 받은 os는 명령어 실행을 일시 정지하고 실행모드에서 커널모드로 변경함으로 커널 코드 실행을 준비한다</li>
<li>트랩 핸들러를 실행한다</li>
<li>페이지 테이블을 검색해서 변환 정보를 찾고 TLB 접근이 가능한 &quot;previleged&quot; 명령어를 사용해서 TLB를 갱신한 후에 리턴한다</li>
<li>하드웨어가 명령어를 재실행한다</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="흐름">흐름</h3>
<p>[하드웨어가 TLB miss를 처리하는 경우]
<img src="https://velog.velcdn.com/images/gigis-note/post/d91444ef-e98c-4d22-87e9-9cbb08aa6dc9/image.png" alt=""></p>
<ul>
<li><p>case(1) cpu ➡️ TLB @MMU ➡️ HIT! ➡️ Phys Addr</p>
</li>
<li><p>case(2) cpu ➡️ TLB @MMU ➡️ MISS! ➡️ Page Table @MainMemory ➡️ HIT! ➡️ Phys Addr</p>
</li>
<li><p>case(3) cpu ➡️ TLB @MMU ➡️ MISS! ➡️ Page Table @MainMemory ➡️ MISS! ➡️ Page Fault</p>
</li>
</ul>
<h2 id="page-fault">Page Fault</h2>
<p><code>Supplemental Page Table</code>의 vip 손님은 page fault handler 임.</p>
<ul>
<li>페이지 폴트 핸들러가 의미하는 바</li>
</ul>
<p>프로젝트 2 에서는 페이지 폴트는 항상 커널이나 유저 프로그램의 버그를 나타냄.
프로젝트 3 에서는 더이상 저것만을 의미하지는 않는다. (좀 더 명확해졌다)</p>
<p>이제 페이지 폴트는 단순히 파일이나 스왑슬롯에서 페이지를 가져와야 한다는 뜻, 아니면 단순히 모든 비트가 0인 페이지 라거나. (페이지 폴트가 의미하는 경우의 수는 줄어들었지만 이제 이렇게 바뀌기 위해서는 우리(학생)가 page fault handler를 좀 더 손봐야 한다.)</p>
<h3 id="페이지-폴트-발생-이후-프로시저">페이지 폴트 발생 이후 프로시저</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/40122675-8419-4273-ab42-579c29a223cc/image.png" alt=""></p>
<ol>
<li><p>내부 테이블(보통 PCB, Process Control Block)을 확인해서 해당 프로세스의 메모리 참조가 유효한지 아닌지 결정하기 위해 확인한다.</p>
</li>
<li><p>유효하지 않은 참조라면 프로세스를 종료시킨다. 유효한데 해당 페이지에 가져오지를 못한거라면 이제 가져온다.</p>
</li>
<li><p>free-frame list에서 하나의 free frame을 찾는다.</p>
</li>
<li><p>원하는 페이지를 새로 할당된 프레임으로 읽어오기 위해 보조 저장 장치 작업을 스케줄한다.</p>
</li>
<li><p>저장소로부터 읽어오는게 끝났다면 내부 테이블을 새로 메모리에 적재된 페이지를 가르킬 수 있도록 업데이트한다.</p>
</li>
<li><p>인스트럭션을 재시작해서 프로세스가 이제 해당 페이지에 접근할 수 있게한다. 마치 항상 메모리에 있었던 것처럼. </p>
</li>
</ol>
<h3 id="페이지-폴트-핸들러의-프로시저more-like-orders">페이지 폴트 핸들러의 프로시저(more like orders)</h3>
<ul>
<li>페이지 폴트 핸들러는 누가 호출?</li>
</ul>
<p><code>userprog/exception.c</code> 에 있는 <code>page_fault()</code> 가 <code>vm/vv.c</code> 에 있는 <code>vm_try_handle_fault()</code>라는 이름을 가진 페이지 폴트 핸들러를 호출한다.</p>
<ol>
<li><code>supplemental page table</code>에서 부재가 발생한 페이지 찾기.</li>
<li>페이지를 저장하기 위해 프레임을 획득하기.</li>
<li>데이터를 파일시스템이나 스왑에서 읽어오기. (읽어오거나 0으로 초기화하는 방식으로 만들어서 프레임으로 가져옴)  </li>
<li>폴트가 발생한 가상 주소에 대한 PTE가 물리 페이지를 가리키도록 지정하기.</li>
</ol>
<h2 id="lazy-loading">Lazy Loading</h2>
<p>보통 lazy loading이라고 하면 웹에서 사용하는 기술을 이야기 하는데 OS에서도 virtual memory에서 비슷한 개념이 쓰이는데 Demand paging이라고 한다. </p>
<p><code>Demand-paged virtual memeory</code> 에서는 프로그램 실행 중에 페이지가 요구될 때만 로드된다. 이는 </p>
<h2 id="page-replacement-policy">Page Replacement Policy</h2>
<h2 id="anonymous-page">Anonymous page</h2>
<h2 id="swap-disk">Swap Disk</h2>
<h2 id="file-backed-page">File-backed Page</h2>
<h2 id="direct-memory-access">Direct Memory Access</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[1218 - 권영진 교수님 OS 특강 ]]></title>
            <link>https://velog.io/@gigis-note/1218</link>
            <guid>https://velog.io/@gigis-note/1218</guid>
            <pubDate>Mon, 18 Dec 2023 04:57:07 GMT</pubDate>
            <description><![CDATA[<p>만약에 4키로바이트 하드디스크의 원자 유닛 </p>
<p>운영체제가 <code>Crash consistency</code> 를 해결 못해주면 application programmer가 해야하는데 이거 잘 안함. 그러나 너무 중요.</p>
<p>foo 를 bar로 업데이트해줄거면 foo 아니면 bar만 accept</p>
<p>Rollback logging</p>
<p>create(/a/log)
write(/a/log, &quot;Foo&quot;)
write(/a/file, &quot;Bar&quot;)
unlink(/a/log)</p>
<p>but</p>
<p>Possible cases</p>
<p><code>Fao</code>
<code>For</code></p>
<p>해결 방법 -&gt; fsync</p>
<p>create(/a/log)
write(/a/log, &quot;Foo&quot;)
fsync(/a/log)
write(/a/file, &quot;Bar&quot;)
fsync(/a/file)
unlink(/a/log)</p>
<p>but still possible cases</p>
<p>Fao
For</p>
<p>but still problem</p>
<p>create하는 동시에 directory block(이것마저 디스크에 적어야하기때문에)
해결하기위해
디렉토리에 fsysnc를 하나 더 넣음</p>
<p>create(/a/log)
write(/a/log, &quot;Foo&quot;)
fsync(/a/log)
fsync(/a/) ---&gt; added one 
write(/a/file, &quot;Bar&quot;)
fsync(/a/file)
unlink(/a/log)</p>
<p>End of Abstraction stories</p>
<hr>
<h2 id="protection-and-isolation">Protection and Isolation</h2>
<p>OS must regain control from app -&gt; timer interrupt</p>
<ol>
<li>Privileged instruction<ul>
<li>하드웨어가 디텍트해서 익셉션을 커널에게 보내고 커널이 결정한다</li>
<li>즉, 하드웨어가 결정권을 커널에게 endorse 함으로써 protection한다.</li>
<li>하드웨어가 privilge 가 더 높음. 결정권을 os에게 endorse</li>
<li>하드웨어는 detect를 하는 매커니즘을 가지고 있고 os가 이걸 어떻게 할지에 대한 policy를 가진다. 즉, detect &lt;-&gt; policy(seperate design term)</li>
</ul>
</li>
<li>Memory protection</li>
<li>(Timer) interrupt</li>
</ol>
<h3 id="isolation">Isolation</h3>
<p>Isolation 이라고 하는건</p>
<ol>
<li>data read &amp; write 를 </li>
<li>control (내 코드로 다른 앱으로 점프 하면 안돼)</li>
</ol>
<p>Isolation by protection domain = process</p>
<p>Isolation boundary -&gt; process</p>
<p>memory abstraction -&gt; virtual address space를 분리시킴으로써 isolation을 한다.</p>
<p>File은 Isolation을 permission system으로 함. 모든 메커니즘과 policy가 OS가 해줌. 권한 체크해주는 polocy에 해당되는 친구가 Reference monitor임 </p>
<p>Web P 사이에 communication 할 수 있게 하는 것. IPC라고 한다.
IPC 기법에는 두가지가 있다.</p>
<ul>
<li>Shared memory (공유 물리 메모리)
<img src="https://velog.velcdn.com/images/gigis-note/post/1f41a299-0abb-4cb0-9231-a80a1bd6bc4b/image.png" alt=""></li>
<li>message passing with buffer (pipe, filefor, sema, etc)</li>
</ul>
<p>같은 물리주소에서 어느 vm으로 가야한다고 알 수 있는 것이 cache coherence.</p>
<p>프로세스에서 다른 프로세스에게 값이 바뀌었다고 어떻게 알려줄 수 있는가? Signal. but it&#39;s slow. 그래서 쓰는게 Polling.</p>
<p>shared memory는 data copy가 안일어난다</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/7fbab347-65f7-4e1a-b258-9a151b8e4a41/image.png" alt="">
msg passing 은 data copy가 일어난다</p>
<p>그래서</p>
<p>데이터가 작으면 카피 코스트가 적으니까 msg passing
데이터가 크면 shared memeory 쓴다</p>
<p>근데 일반적으로 편해서 데이터 커도 msg passing 쓴다</p>
<h3 id="sharing">sharing</h3>
<ul>
<li>Time sharing -&gt; cpu -&gt; scheduling </li>
<li>Space sharing -&gt; memory <ul>
<li><img src="https://velog.velcdn.com/images/gigis-note/post/33fb01e7-63e6-4a5d-a6f1-0d3c5d91f02a/image.png" alt=""></li>
<li>page eviction은 File backed, swap은 Anon</li>
</ul>
</li>
</ul>
<h2 id="sch">sch</h2>
<ul>
<li>fifo <ul>
<li>con: convoy effect(high response time)</li>
</ul>
</li>
<li>SJF<ul>
<li>con: starvation</li>
</ul>
</li>
<li>RR<ul>
<li>con: turnaound time(low throughput)</li>
</ul>
</li>
</ul>
<p>Annon : 최초 - 0으로 밀어 / swap된 page 가져오기
File backed : loading from file/binary</p>
<p>page eviction하는걸 page reclamation(?)이라고 </p>
<p>page replacement policies
LRU 는 구현코스트가 너무 비쌈. 커널에 page fault 계속 내야해 
Clock은 LRU를 approximates해서 사용한다.
근데 1번이 누군지는 몰라. 그렇지만 맨 뒤에가 누군지만 알면 되는거니까 ㄱㅊ</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/be18db5f-7381-4277-a6ce-8b00e2c2b8b8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1215-TIL(gitbook)]]></title>
            <link>https://velog.io/@gigis-note/1215</link>
            <guid>https://velog.io/@gigis-note/1215</guid>
            <pubDate>Fri, 15 Dec 2023 12:35:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>1215 금요일 - 10시 출근 (1시간 커피챗)</li>
</ul>
</blockquote>
<ul>
<li><input checked="" disabled="" type="checkbox"> kaist gitbook Project3 Virtual Memory 읽기</li>
<li><input disabled="" type="checkbox"> kaist gitbook Project3 FAQ, Appendix Memory Allocation, Virtual Address, Page Table  읽기</li>
<li><input checked="" disabled="" type="checkbox"> 공부 키워드 TIL 정리</li>
</ul>
<hr>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/cafe8562-dc3c-4437-9e34-d6c943f36c78/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/eae0a86d-2c96-41ee-b35c-7dd21022056c/image.png" alt="">
<img src="https://velog.velcdn.com/images/gigis-note/post/fc61d888-e7a0-4f00-8616-6f763f97d373/image.png" alt=""></p>
<p>by metamong</p>
<hr>
<h1 id="gitbook-project3-virtual-memory---intro">gitbook Project3 Virtual Memory - Intro</h1>
<p><a href="https://casys-kaist.github.io/pintos-kaist/project3/introduction.html">https://casys-kaist.github.io/pintos-kaist/project3/introduction.html</a></p>
<h2 id="source-files">Source files</h2>
<ul>
<li>include/devices/block.h, devices/block.c</li>
</ul>
<p>Provides sector-based read and write access to block device. You will use this interface to access the swap partition as a block device.</p>
<p>이라고 나와있는데 이상함. 경로에 해당 파일 없음</p>
<p>아, 깃북 업뎃이 안된거라고 한다. 현재 위치는 아래임
<code>include/devices/disk.h</code>, <code>devices/disk.c</code> </p>
<h2 id="memory-terminology">Memory Terminology</h2>
<h3 id="pages">pages</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/eef21377-b1c4-4f52-8d95-de4b823cb4d8/image.png" alt=""></p>
<ul>
<li><p>페이지, 때로 가상 페이지라고도 불리는 것은 길이가 4,096바이트(페이지 크기)인 가상 메모리의 연속된 영역이다.</p>
</li>
<li><p>페이지는 무조건 &#39;page-aligned&#39; 되어야하며, 페이지 크기로 균등하게 나눌 수 있는 가상 주소에서 시작해야 한다.</p>
</li>
<li><p>64비트 가상 주소의 마지막 12비트는 페이지 오프셋(또는 단순히 오프셋)이다. (상위 비트는 페이지 테이블에서의 인덱스를 나타냄)</p>
</li>
<li><p>각 프로세스는 하나의 독립적인 <code>set of user(virtual)pages</code> 를 가진다. which 가상주소  KERN_BASE(0x8004000000) 아래에 있는</p>
</li>
<li><p><code>set of kernel (virtual) pages</code>는  전역이므로 어떤 스레드나 프로세스가 실행 중이든 위치가 동일함</p>
</li>
<li><p>커널은 유저페이지 &amp; 커널페이지에 접근 가능. but 유저페이지는 자신의 사용자 페이지에만 접근 가능</p>
</li>
</ul>
<h3 id="frames">Frames</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/bd621c78-22ba-4553-862b-32bc0dfc35b7/image.png" alt=""></p>
<ul>
<li><p>프레임(physical frame or a page frame)은 물리 메모리의 연속된 영역임</p>
</li>
<li><p>페이지와 마찬가지로 프레임은 <code>page-size</code>이며 <code>page-aligned</code> 되어야 함. (noticed point: 뭔가 프레임 사이즈? 라고 할 것 같은데 프레임도 페이지 사이즈, 페이지 얼라인 되어야한다고 한다)</p>
</li>
<li><p>x86-64 아키텍처에서는 물리 주소에 직접적으로 접근할 수 있는 방법을 제공하지 않음.</p>
</li>
<li><p>Pintos는 이를 우회하기 위해 커널 가상 메모리를 물리 메모리에 직접 매핑함.</p>
</li>
</ul>
<h3 id="tlb-translation-look-aside-buffer">TLB (translation look-aside buffer)</h3>
<ul>
<li><p>직역하면 변환 색인 버퍼라는 이름. 특수한 작은 고속 룩업 하드웨어 캐시를 사용함. (기능이 캐시랑 거의 비슷한듯)</p>
</li>
<li><p>TLB가 가득 차 있으면 기존 내역을 하나 골라서 새로 넣을 걸로 교체해야함(LRU, RR, or Random 정책들 중에서 골라서. tmi: LRU연산은 너무 비싸서 잘 안한다고 하고, 랜덤 정책은 은근 hit ratio가 평균값 나오기 때문에 자주 쓰인다고 함) </p>
</li>
</ul>
<h3 id="page-tables">Page Tables</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/4bc88864-d739-4d54-9475-c867eaa221b0/image.png" alt=""></p>
<ul>
<li><p>페이지테이블 관리 코드 @ <code>threads/mmu.c</code></p>
</li>
<li><p>페이지 테이블은 CPU가 가상 주소를 물리 주소로 변환하는 데 사용하는 데이터 구조. 페이지에서 프레임으로 변환하는 도구. <code>Virt Addr -&gt; Phys Addr</code></p>
</li>
</ul>
<h3 id="흐름-정리">흐름 정리</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/d91444ef-e98c-4d22-87e9-9cbb08aa6dc9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/eae0a86d-2c96-41ee-b35c-7dd21022056c/image.png" alt=""></p>
<ul>
<li><p>case(1) cpu ➡️ TLB @MMU ➡️ HIT! ➡️ Phys Addr</p>
</li>
<li><p>case(2) cpu ➡️ TLB @MMU ➡️ MISS! ➡️ Page Table @MainMemory ➡️ HIT! ➡️ Phys Addr</p>
</li>
<li><p>case(3) cpu ➡️ TLB @MMU ➡️ MISS! ➡️ Page Table @MainMemory ➡️ MISS! ➡️ Page Fault</p>
</li>
</ul>
<h2 id="resource-management-overview">Resource Management Overview</h2>
<h3 id="supplemental-page-table">Supplemental page table</h3>
<p>supplementing 페이지 테이블을 제공해서 페이지 폴트 핸들링이 작동할 수 있게 함.</p>
<p>Enables page fault handling by supplementing the page table. See Managing the Supplemental Page Table below.</p>
<h3 id="frame-table">Frame table</h3>
<p>물리프레임의 효율적인 방출 정책 구현을 허용(?)</p>
<p>Allows efficient implementation of eviction policy of physical frames. See Managing the Frame Table below.</p>
<h3 id="swap-table">Swap table</h3>
<p>스왑 스롯의 사용 상태를 트랙할 수 있음</p>
<p>Tracks usage of swap slots. See Managing the Swap Table below.</p>
<blockquote>
<p>근데 이 세 개의 독립적인 데이터구조를 구현할 필요는 없음. 관련 리소스를 완전히 아님 부분적으로 통합해서 통일된 제이터 구조로 구성하는 것이 편리할 수 있음</p>
</blockquote>
<blockquote>
<p>내가 해야할 것은 각 엘리먼트에 어떤 정보가 담겨야 하는지, 데이터 구조의 스콥(local or global), 몇개의 인스턴스가 스콥 안에 필요로 할지 임.</p>
</blockquote>
<blockquote>
<p>설계를 간단하게 하기 위해서 non-pageable memory (e.g., memory allocated by calloc or malloc)에 자료구조를 저장하면 좋다.</p>
</blockquote>
<h3 id="구현시-고를-수-있는-옵션">구현시 고를 수 있는 옵션</h3>
<ul>
<li><p>리스트나 배열은 비효율적이라 추천하는건 비트맵 or 해시테이블. (Pintos에는 lib/kernel/bitmap.c 및 include/lib/kernel/bitmap.h에 비트맵 데이터 구조가 포함되어 있음)</p>
</li>
<li><p>비트맵은 참 또는 거짓이 되는 비트의 배열임.</p>
</li>
<li><p>비트맵은 보통 동일한 리소스의 사용을 추적하는데 사용됨.</p>
</li>
<li><p>핀토스의 비트맵은 크기가 고정되어있음. 하지만 크기 조정가능하게 확장도 가능함.</p>
</li>
<li><p>해시테이블은 다양한 크기의 테이블에서 효율적인 삽입 삭제를 지원함</p>
</li>
</ul>
<h2 id="managing-the-supplemental-page-table">Managing the Supplemental Page Table</h2>
<ul>
<li>&quot;보충 페이지 테이블&quot; why 보충?</li>
</ul>
<p>왜 <code>Page Table</code> 이라고 안하고 <code>Supplemental Page Table</code> 라고 하냐면, </p>
<p>보충 페이지 테이블은 페이지 테이블의 형식에 따른 제약으로 인해 각 페이지에 대한 추가 데이터를 제공하는 데이터 구조인데 이게 주로 &quot;페이지 테이블&quot; 이라고 불려서 혼란을 막기 위해 앞에 &quot;보충&quot; 이라는 말음 붙였다.</p>
<ul>
<li><code>Supplemental Page Table</code>의 목적 주요 두 가지</li>
</ul>
<p>첫 째로, (주요 목적) 페이지 폴트가 났을 때, <code>Supplemental Page Table</code>에서 부재가 발생한 가상 페이지를 조회하여 해당 위치에 어떤 데이터가 있어야 하는지 확인 하는 것이다.</p>
<p>둘 째로, 프로세스가 종료될 때, 어떤 자원을 해제할지 결정하기 위해 <code>Supplemental Page Table</code>를 참고한다.</p>
<h3 id="organization-of-supplemental-page-table">Organization of Supplemental Page Table</h3>
<ul>
<li><code>Supplemental Page Table</code>를 꾸리는 두 가지 방법<ul>
<li>세그먼트를 기준으로<ul>
<li>여기서 말하는 세그먼트는 연속된 페이지 그룹을 말함. 예를들어, 실행 가능한 파일이나 메모리 맵핑 파일을 포함하는 메모리 영역</li>
</ul>
</li>
<li>페이지를 기준으로</li>
<li>(Optionally) 역설적이긴 한데 보충 테이블 페이지의 멤버를 tracking하기 위해 페이지 테이블을 사용하는 것도 가능. (잘하는 학생들에게만 추천요)</li>
</ul>
</li>
</ul>
<h3 id="handling-page-fault">Handling page fault</h3>
<p><code>Supplemental Page Table</code>의 vip 손님은 page fault handler 임.</p>
<ul>
<li>페이지 폴트 핸들러가 의미하는 바</li>
</ul>
<p>프로젝트 2 에서는 페이지 폴트는 항상 커널이나 유저 프로그램의 버그를 나타냄.
프로젝트 3 에서는 더이상 저것만을 의미하지는 않는다. (좀 더 명확해졌다)</p>
<p>이제 페이지 폴트는 단순히 파일이나 스왑슬롯에서 페이지를 가져와야 한다는 뜻, 아니면 단순히 모든 비트가 0인 페이지 라거나. (페이지 폴트가 의미하는 경우의 수는 줄어들었지만 이제 이렇게 바뀌기 위해서는 우리(학생)가 page fault handler를 좀 더 손봐야 한다.)</p>
<ul>
<li>페이지 폴트 핸들러는 누가 호출?</li>
</ul>
<p><code>userprog/exception.c</code> 에 있는 <code>page_fault()</code> 가 <code>vm/vv.c</code> 에 있는 <code>vm_try_handle_fault()</code>라는 이름을 가진 페이지 폴트 핸들러를 호출한다.</p>
<ul>
<li>페이지 폴트 핸들러가 해야하는 일들(more like orders)<ol>
<li><code>supplemental page table</code>에서 부재가 발생한 페이지 찾기.</li>
<li>페이지를 저장하기 위해 프레임을 획득하기.</li>
<li>데이터를 파일시스템이나 스왑에서 읽어오기. (읽어오거나 0으로 초기화하는 방식으로 만들어서 프레임으로 가져옴)  </li>
<li>폴트가 발생한 가상 주소에 대한 PTE가 물리 페이지를 가리키도록 지정하기.</li>
</ol>
</li>
</ul>
<h2 id="managing-the-frame-table">Managing the Frame Table</h2>
<ul>
<li><p>하는 일
비어있는 프레임이 없을 때 쫓아낼 페이지를 골라줌. 즉, eviction policy를 구현할 수 있도록 해줌</p>
</li>
<li><p>구성</p>
<ul>
<li>프레임 테이블 안의 각 프레임은 하나의 엔트리 정보를 갖음</li>
<li>각 엔트리에는 해당 엔트리를 차지하고 있는 페이지에 대한 포인터(있으면)와 내가 선택해서 넣을 수 있는 기타 데이터들이 있음</li>
</ul>
</li>
<li><p>사용 방법</p>
<ul>
<li>유저 페이지를 위해 프레임을 사용하고 싶으면 <code>palloc_get_page(PAL_USER)</code> 호출.(이렇게 해야 유저 풀에서 획득된 것임이 확실함)</li>
<li>사용되지 않은 프레임을 획득하는 것이 중요. 근데 free상태인 프레임이 없으면 몇몇 페이지들을 쫓아내서 그 프레임을 free 상태로 만들어 줘야 함</li>
</ul>
</li>
<li><p>eviction 절차</p>
<ol>
<li>페이지 재배치 알고리즘 이용해서 victim 고르기. 페이지 테이블에 있는 &quot;accessed&quot;, &quot;dirty&quot; 비트들이 유용.</li>
<li>해당 프레임을 참조하는 모든 페이지 테이블에서 참조를 제거. (공유를 구현하지 않았을 경우는 해당 프레임을 참조하는 페이지는 항상 한 개만 존재 해야 함) </li>
<li>(if necessary) 페이지를 파일 시스템이나 스왑에 write하기. (쫓겨난 프레임은 이제 다른 페이지를 저장하는 데에 사용할 수 있음)</li>
</ol>
</li>
</ul>
<h3 id="accessed-and-dirty-bits">Accessed and Dirty Bits</h3>
<ul>
<li><p>페이지에 read 하면 CPU는 페이지의 PTE에 있는 accessed bit를 1로 설정한다. </p>
</li>
<li><p>write 할 때는 accessed bit 와 dirty bit 를 1로 설정한다.</p>
</li>
<li><p>CPU는 절대 이 비트들을 0으로 되돌릴 수 없음. only the OS can.</p>
</li>
<li><p>같은 프레임을 참조하는 두 개이상의 페이지들인 <code>aliases</code> 를 유의해야한다. aliased 프레임이 accessed 될 때, accessed 비트와 dirty 비트는 하나의 페이지 테이블 엔트리에서만 업데이트됨</p>
</li>
<li><p>pintos에서 모든 유저 가상 페이지는 커널 가상 페이지에 alias 되어있음. 내가 이 alias들을 관리해야 함</p>
</li>
<li><p>alias 관리 예시</p>
<ul>
<li>내 code가 accessed 와 dirty 비트를 업데이트 할 때 양쪽 주소 모두를 확인하고 업데이트 해야함</li>
<li>유저 가상 주소를 통해서만 유저 데이터에 접근하게 함으로써 커널이 이 문제를 피하게 할 수 있음</li>
</ul>
</li>
</ul>
<h2 id="managing-the-swap-table">Managing the Swap Table</h2>
<ul>
<li>역할<ul>
<li>스왑테이블은 사용중인 스왑 슬롯과 빈 스왑 슬롯들을 추적하기.</li>
<li>페이지가 다시 읽히거나 페이지 주인인 프로세스가 종료되어 버릴 경우엔 스왑 테이블이 스왑 슬롯을 free 하는거 승낙해주기.</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li>n-MB swap partition을 포함하는 디스크 생성 방법 </li>
</ul>
</blockquote>
<ul>
<li><code>swap.dsk</code>를 생성하려면 <code>vm/build</code> 경로에서 <code>pintos-mkdisk swap.dsk --swap-size=n</code> 명령어를 사용</li>
<li><code>swap.dsk</code>를 만들면 pintos 실행할 때 자동으로 추가 디스크로 연결됨.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1214-WIL]]></title>
            <link>https://velog.io/@gigis-note/1214-WIL</link>
            <guid>https://velog.io/@gigis-note/1214-WIL</guid>
            <pubDate>Thu, 14 Dec 2023 01:38:01 GMT</pubDate>
            <description><![CDATA[<h2 id="arguments-passing">Arguments Passing</h2>
<h3 id="흐름-이해에-도움이-되었던-그림">흐름 이해에 도움이 되었던 그림</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/0f1cb599-e8f9-416f-8e91-e4b0288f3d79/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/d9004978-de68-499e-9774-8200792e6cbf/image.png" alt=""></p>
<h2 id="아쉬운-점-1">아쉬운 점 1</h2>
<h3 id="돌아는-가지만-가독성이-매우-떨어집니다">돌아는 가지만 가독성이 매우 떨어집니다</h3>
<h4 id="스택포인터-형변환-한번-하려고-하면">스택포인터 형변환 한번 하려고 하면...</h4>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/fa62fcf1-e2d5-4f09-be48-8131aa8d7ad4/image.png" alt=""></p>
<h3 id="문제가-뭐였을까">문제가 뭐였을까</h3>
<h4 id="스택포인터-인자를-이렇게-받았던-것">스택포인터 인자를 이렇게 받았던 것</h4>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/3da6e3e8-c667-4f0f-b3d8-a4f3b52500e8/image.png" alt=""></p>
<h4 id="그래서-한번-쓰려고-하면">그래서 한번 쓰려고 하면</h4>
<p><code>parse[i]</code> -&gt; <code>*(char **) rsp</code>
<code>parse[i][j]</code> -&gt; <code>**(char **) rsp</code></p>
<h4 id="주소값-저장하는-코드">주소값 저장하는 코드</h4>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/4ab2f491-f805-4c6b-be0b-1e82c2487bbb/image.png" alt=""></p>
<h3 id="더-좋은-방법">더 좋은 방법</h3>
<h4 id="befors">BEFORS</h4>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/c40bf227-3de6-4a7f-89aa-aff4d338717d/image.png" alt=""></p>
<h4 id="as">AS</h4>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/6d5b9181-1cc6-4e0c-b41e-4025a3b3665b/image.png" alt=""></p>
<h3 id="char-rsp-➡-voidif_-rsp"><strong>(char *</strong>)rsp ➡ (void*)(if_-&gt;rsp)</h3>
<hr>
<h2 id="아쉬운점-2">아쉬운점 2</h2>
<h3 id="비효율적인-문자열-저장-방식">비효율적인 문자열 저장 방식</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/7388263d-5e3a-4068-b618-3893e82a8945/image.png" alt=""></p>
<h3 id="동료-코드">동료 코드</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/dcaf1187-cfdc-44a8-b648-94b57962ed89/image.png" alt=""></p>
<h2 id="system-call">system call</h2>
<p>작성예정</p>
<hr>
<h2 id="느낀점">느낀점</h2>
<h3 id="1-도큐먼트도-좋지만-남들은-어떻게-하는지-관찰하자">1) 도큐먼트도 좋지만 남들은 어떻게 하는지 관찰하자.</h3>
<h3 id="2til은-사진이다">2)TIL은 사진이다.</h3>
<h4 id="왜냐">왜냐</h4>
<h4 id="1-여행가서-남는건-사진-밖에-없듯이-공부-후-남는건-정리한-글-뿐-기억력을-믿지-말자">1. 여행가서 남는건 사진 밖에 없듯이 공부 후 남는건 정리한 글 뿐. 기억력을 믿지 말자.</h4>
<h4 id="2-순간이-지나가면-놓치고-만다-오늘-하루가-가기-전에-잡아두자">2. 순간이 지나가면 놓치고 만다. 오늘 하루가 가기 전에 잡아두자.</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[1210-TIL(pintos project2 arg passing)-작성중]]></title>
            <link>https://velog.io/@gigis-note/arguementpassing</link>
            <guid>https://velog.io/@gigis-note/arguementpassing</guid>
            <pubDate>Mon, 11 Dec 2023 05:05:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gigis-note/post/a2eb19f7-beb5-4343-b90c-4e9f2c9b3f9a/image.png" alt=""></p>
<ul>
<li><p>process_exec() </p>
<ul>
<li>file_name문자열을파싱</li>
<li>첫번째토큰을thread_create()함수에스레드이름으로전달</li>
</ul>
</li>
<li><p>start_process()</p>
<ul>
<li>file_name문자열파싱 &lt;- 여기서 또?</li>
<li>argument_stack()함수를이용해스택에토큰들을저장</li>
</ul>
</li>
<li><p>arguement_stack()</p>
<ul>
<li>유저스택에프로그램이름과인자들을저장하는함수</li>
<li>parse: 프로그램이름과인자가저장되어있는 메모리공간, count: 인자의개수, esp: 스택포인터를가리키는주소</li>
</ul>
</li>
</ul>
<p>한양대 슬라이드 내용인데 왜 process-exec에서 첫번쨰 토큰을 thread_create()함수에 전달하나?</p>
<hr>
<p>음..큰그림을 모르겠다. 이해를 돕기 위해 블로그 참고 <a href="https://velog.io/@daelkdev/Pintos-Project-2-%EC%A0%95%EB%A6%AC">블로그</a>
<img src="https://velog.velcdn.com/images/gigis-note/post/d9004978-de68-499e-9774-8200792e6cbf/image.png" alt=""></p>
<p>&lt;init.c&gt;
→ int main(void) 
→ run_actions(argv) 
→ run_task(char **argv) 
→ process_wait(process_create_initd (task));</p>
<p>&lt;process.c&gt;
→ process_create_initd(task) 
→ thread_create(file_name, PRI_DEFAULT, initd, fn_copy) 
→ initd()
→ process_exec()
→ load(입력값, if)
load에서 입력값 parsing, <em>if에 주소저장, load 성공 후 실행할 명령어 주소저장
    // 이진 파일을 디스크에서 메모리로 로드한다.
    // 로드된 후 실행할 메인 함수의 시작 주소 필드 초기화 (if</em>.rip)
    // user stack의 top 포인터 초기화 (if_.rsp)
    // 위 과정을 성공하면 실행을 계속하고, 실패하면 스레드가 종료된다.</p>
<hr>
<p>잠깐, 조금 더 큰 그림으로 프로그램의 전체 흐름을 다시 제대로 짚고 넘어가자</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/0f1cb599-e8f9-416f-8e91-e4b0288f3d79/image.png" alt=""></p>
<hr>
<h3 id="처음-시도한-코드">처음 시도한 코드</h3>
<pre><code>#include &quot;userprog/process.h&quot;
#include &lt;debug.h&gt;
#include &lt;inttypes.h&gt;
#include &lt;round.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &quot;userprog/gdt.h&quot;
#include &quot;userprog/tss.h&quot;
#include &quot;filesys/directory.h&quot;
#include &quot;filesys/file.h&quot;
#include &quot;filesys/filesys.h&quot;
#include &quot;threads/flags.h&quot;
#include &quot;threads/init.h&quot;
#include &quot;threads/interrupt.h&quot;
#include &quot;threads/palloc.h&quot;
#include &quot;threads/thread.h&quot;
#include &quot;threads/mmu.h&quot;
#include &quot;threads/vaddr.h&quot;
#include &quot;intrinsic.h&quot;
#ifdef VM
#include &quot;vm/vm.h&quot;
#endif

static void process_cleanup(void);
static bool load(const char *file_name, struct intr_frame *if_);
static void initd(void *f_name);
static void __do_fork(void *);

/* General process initializer for initd and other process. */
static void
process_init(void)
{
    struct thread *current = thread_current();
}

/* Starts the first userland program, called &quot;initd&quot;, loaded from FILE_NAME.
 * The new thread may be scheduled (and may even exit)
 * before process_create_initd() returns. Returns the initd&#39;s
 * thread id, or TID_ERROR if the thread cannot be created.
 * Notice that THIS SHOULD BE CALLED ONCE. */
tid_t process_create_initd(const char *file_name)
{
    char *fn_copy;
    tid_t tid;

    /* Make a copy of FILE_NAME.
     * Otherwise there&#39;s a race between the caller and load(). */
    fn_copy = palloc_get_page(0);
    if (fn_copy == NULL)
        return TID_ERROR;
    strlcpy(fn_copy, file_name, PGSIZE);

    /* --------------project 2 : arguemnt passing------------ */
    char *save_ptr;
    strtok_r(file_name, &quot; &quot;, &amp;save_ptr);
    /* ------------------------------------------------------ */

    /* Create a new thread to execute FILE_NAME. */
    tid = thread_create(file_name, PRI_DEFAULT, initd, fn_copy);
    if (tid == TID_ERROR)
        palloc_free_page(fn_copy);
    return tid;
}

/* A thread function that launches first user process. */
static void
initd(void *f_name)
{
#ifdef VM
    supplemental_page_table_init(&amp;thread_current()-&gt;spt);
#endif

    process_init();

    if (process_exec(f_name) &lt; 0)
        PANIC(&quot;Fail to launch initd\n&quot;);
    NOT_REACHED();
}

/* Clones the current process as `name`. Returns the new process&#39;s thread id, or
 * TID_ERROR if the thread cannot be created. */
tid_t process_fork(const char *name, struct intr_frame *if_ UNUSED)
{
    /* Clone current thread to new thread.*/
    return thread_create(name,
                         PRI_DEFAULT, __do_fork, thread_current());
}

#ifndef VM
/* Duplicate the parent&#39;s address space by passing this function to the
 * pml4_for_each. This is only for the project 2. */
static bool
duplicate_pte(uint64_t *pte, void *va, void *aux)
{
    struct thread *current = thread_current();
    struct thread *parent = (struct thread *)aux;
    void *parent_page;
    void *newpage;
    bool writable;

    /* 1. TODO: If the parent_page is kernel page, then return immediately. */

    /* 2. Resolve VA from the parent&#39;s page map level 4. */
    parent_page = pml4_get_page(parent-&gt;pml4, va);

    /* 3. TODO: Allocate new PAL_USER page for the child and set result to
     *    TODO: NEWPAGE. */

    /* 4. TODO: Duplicate parent&#39;s page to the new page and
     *    TODO: check whether parent&#39;s page is writable or not (set WRITABLE
     *    TODO: according to the result). */

    /* 5. Add new page to child&#39;s page table at address VA with WRITABLE
     *    permission. */
    if (!pml4_set_page(current-&gt;pml4, va, newpage, writable))
    {
        /* 6. TODO: if fail to insert page, do error handling. */
    }
    return true;
}
#endif

/* A thread function that copies parent&#39;s execution context.
 * Hint) parent-&gt;tf does not hold the userland context of the process.
 *       That is, you are required to pass second argument of process_fork to
 *       this function. */
static void
__do_fork(void *aux)
{
    struct intr_frame if_;
    struct thread *parent = (struct thread *)aux;
    struct thread *current = thread_current();
    /* TODO: somehow pass the parent_if. (i.e. process_fork()&#39;s if_) */
    struct intr_frame *parent_if;
    bool succ = true;

    /* 1. Read the cpu context to local stack. */
    memcpy(&amp;if_, parent_if, sizeof(struct intr_frame));

    /* 2. Duplicate PT */
    current-&gt;pml4 = pml4_create();
    if (current-&gt;pml4 == NULL)
        goto error;

    process_activate(current);
#ifdef VM
    supplemental_page_table_init(&amp;current-&gt;spt);
    if (!supplemental_page_table_copy(&amp;current-&gt;spt, &amp;parent-&gt;spt))
        goto error;
#else
    if (!pml4_for_each(parent-&gt;pml4, duplicate_pte, parent))
        goto error;
#endif

    /* TODO: Your code goes here.
     * TODO: Hint) To duplicate the file object, use `file_duplicate`
     * TODO:       in include/filesys/file.h. Note that parent should not return
     * TODO:       from the fork() until this function successfully duplicates
     * TODO:       the resources of parent.*/

    process_init();

    /* Finally, switch to the newly created process. */
    if (succ)
        do_iret(&amp;if_);
error:
    thread_exit();
}

/* Switch the current execution context to the f_name.
 * Returns -1 on fail. */
int process_exec(void *f_name)
{
    char *file_name = f_name;
    bool success;

    /* We cannot use the intr_frame in the thread structure.
     * This is because when current thread rescheduled,
     * it stores the execution information to the member. */
    struct intr_frame _if;
    _if.ds = _if.es = _if.ss = SEL_UDSEG;
    _if.cs = SEL_UCSEG;
    _if.eflags = FLAG_IF | FLAG_MBS;

    /* We first kill the current context */
    process_cleanup();

    /* --------------project 2 : arguemnt passing------------ */
    int argc = 0;
    char *argv[64];
    char *ret_ptr, *next_ptr;

    ret_ptr = strtok_r(file_name, &quot; &quot;, &amp;next_ptr);
    while (ret_ptr) // Until ret_ptr is not NULL
    {
        argv[argc++] = ret_ptr;                      // Store the current token on argv array
        ret_ptr = strtok_r(NULL, &quot; &quot;, &amp;next_ptr); // Find the next token from next_ptr and store at the ret_ptr including NULL
    }
    /* ------------------------------------------------------ */

    /* And then load the binary */
    success = load(file_name, &amp;_if);

    /* --------------project 2 : arguemnt passing------------ */
    argument_stack(argv, argc, &amp;_if); // Create the argument stack
    printf(&#39;Im having a nightmare indeed -------------------------!&#39;);
    hex_dump(_if.rsp, _if.rsp, USER_STACK - (uint64_t)_if.rsp, true); // Display the contents of the arg stack using hex_dump
    /* ------------------------------------------------------ */

    /* If load failed, quit. */
    palloc_free_page(file_name);
    if (!success)
        return -1;

    /* Start switched process. */
    do_iret(&amp;_if);
    NOT_REACHED();
}

/* Waits for thread TID to die and returns its exit status.  If
 * it was terminated by the kernel (i.e. killed due to an
 * exception), returns -1.  If TID is invalid or if it was not a
 * child of the calling process, or if process_wait() has already
 * been successfully called for the given TID, returns -1
 * immediately, without waiting.
 *
 * This function will be implemented in problem 2-2.  For now, it
 * does nothing. */
int process_wait(tid_t child_tid UNUSED)
{
    /* XXX: Hint) The pintos exit if process_wait (initd), we recommend you
     * XXX:       to add infinite loop here before
     * XXX:       implementing the process_wait. */
    for (int i = 0; i &lt; 100000000; i++)
    {
    } // temeral code to test the argument passing
    return -1;
}

/* Exit the process. This function is called by thread_exit (). */
void process_exit(void)
{
    struct thread *curr = thread_current();
    /* TODO: Your code goes here.
     * TODO: Implement process termination message (see
     * TODO: project2/process_termination.html).
     * TODO: We recommend you to implement process resource cleanup here. */

    process_cleanup();
}

/* Free the current process&#39;s resources. */
static void
process_cleanup(void)
{
    struct thread *curr = thread_current();

#ifdef VM
    supplemental_page_table_kill(&amp;curr-&gt;spt);
#endif

    uint64_t *pml4;
    /* Destroy the current process&#39;s page directory and switch back
     * to the kernel-only page directory. */
    pml4 = curr-&gt;pml4;
    if (pml4 != NULL)
    {
        /* Correct ordering here is crucial.  We must set
         * cur-&gt;pagedir to NULL before switching page directories,
         * so that a timer interrupt can&#39;t switch back to the
         * process page directory.  We must activate the base page
         * directory before destroying the process&#39;s page
         * directory, or our active page directory will be one
         * that&#39;s been freed (and cleared). */
        curr-&gt;pml4 = NULL;
        pml4_activate(NULL);
        pml4_destroy(pml4);
    }
}

/* Sets up the CPU for running user code in the nest thread.
 * This function is called on every context switch. */
void process_activate(struct thread *next)
{
    /* Activate thread&#39;s page tables. */
    pml4_activate(next-&gt;pml4);

    /* Set thread&#39;s kernel stack for use in processing interrupts. */
    tss_update(next);
}

/* We load ELF binaries.  The following definitions are taken
 * from the ELF specification, [ELF1], more-or-less verbatim.  */

/* ELF types.  See [ELF1] 1-2. */
#define EI_NIDENT 16

#define PT_NULL 0            /* Ignore. */
#define PT_LOAD 1            /* Loadable segment. */
#define PT_DYNAMIC 2        /* Dynamic linking info. */
#define PT_INTERP 3            /* Name of dynamic loader. */
#define PT_NOTE 4            /* Auxiliary info. */
#define PT_SHLIB 5            /* Reserved. */
#define PT_PHDR 6            /* Program header table. */
#define PT_STACK 0x6474e551 /* Stack segment. */

#define PF_X 1 /* Executable. */
#define PF_W 2 /* Writable. */
#define PF_R 4 /* Readable. */

/* Executable header.  See [ELF1] 1-4 to 1-8.
 * This appears at the very beginning of an ELF binary. */
struct ELF64_hdr
{
    unsigned char e_ident[EI_NIDENT];
    uint16_t e_type;
    uint16_t e_machine;
    uint32_t e_version;
    uint64_t e_entry;
    uint64_t e_phoff;
    uint64_t e_shoff;
    uint32_t e_flags;
    uint16_t e_ehsize;
    uint16_t e_phentsize;
    uint16_t e_phnum;
    uint16_t e_shentsize;
    uint16_t e_shnum;
    uint16_t e_shstrndx;
};

struct ELF64_PHDR
{
    uint32_t p_type;
    uint32_t p_flags;
    uint64_t p_offset;
    uint64_t p_vaddr;
    uint64_t p_paddr;
    uint64_t p_filesz;
    uint64_t p_memsz;
    uint64_t p_align;
};

/* Abbreviations */
#define ELF ELF64_hdr
#define Phdr ELF64_PHDR

static bool setup_stack(struct intr_frame *if_);
static bool validate_segment(const struct Phdr *, struct file *);
static bool load_segment(struct file *file, off_t ofs, uint8_t *upage,
                         uint32_t read_bytes, uint32_t zero_bytes,
                         bool writable);

/* Loads an ELF executable from FILE_NAME into the current thread.
 * Stores the executable&#39;s entry point into *RIP
 * and its initial stack pointer into *RSP.
 * Returns true if successful, false otherwise. */
static bool
load(const char *file_name, struct intr_frame *if_)
{
    struct thread *t = thread_current();
    struct ELF ehdr;
    struct file *file = NULL;
    off_t file_ofs;
    bool success = false;
    int i;

    /* Allocate and activate page directory. */
    t-&gt;pml4 = pml4_create();
    if (t-&gt;pml4 == NULL)
        goto done;
    process_activate(thread_current());

    /* Open executable file. */
    file = filesys_open(file_name);
    if (file == NULL)
    {
        printf(&quot;load: %s: open failed\n&quot;, file_name);
        goto done;
    }

    /* Read and verify executable header. */
    if (file_read(file, &amp;ehdr, sizeof ehdr) != sizeof ehdr || memcmp(ehdr.e_ident, &quot;\177ELF\2\1\1&quot;, 7) || ehdr.e_type != 2 || ehdr.e_machine != 0x3E // amd64
        || ehdr.e_version != 1 || ehdr.e_phentsize != sizeof(struct Phdr) || ehdr.e_phnum &gt; 1024)
    {
        printf(&quot;load: %s: error loading executable\n&quot;, file_name);
        goto done;
    }

    /* Read program headers. */
    file_ofs = ehdr.e_phoff;
    for (i = 0; i &lt; ehdr.e_phnum; i++)
    {
        struct Phdr phdr;

        if (file_ofs &lt; 0 || file_ofs &gt; file_length(file))
            goto done;
        file_seek(file, file_ofs);

        if (file_read(file, &amp;phdr, sizeof phdr) != sizeof phdr)
            goto done;
        file_ofs += sizeof phdr;
        switch (phdr.p_type)
        {
        case PT_NULL:
        case PT_NOTE:
        case PT_PHDR:
        case PT_STACK:
        default:
            /* Ignore this segment. */
            break;
        case PT_DYNAMIC:
        case PT_INTERP:
        case PT_SHLIB:
            goto done;
        case PT_LOAD:
            if (validate_segment(&amp;phdr, file))
            {
                bool writable = (phdr.p_flags &amp; PF_W) != 0;
                uint64_t file_page = phdr.p_offset &amp; ~PGMASK;
                uint64_t mem_page = phdr.p_vaddr &amp; ~PGMASK;
                uint64_t page_offset = phdr.p_vaddr &amp; PGMASK;
                uint32_t read_bytes, zero_bytes;
                if (phdr.p_filesz &gt; 0)
                {
                    /* Normal segment.
                     * Read initial part from disk and zero the rest. */
                    read_bytes = page_offset + phdr.p_filesz;
                    zero_bytes = (ROUND_UP(page_offset + phdr.p_memsz, PGSIZE) - read_bytes);
                }
                else
                {
                    /* Entirely zero.
                     * Don&#39;t read anything from disk. */
                    read_bytes = 0;
                    zero_bytes = ROUND_UP(page_offset + phdr.p_memsz, PGSIZE);
                }
                if (!load_segment(file, file_page, (void *)mem_page,
                                  read_bytes, zero_bytes, writable))
                    goto done;
            }
            else
                goto done;
            break;
        }
    }

    /* Set up stack. */
    if (!setup_stack(if_))
        goto done;

    /* Start address. */
    if_-&gt;rip = ehdr.e_entry;

    /* TODO: Your code goes here.
     * TODO: Implement argument passing (see project2/argument_passing.html). */

    success = true;

done:
    /* We arrive here whether the load is successful or not. */
    file_close(file);
    return success;
}

/* Checks whether PHDR describes a valid, loadable segment in
 * FILE and returns true if so, false otherwise. */
static bool
validate_segment(const struct Phdr *phdr, struct file *file)
{
    /* p_offset and p_vaddr must have the same page offset. */
    if ((phdr-&gt;p_offset &amp; PGMASK) != (phdr-&gt;p_vaddr &amp; PGMASK))
        return false;

    /* p_offset must point within FILE. */
    if (phdr-&gt;p_offset &gt; (uint64_t)file_length(file))
        return false;

    /* p_memsz must be at least as big as p_filesz. */
    if (phdr-&gt;p_memsz &lt; phdr-&gt;p_filesz)
        return false;

    /* The segment must not be empty. */
    if (phdr-&gt;p_memsz == 0)
        return false;

    /* The virtual memory region must both start and end within the
       user address space range. */
    if (!is_user_vaddr((void *)phdr-&gt;p_vaddr))
        return false;
    if (!is_user_vaddr((void *)(phdr-&gt;p_vaddr + phdr-&gt;p_memsz)))
        return false;

    /* The region cannot &quot;wrap around&quot; across the kernel virtual
       address space. */
    if (phdr-&gt;p_vaddr + phdr-&gt;p_memsz &lt; phdr-&gt;p_vaddr)
        return false;

    /* Disallow mapping page 0.
       Not only is it a bad idea to map page 0, but if we allowed
       it then user code that passed a null pointer to system calls
       could quite likely panic the kernel by way of null pointer
       assertions in memcpy(), etc. */
    if (phdr-&gt;p_vaddr &lt; PGSIZE)
        return false;

    /* It&#39;s okay. */
    return true;
}

#ifndef VM
/* Codes of this block will be ONLY USED DURING project 2.
 * If you want to implement the function for whole project 2, implement it
 * outside of #ifndef macro. */

/* load() helpers. */
static bool install_page(void *upage, void *kpage, bool writable);

/* Loads a segment starting at offset OFS in FILE at address
 * UPAGE.  In total, READ_BYTES + ZERO_BYTES bytes of virtual
 * memory are initialized, as follows:
 *
 * - READ_BYTES bytes at UPAGE must be read from FILE
 * starting at offset OFS.
 *
 * - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
 *
 * The pages initialized by this function must be writable by the
 * user process if WRITABLE is true, read-only otherwise.
 *
 * Return true if successful, false if a memory allocation error
 * or disk read error occurs. */
static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
             uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
    ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(upage) == 0);
    ASSERT(ofs % PGSIZE == 0);

    file_seek(file, ofs);
    while (read_bytes &gt; 0 || zero_bytes &gt; 0)
    {
        /* Do calculate how to fill this page.
         * We will read PAGE_READ_BYTES bytes from FILE
         * and zero the final PAGE_ZERO_BYTES bytes. */
        size_t page_read_bytes = read_bytes &lt; PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

        /* Get a page of memory. */
        uint8_t *kpage = palloc_get_page(PAL_USER);
        if (kpage == NULL)
            return false;

        /* Load this page. */
        if (file_read(file, kpage, page_read_bytes) != (int)page_read_bytes)
        {
            palloc_free_page(kpage);
            return false;
        }
        memset(kpage + page_read_bytes, 0, page_zero_bytes);

        /* Add the page to the process&#39;s address space. */
        if (!install_page(upage, kpage, writable))
        {
            printf(&quot;fail\n&quot;);
            palloc_free_page(kpage);
            return false;
        }

        /* Advance. */
        read_bytes -= page_read_bytes;
        zero_bytes -= page_zero_bytes;
        upage += PGSIZE;
    }
    return true;
}

/* Create a minimal stack by mapping a zeroed page at the USER_STACK */
static bool
setup_stack(struct intr_frame *if_)
{
    uint8_t *kpage;
    bool success = false;

    kpage = palloc_get_page(PAL_USER | PAL_ZERO);
    if (kpage != NULL)
    {
        success = install_page(((uint8_t *)USER_STACK) - PGSIZE, kpage, true);
        if (success)
            if_-&gt;rsp = USER_STACK;
        else
            palloc_free_page(kpage);
    }
    return success;
}

/* Adds a mapping from user virtual address UPAGE to kernel
 * virtual address KPAGE to the page table.
 * If WRITABLE is true, the user process may modify the page;
 * otherwise, it is read-only.
 * UPAGE must not already be mapped.
 * KPAGE should probably be a page obtained from the user pool
 * with palloc_get_page().
 * Returns true on success, false if UPAGE is already mapped or
 * if memory allocation fails. */
static bool
install_page(void *upage, void *kpage, bool writable)
{
    struct thread *t = thread_current();

    /* Verify that there&#39;s not already a page at that virtual
     * address, then map our page there. */
    return (pml4_get_page(t-&gt;pml4, upage) == NULL &amp;&amp; pml4_set_page(t-&gt;pml4, upage, kpage, writable));
}

/* --------------project 2 : arguemnt passing------------ */
/*
| Address      | Name         | Data        | Type        |
| -------------|--------------|-------------|-------------|
| 0x4747fffc   | argv[3][...] | &#39;bar\0&#39;     | char[4]     |
| 0x4747fff8   | argv[2][...] | &#39;foo\0&#39;     | char[4]     |
| 0x4747fff5   | argv[1][...] | &#39;-l\0&#39;      | char[3]     |
| 0x4747ffed   | argv[0][...] | &#39;/bin/ls\0&#39; | char[8]     |
| 0x4747ffe8   | word-align   | 0           | uint8_t[]   |
| 0x4747ffe0   | argv[4]      | 0           | char *      |
| 0x4747ffd8   | argv[3]      | 0x4747fffc  | char *      |
| 0x4747ffd0   | argv[2]      | 0x4747fff8  | char *      |
| 0x4747ffc8   | argv[1]      | 0x4747fff5  | char *      |
| 0x4747ffc0   | argv[0]      | 0x4747ffed  | char *      |
| 0x4747ffb8   | return addr  | 0           | void (*)()  |

&lt;argument stack downwards using rsp stack pointer&gt;
if_-&gt;rsp:   struct intr_frame 구조체의 rsp 멤버 변수는 스택 포인터
if_-&gt;R:     struct intr_frame 구조체의 R 멤버 변수는 레지스터의 값
if_-&gt;R.rdi: R.rdi는 함수 호출 시 첫 번째 인수를 전달하는 레지스터
if_-&gt;R.rsi: R.rsi는 함수 호출 시 두 번째 인수를 전달하는 레지스터
*/
void argument_stack(char **argv, int argc, struct intr_frame *if_)
{
    /* Copy the argument string to the stack. */
    for (int i = argc - 1; i &gt;= 0; i--) // Minus i to store the elements from the end of the array.
    {
        int N = strlen(argv[i]) + 1;  // Plus 1 to consider sentinel(`\0`) which need to be located at the end of string.
        if_-&gt;rsp -= N;                  // Decrement the stack pointer to make space for the argument string.
        memcpy(if_-&gt;rsp, argv[i], N); // Copy the argument string to the stack.
        argv[i] = (char *)if_-&gt;rsp;      // Update the argument pointer to point to the copied string on the stack.
    }

    /* word alignment */
    if (if_-&gt;rsp % 8)
    {
        int padding = if_-&gt;rsp % 8;
        if_-&gt;rsp -= padding;          // Align the stack pointer to an 8-byte boundary if needed.
        memset(if_-&gt;rsp, 0, padding); // Fill the padding bytes with zeros.
    }

    /* for NULL sentinel of argv array */
    if_-&gt;rsp -= 8;
    memset(if_-&gt;rsp, 0, 8); // Place a NULL sentinel at the bottom of the stack.

    /* Copy the argument pointer to the stack */
    for (int i = argc - 1; i &gt;= 0; i--)
    {
        if_-&gt;rsp -= 8;                   // Decrement the stack pointer to make space for the argument pointer.
        memcpy(if_-&gt;rsp, &amp;argv[i], 8); // Copy the argument pointer to the stack.
    }

    /* fake return address */
    if_-&gt;rsp -= 8;
    memset(if_-&gt;rsp, 0, 8); // Place a fake return address (0) at the bottom of the stack.

    if_-&gt;R.rdi = argc;                   // Set rdi (the first function parameter) to argc.
    if_-&gt;R.rsi = (char *)if_-&gt;rsp + 8; // Set rsi (the second function parameter) to &amp;argv[0] which is the stack pointer&#39;s next address.
}
/* ------------------------------------------------------ */

#else
/* From here, codes will be used after project 3.
 * If you want to implement the function for only project 2, implement it on the
 * upper block. */

static bool
lazy_load_segment(struct page *page, void *aux)
{
    /* TODO: Load the segment from the file */
    /* TODO: This called when the first page fault occurs on address VA. */
    /* TODO: VA is available when calling this function. */
}

/* Loads a segment starting at offset OFS in FILE at address
 * UPAGE.  In total, READ_BYTES + ZERO_BYTES bytes of virtual
 * memory are initialized, as follows:
 *
 * - READ_BYTES bytes at UPAGE must be read from FILE
 * starting at offset OFS.
 *
 * - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
 *
 * The pages initialized by this function must be writable by the
 * user process if WRITABLE is true, read-only otherwise.
 *
 * Return true if successful, false if a memory allocation error
 * or disk read error occurs. */
static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
             uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
    ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(upage) == 0);
    ASSERT(ofs % PGSIZE == 0);

    while (read_bytes &gt; 0 || zero_bytes &gt; 0)
    {
        /* Do calculate how to fill this page.
         * We will read PAGE_READ_BYTES bytes from FILE
         * and zero the final PAGE_ZERO_BYTES bytes. */
        size_t page_read_bytes = read_bytes &lt; PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

        /* TODO: Set up aux to pass information to the lazy_load_segment. */
        void *aux = NULL;
        if (!vm_alloc_page_with_initializer(VM_ANON, upage,
                                            writable, lazy_load_segment, aux))
            return false;

        /* Advance. */
        read_bytes -= page_read_bytes;
        zero_bytes -= page_zero_bytes;
        upage += PGSIZE;
    }
    return true;
}

/* Create a PAGE of stack at the USER_STACK. Return true on success. */
static bool
setup_stack(struct intr_frame *if_)
{
    bool success = false;
    void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);

    /* TODO: Map the stack on stack_bottom and claim the page immediately.
     * TODO: If success, set the rsp accordingly.
     * TODO: You should mark the page is stack. */
    /* TODO: Your code goes here */

#     return success;
}
#endif /* VM */

</code></pre><h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/d2716895-b307-4f7b-b0c5-86229ae58238/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1211-os특강(권영진)]]></title>
            <link>https://velog.io/@gigis-note/1211-os%ED%8A%B9%EA%B0%95%EA%B6%8C%EC%98%81%EC%A7%84</link>
            <guid>https://velog.io/@gigis-note/1211-os%ED%8A%B9%EA%B0%95%EA%B6%8C%EC%98%81%EC%A7%84</guid>
            <pubDate>Mon, 11 Dec 2023 04:46:32 GMT</pubDate>
            <description><![CDATA[<ol>
<li>지역성 </li>
<li>Demand paging</li>
</ol>
<p>os 왜필요하냐</p>
<ol>
<li>API(read,write)를 제공함으로써 하드웨어를 사용할 수 있게 함</li>
<li>하드웨어의 추상화</li>
</ol>
<p>운영체제를 이해할 땐 탑다운을 이해해야함
추상화를 왜 이렇게 했는지를 이야기하기 위해 탑다운으로 이야기해야한다. -&gt; 메타 놀리지 왓</p>
<p>추상화란?</p>
<ol>
<li>불필요한 것을 없엔다 -&gt; 쉬운 이해(easy to use)</li>
</ol>
<p>making sth easier to understand by ignoring some of details</p>
<blockquote>
<p>what is the most important idea in terms of software architecture? 
layers of abstraction! but the John Ousterhout says plus, problem decomposition(DP)</p>
</blockquote>
<p>처음 os디자인의 생각
1)쉬워야하고 2)하드웨어가 관리되어야하고 3)보호되어야한다
-&gt; 프로세스: 하나의 컴퓨터처럼 보이게. 즉 프로세스란 하나의 컴퓨터를 추상화 한 것.</p>
<p>프로세스
CPU - virtualizing CPU
Memeory - Virtual address space
Storage - </p>
<p>이렇게 추상화해서 각 애플리케이션이 개별적인 컴퓨터를 사용하는것처럼 만들기 위해서는 &quot;프로세스&quot;</p>
<blockquote>
<p>Knowlege for you guys
전역변수는 데이터영역
지역변수는 힙영역
char *p에서 p변수 자체는 stack영역에
p가 가르키는 주소는 어디에 존재? heap영역에</p>
</blockquote>
<p>앱스트랙션: 각각의 컴포넌트를 디컴포즈</p>
<h2 id="물리메모리">물리메모리</h2>
<h3 id="주소-추상화">주소 추상화</h3>
<p>각 물리주소를 페이지 단위(4kb)로 쪼개고 가상주소로 맵핑해주는 것</p>
<p>어떻게 하느냐?</p>
<ul>
<li>세그멘티이션</li>
<li>페이징</li>
<li>세그멘티드 페이징</li>
</ul>
<p>그렇지만 얘네들은 어디까지 주소를 추상화하기 위한 툴일 뿐
얘네들은 MMU안에 구현이 되어있음. 왜그랬을까? 너무빨라서 하드웨어에게 부탁. &lt;- 이해안감</p>
<p>MMU가 접근이 가능한지 안한지 판단한다. 어떤것을 기준으로? protection. 이 정보는 어디에 써있냐? 페이지테이블을 보고 판단. 
허용하면 안되는 곳을 접근하면 page fault handler가 page fault를 냄
세그멘테이션 폴트는 왜 난다는지 못들음</p>
<p>page table 디테일을 찾아보라 멀테레벨, 싱글 어쩌고 저쩌고 장단점 찾아보라
<img src="https://velog.velcdn.com/images/gigis-note/post/f3c6b339-8f8e-44eb-848b-1f09b059a783/image.png" alt=""></p>
<p>TMB라는 하드웨어 캐싱같이 도와주는 애인데 찾아봐라</p>
<p>페이지 테이블이 어디에 저장되어있냐-&gt;메모리</p>
<p>페이지 테이블 값 세팅 누가하냐 -&gt; 커널
운영체제가 세팅해준걸 누가 사용하냐 -&gt; 하드웨어</p>
<p>물리주소를 언제 할당해주냐 -&gt; 사용할 때
그럼 운영체제는 사용하는 시점을 알아야하는데 어떻게 아냐 즉 디멘드 페이징을 어떻게 하느냐? 
Application first accesses unallocated physical memory
<img src="https://velog.velcdn.com/images/gigis-note/post/0f77367a-1fba-4ebf-b6ed-0bdf35cb824d/image.png" alt="">
이렇게 도는동안 어플리케이션이 멈춘다. 그래서 옵티마이즈가 중요한데 이중에서 Zero the page가 제일 오래걸린다. 왜 지워야하냐? 보안 문제때문에. protection! </p>
<h2 id="가상메모리">가상메모리</h2>
<h3 id="다시-페이지-폴트-핸들링">다시 페이지 폴트 핸들링</h3>
<p>after fault : 메모리에 없어서 디스크까지 다녀오는거 file backed memeory </p>
<p>VA -&gt; Function -&gt; PM</p>
<p>Two types of memeory </p>
<p>1) file backed memeory
2) anonymous memeory</p>
<ul>
<li>Swap disk, etc</li>
</ul>
<p>DMA는 IO시간을 옵티마이즈 하기 위해서 함. 
Read가 끝났어요 라고 알려주는 메커니즘을 뭐라할까요? 인터럽트~</p>
<h2 id="storage-abstractionfile-system">Storage Abstraction(File system)</h2>
<p>file system 은 memeory system 과 공통점과 차이점이 있다.
File : a logical unit of storage</p>
<p>file도 VA -&gt; Function -&gt; PM 구조와 비슷함
It&#39;s called <code>Level of indirection</code>
물리주소에서는 실제 공간의 위치와 상관이 없음. 로지컬만 붙어있으면 됨
물리주소 어디에 맵핑할지 어떻게 정하냐? 데이터의 file과 offset을 가지고 </p>
<p>HOW
Indexed allocation, FAT, BTE, Extent tree
allocation 하면 Pysical block을 리턴한다.
MMU는 HW에서 하는데, File system은 인덱싱을 SW에서 한다. 왜? 느려도 되서 &lt;- 이해안감</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/42614300-769a-4ac2-bfa2-3a0ba0665e61/image.png" alt=""></p>
<p>Metadata of a file 을 inode라고 함</p>
<p>인터널 노드는 어디에 저장되어있나? 메모리? 스토리지? 아니 둘다!</p>
<p>하드웨어가 도와줄 필요가 있나? 아니 느려서 ㄱㅊ</p>
<p>얼로케이트 언제해주냐? 사용할때! (페이지폴트같은 메커니즘은 필요없다) -&gt; <code>fsync</code> 파일 시스템만이 가지고 있는 독특한 시스템콜이 불릴때 얼로케이션이 일어남.</p>
<p>데이터를 디램에 놓고 디램에서 꺼내 쓴다. 그렇게 하기 위해 있는것이 <code>버퍼 캐시</code> or <code>페이지 캐시</code> </p>
<p>또하나의 특징이 시스템 콜 인터페이스와 캐시 사이에 추상화된 VFS가 있음
<img src="https://velog.velcdn.com/images/gigis-note/post/4f83844a-2cf2-4cf2-970d-72a1fbd89a9e/image.png" alt=""></p>
<p>classic consistency 가 뭔지 못들음
이걸 잘 하지 못하면 
<img src="https://velog.velcdn.com/images/gigis-note/post/2061e522-45a8-4028-b35a-93ac5f619d59/image.png" alt="">
이케됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1204-TIL(keywords:usermode vs kernel mode, user stack, system call .etc)]]></title>
            <link>https://velog.io/@gigis-note/1204-TILkeywordsusermode-vs-kernel-mode-user-stack-system-call-.etc</link>
            <guid>https://velog.io/@gigis-note/1204-TILkeywordsusermode-vs-kernel-mode-user-stack-system-call-.etc</guid>
            <pubDate>Tue, 05 Dec 2023 05:43:30 GMT</pubDate>
            <description><![CDATA[<h1 id="공부-키워드">공부 키워드</h1>
<h2 id="user-mode-vs-kernel-mode">User mode vs Kernel mode</h2>
<h3 id="user-mode">User mode</h3>
<ul>
<li><strong>한정된 권한</strong>: 보안과 안정성을 위해 유저 모드에서는 실행 중인 프로그램이 시스템 리소스에 직접 접근할 수 있는 권한이 제한되어 있다.</li>
<li><strong>응용 프로그램 실행</strong>: 주로 응용프로그램은 응용 프로그램에서 실행된다. 사용자가 작성한 프로그램도 유저모드에서 동작한다.</li>
<li><strong>시스템 콜 호출</strong>: 유저 모드에서 커널모드로의 전환은 exception(인터럽트, 폴트, 트랩핑 시스템 콜과 같은)을 통해 이루어진다. 사용자 프로그램이 커널의 서비스를 필요로 할 때, 시스템콜을 호출하여 커널모드로 전환하고 필요한 작업을 커널에서 수행하고 다시 유저모드로 돌아온다.<blockquote>
<p>이 부분에 대한 책에 나와있는 내용은 이렇다. 
<em>Exception이 발생하고 control이 exception handler한테 전달될 때, 프로세서는 유저모드에서 커널모드로 바꾼다. 그리고 나서 어플리케이션 코드로 돌아왔을 때 프로세서는 모드를 커널모드에서 다시 유저모드로 변경한다.(csapp p.761)</em> </p>
</blockquote>
</li>
</ul>
<h3 id="kernel-mode">Kernel Mode</h3>
<ul>
<li><strong>전체 권한</strong>: 커널 모드에서 실행중인 코드는 시스템의 모든 자원에 대한 완전한 권한을 갖는다.</li>
<li><strong>운영 체제 수행</strong>: 운영체제의 핵심 부분인 <code>커널</code>은 커널모드에서 동작한다. 커널은 하드웨어 리소스를 관리하고 다양한 프로세스 간의 스케줄링 및 통신을 담당한다.<blockquote>
<p>이 부분에 대한 책에 나와있는 내용은 이렇다. 
<em>커널은 각 프로세스마다 <code>context</code>를 유지한다. <code>context</code>는 선점된 프로세스를 다시 시작하기 위해 커널이 필요로 하는 상태이다. 이 <code>context</code>는 아래와 같은 값의 오브젝트로 구성되어 있다.
general-purpose registers, the floating-point registers, the
program counter, user’s stack, status registers, kernel’s stack, and various kernel
data structures such as a page table and a file table(csapp p.762)</em></p>
</blockquote>
</li>
<li><strong>인터럽트 및 예외 처리</strong>:  커널 모드에서는 하드웨어 인터럽트 및 예외를 처리할 수 있다. 이는 운영 체제가 하드웨어 이벤트에 반응하고 시스템을 안정적으로 유지하기 위해 필요한 기능이다.</li>
</ul>
<blockquote>
<p>🙋🏻quick question: 커널이 실행되고 있는 동안 문맥교환이 발생할 수 있을까? 
정답은 &quot;있다&quot;
-&gt; 커널은 유저를 대신해서 시스템콜을 실행해서 context switch를 일어나게 할 수 있다. 대표적으로 <code>sleep</code> 시스템콜이 있다. </p>
</blockquote>
<p>시스템은 일반적으로 주기적인 타이머 인터럽트를 생성하는 메커니즘을 가지고 있고 이 인터럽트는 일반적으로 1ms 또는 10ms마다 발생한다. 타이머 인터럽트가 발생할 때마다 커널은 현재 프로세스가 충분히 실행되었다고 판단하고 새로운 프로세스로 전환할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/f43fb909-7777-49f9-a964-b82584151b60/image.png" alt=""></p>
<h2 id="register-vs-memory">Register vs Memory</h2>
<p><img src="https://velog.velcdn.com/images/gigis-note/post/08bd20a3-3ada-4449-bd13-e92e1ddf9635/image.png" alt=""></p>
<h3 id="공통점">공통점</h3>
<ul>
<li><p><strong>데이터 저장</strong>: Register와 Memory는 모두 데이터를 저장하는 용도</p>
</li>
<li><p><strong>CPU와 상호작용</strong>: CPU와 직접적으로 상호 작용하여 데이터 처리 및 프로그램 실행에 기여함</p>
</li>
<li><p><strong>핵심적 하드웨어 구성 요소</strong>: 둘 다 컴퓨터 시스템의 핵심 하드웨어 구성 요소임</p>
<h3 id="차이점">차이점</h3>
</li>
<li><p><strong>저장 용량과 속도</strong>: Register와 Memory는 모두 데이터를 저장하는 용도</p>
</li>
<li><p><strong>용도</strong>: register는 주로 CPU의 연산 및 제어 목적으로 사용되고, Memory는 데이터와 프로그램 코드를 저장하는데 사용됨. (메모리는 주 기억장치(메인메모리)와 보조 기억장치로 나뉨)</p>
<blockquote>
<ul>
<li>주 기억장치 (Main Memory 또는 RAM):
<em>주로 현재 실행 중인 프로그램 및 데이터를 임시로 저장하는 용도로 사용됩니다.
CPU가 직접 액세스할 수 있는 공간이며, 빠른 속도로 데이터에 접근할 수 있습니다. 전원이  꺼지면 저장된 데이터가 소멸된다.</em></li>
</ul>
</blockquote>
<ul>
<li>보조 기억장치 (Secondary Storage):
<em>데이터의 영구 저장을 담당하며, 주로 하드 디스크, SSD, 광 디스크 등이 포함됩니다.
주로 대용량 데이터와 프로그램이 저장되어, 전원이 꺼져도 데이터가 보존됩니다.</em></li>
</ul>
</li>
<li><p><strong>접근방식</strong>: Register는 직접적인 연산 및 명령어 수행에 사용되므로 CPU 내부에서 직접적으로 접근된다. Memory는 CPU가 주소를 통해 간접적으로 access한다. </p>
</li>
</ul>
<h2 id="user-stack">User Stack</h2>
<p>힙과 마찬가지로 유저 스택은 프로그램 실행 중에 동적으로 확장 및 축소된다. 함수를 호출할 때마다 스택이 확장되고, 함수에서 반환할 때마다 축소된다.
유저스택은 가장 큰 유효한 사용자 주소(2^48-1) 아래에서 시작해서 작은 메모리 주소 방향으로 즉, 아래로 자란다.
<img src="https://velog.velcdn.com/images/gigis-note/post/fa5011d9-9180-4d3c-9189-5d5b8a1f9602/image.png" alt=""></p>
<p>여기서 스택을 break down 해보면 아래와 같다.
<img src="https://velog.velcdn.com/images/gigis-note/post/de277193-31c1-44c6-8b17-af34e741d860/image.png" alt="">
(스택의 가장 밑바닥부터 - 윗윗 그림에서는 맨위, 바로 위 그림에서는 맨 아래칸부터)</p>
<ul>
<li><code>스택 프레임</code> 스택의 맨 밑에는 system start-up function, <code>libc_start_main</code>이라는 스택프레임이 있다.</li>
<li><code>argv</code> - argv[0], ... argv[argc-1], argv[argc] = NULL<ul>
<li><code>argv</code> 라는 포인터 변수는 각각 인자 문자열을 가리키는 포인터로 이루어진 null로 끝나는 배열을 가르키고 있다.</li>
<li>유저-레벨 어플리케이션은 <code>%rdi</code>, <code>%rsi</code>, <code>%rdx</code>, <code>%rcx</code>, <code>%r8</code>, <code>%r9</code> 시퀀스들을 전달하기 위해 정수 레지스터를 사용합니다.(리턴값이 있다면 %rax에 저장됨)</li>
<li><code>%rsi</code>를 argv 주소(argv[0]의 주소)를 가리키게, <code>%rdi</code> 를 argc 로 설정합니다.</li>
</ul>
</li>
<li><code>envp</code> - envp[0], ... envp[n-1], envp[n] == NULL<ul>
<li><code>envp</code> 포인터 변수는 null로 끝나는 포인터 배열을 가르키며 각 포인터는 환경변수 문자열을 가르키고 있다. (각 문자열은 name=value 형식의 name-value의 쌍으로 구성된다)</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong><code>argc</code>와 <code>argv</code>의 차이</strong></p>
</blockquote>
<ol>
<li><strong><code>argc</code> (Argument Count):</strong><ul>
<li><code>argc</code>는 명령행 인자의 개수를 나타냅니다.</li>
<li>프로그램을 실행할 때 명령행에 전달되는 인자의 총 개수가 여기에 저장됩니다.</li>
<li>프로그램 실행 시 최소한 1이며, 프로그램 이름 자체도 하나의 인자로 간주됩니다.</li>
</ul>
</li>
<li><strong><code>argv</code> (Argument Vector):</strong><ul>
<li><code>argv</code>는 명령행 인자의 배열이며, 각 요소는 문자열로서 하나의 명령행 인자를 나타냅니다.</li>
<li><code>argv[0]</code>은 프로그램의 이름을 가리킵니다.</li>
<li><code>argv[1]</code>, <code>argv[2]</code>, ... 등은 추가적인 명령행 인자를 가리킵니다.</li>
</ul>
</li>
</ol>
<ul>
<li><code>Null-terminated environment variable strings</code>
(null-terminated array of pointers, which points to an envrionment variable string on the stack. <code>environ</code>이라는 전역변수가 해당 포인터들의 첫번째 요소를 가르킨다(envp[0])).</li>
<li><code>Null-terminated command-line arg strings</code></li>
</ul>
<h2 id="system-call">System Call</h2>
<p>csapp p.756</p>
<p>exception 두가지</p>
<ol>
<li>external device hw exception=interrupt</li>
<li>user로 부터의 sw exception=fault, trap, abort</li>
</ol>
<p><a href="https://casys-kaist.github.io/pintos-kaist/project2/system_call.html">git book</a></p>
<h2 id="file-descriptor">File Descriptor</h2>
<h3 id="개념-복습">개념 복습</h3>
<p>파일 디스크립터 in terms of File I/O : 파일을 열거나 읽거나 쓸 때 시스템은 각 파일에 대해 고유한 파일 디스크립터를 할당한다.</p>
<p>이 file descriptor는 파일에 대한 이후 모든 작업에서 사용됨</p>
<p>커널은 열린 파일에 대한 모든 정보를 추적. 애플리케이션은 디스크립터만 추적.</p>
<p>리눅스 쉘에 의해 생성된 프로세스는 세 개의 열린 파일을 가진다.</p>
<ol>
<li>표준 입력 - descriptor 0</li>
<li>표준 출력 - descriptor 1</li>
<li>표준 에러 - descriptor 2</li>
</ol>
<p><img src="blob:https://velog.io/6076dfa8-0a3b-4595-b07b-2aa1e411e1c2" alt="업로드중.."></p>
<p>open 함수는 파일 이름을 파일 디스크립터로 변환하고 해당 디스크립터 번호를 반환합니다. 반환된 디스크립터는 항상 프로세스에서 현재 열려 있지 않은 가장 작은 디스크립터입니다. flags 인자는 프로세스가 파일에 접근할 의도를 나타냅니다.</p>
<h2 id="cache">Cache</h2>
<h3 id="1-캐시의-동작-원리">1. 캐시의 동작 원리:</h3>
<p><strong>Locality (지역성):</strong> 프로그램이 특정 데이터나 명령어에 접근할 때, 이와 주변에 있는 데이터에 대한 접근이 높은 확률이 있습니다. 캐시는 이러한 지역성을 활용하여 주로 사용되는 데이터를 저장합니다.</p>
<p><strong>캐시 블록과 세트:</strong> 캐시는 일정 크기의 블록으로 데이터를 저장하며, 이러한 블록들을 셋(set)으로 구성합니다. 블록은 일반적으로 메모리에서 연속된 주소 공간을 차지하며, 각 블록은 캐시의 한 셋에 속합니다.</p>
<p><strong>캐시 태그:</strong> 캐시에 저장된 데이터가 메모리에서 어디에 위치하는지를 나타내기 위해 태그(tag)라는 정보를 사용합니다. 캐시에 저장된 데이터에 접근할 때, 태그와 비교하여 원하는 데이터를 찾습니다.</p>
<p><strong>캐시 미스(Cache Miss)와 비용:</strong> 캐시에 데이터가 없어서 메모리에서 가져와야 할 때, 이를 캐시 미스라고 합니다. 캐시 미스는 비용이 높은 작업으로 간주되며, 메모리로부터 데이터를 읽어와 캐시에 적재합니다.</p>
<h3 id="2-캐시-메모리의-설계와-최적화">2. 캐시 메모리의 설계와 최적화:</h3>
<p>적절한 크기와 조직: 캐시의 크기와 조직은 성능에 큰 영향을 미칩니다. 적절한 캐시 크기와 구조를 선택하여 데이터 지역성을 잘 활용하는 것이 중요합니다.</p>
<p>캐시 지역성 활용: 시간 지역성(특정 데이터가 재사용되는 경향)과 공간 지역성(한 번에 여러 데이터가 사용되는 경향)을 활용하여 캐시의 효율을 높일 수 있습니다.</p>
<h3 id="3-캐시-미스cache-miss의-비용">3. 캐시 미스(Cache Miss)의 비용:</h3>
<p><strong>Cold Miss:</strong> 처음 발생하는 캐시 미스. 초기화되지 않은 데이터에 접근할 때 발생하며, 초기 비용이 높을 수 있습니다.</p>
<p><strong>Conflict Miss:</strong> 캐시의 한 셋에 여러 데이터가 들어가 있는 경우 발생하는 미스. 캐시의 셋 수를 늘리거나 다양한 방법으로 충돌을 해결해야 합니다.</p>
<p><strong>Capacity Miss:</strong> 캐시가 꽉 차서 새로운 데이터를 적재할 수 없는 경우 발생하는 미스. 캐시 크기를 늘리거나 교체 알고리즘을 최적화하여 해결할 수 있습니다.</p>
<h3 id="4-캐시-지역성">4. 캐시 지역성:</h3>
<p><strong>시간 지역성(Time Locality):</strong> 특정 데이터에 한 번 접근하면 가까운 시간 내에 다시 접근할 확률이 높음.</p>
<p><strong>공간 지역성(Spatial Locality):</strong> 특정 데이터에 접근하면 그 주변의 데이터에도 접근할 확률이 높음.</p>
<h2 id="atomic-operation">Atomic Operation</h2>
<h3 id="개념-복습-1">개념 복습</h3>
<p>원자적 연산은 다른 스레드나 프로세스의 동시적인 접근에 대해 불변성을 보장하는 연산입니다. 이는 연산이 전체적으로 완전히 실행되거나 전혀 실행되지 않는 것을 의미합니다. 즉, 다른 연산이 중간에 끼어들거나 일부만 실행되지 않습니다.</p>
<p>원자적 연산은 다중 스레드 환경에서 공유 자원에 안전하게 접근하기 위해 사용됩니다. 여러 스레드가 동시에 같은 자원에 접근하는 경우, 원자적 연산을 사용하면 경쟁 조건(Race Condition)과 같은 문제를 방지할 수 있습니다.</p>
<h3 id="pintos-project-에서의-원자적-연산">pintos project 에서의 원자적 연산</h3>
<p><strong>인터럽트 비활성화 및 활성화:</strong>
intr_disable() 및 intr_enable() 함수를 사용하여 인터럽트를 비활성화하고 활성화합니다. 이러한 함수는 인터럽트를 원자적으로 다루어 공유 자원에 대한 안전한 접근을 보장합니다.</p>
<p><strong>임계 영역 진입 및 이탈:</strong>
lock_acquire() 및 lock_release() 함수를 사용하여 임계 영역에 진입하고 이를 이탈합니다. 이 함수들은 락(lock)을 원자적으로 획득하고 해제하여 여러 스레드 간의 동기화를 제어합니다.</p>
<p><strong>Condition Variable 조작:</strong>
cond_wait(), cond_signal(), cond_broadcast() 함수를 사용하여 condition variable을 조작합니다. 이러한 함수들은 스레드 간의 신호 전달 및 대기를 원자적으로 처리합니다.</p>
<p><strong>세마포어 조작:</strong>
sema_down() 및 sema_up() 함수를 사용하여 세마포어를 조작합니다. 세마포어는 스레드 간의 상호 배제와 동기화를 위해 사용되며, 이러한 함수들은 세마포어를 원자적으로 다룹니다.</p>
<p><strong>페이지 테이블 조작:</strong>
페이지 테이블에 접근할 때 원자적으로 조작해야 합니다. 페이지 테이블 업데이트와 관련된 연산들은 스레드 간의 메모리 관리에서 중요한 부분이기 때문입니다.</p>
<p><strong>파일 디스크립터 관리:</strong>
파일 디스크립터를 할당하거나 해제할 때 원자적으로 조작해야 합니다. 파일 디스크립터를 관리하는 동안 스레드 간의 충돌을 피하기 위해 특히 주의해야 합니다.</p>
<h2 id="rax-register">rax register</h2>
<p><code>rax</code> 레지스터는 x86 아키텍처에서 사용되는 64비트 레지스터 중 하나입니다. 이 레지스터는 다양한 목적으로 사용되며, 그 중 일부는 다음과 같습니다:</p>
<ol>
<li><p><strong>함수 반환 값</strong>: 함수가 값을 반환할 때, 보통 <code>rax</code> 레지스터에 반환값이 저장됩니다. 이 규칙은 x86_64 아키텍처에서 cdecl 호출 규약을 따르는 경우에 해당합니다.</p>
</li>
<li><p><strong>시스템 호출 인터페이스 (System Call Interface)</strong>: x86_64 리눅스에서는 시스템 호출 시 <code>rax</code> 레지스터에 시스템 호출 번호가 저장됩니다. 시스템 호출의 종류를 나타내는 중요한 정보를 포함합니다.</p>
</li>
<li><p><strong>산술 연산</strong>: <code>rax</code> 레지스터는 일반적인 산술 연산에 사용됩니다. 산술 연산 결과가 여기에 저장됩니다.</p>
</li>
<li><p><strong>임시 데이터 저장</strong>: 함수나 루틴에서 임시로 데이터를 저장하는 데 사용될 수 있습니다.</p>
</li>
<li><p><strong>부울 플래그 반환</strong>: 어셈블리 레벨에서는 일부 명령이 실행 후 상태에 따라 <code>rax</code> 레지스터에 부울 플래그 값을 저장할 수 있습니다.</p>
</li>
<li><p><strong>메모리 주소 저장</strong>: 포인터나 메모리 주소를 저장하는 데도 사용될 수 있습니다.</p>
</li>
</ol>
<p>이외에도 다양한 용도로 <code>rax</code> 레지스터가 활용될 수 있습니다. 프로그램이나 컨텍스트에 따라서 그 목적이 달라집니다.</p>
<h2 id="32-bit-os-vs-64-bit-os">32 bit OS vs 64 bit OS</h2>
<table>
<thead>
<tr>
<th>특성</th>
<th>32비트 운영 체제</th>
<th>64비트 운영 체제</th>
</tr>
</thead>
<tbody><tr>
<td>주소 공간 (가상 메모리)</td>
<td>4GB (2^32 바이트)</td>
<td>18.4 million TB (2^64 바이트)</td>
</tr>
<tr>
<td>레지스터 크기</td>
<td>32비트</td>
<td>64비트</td>
</tr>
<tr>
<td>주소 버스 크기</td>
<td>32비트</td>
<td>64비트</td>
</tr>
<tr>
<td>물리 메모리 크기</td>
<td>4GB</td>
<td>16EB (Exabyte, 2^60 바이트)</td>
</tr>
<tr>
<td>포인터 크기</td>
<td>32비트</td>
<td>64비트</td>
</tr>
<tr>
<td>레지스터의 갯수</td>
<td>적은 수</td>
<td>더 많은 수</td>
</tr>
<tr>
<td>성능 향상</td>
<td>64비트 아키텍처에 최적화</td>
<td>더 큰 레지스터, 물리 메모리 지원</td>
</tr>
<tr>
<td>소프트웨어 호환성</td>
<td>32비트 소프트웨어 지원</td>
<td>32비트 및 64비트 소프트웨어 모두 지원</td>
</tr>
<tr>
<td>운영 체제 및 응용 프로그램</td>
<td>일반적으로 32비트</td>
<td>64비트로 제작된 운영 체제 및 응용 프로그램</td>
</tr>
</tbody></table>
]]></description>
        </item>
    </channel>
</rss>