<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jjin.log</title>
        <link>https://velog.io/</link>
        <description>문과생 컴공초보👩🏻‍💻</description>
        <lastBuildDate>Wed, 26 May 2021 01:51:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jjin.log</title>
            <url>https://images.velog.io/images/jiina_28/profile/e16fde44-d9d7-4972-8787-b74be4d2703f/KakaoTalk_Photo_2021-01-22-11-20-44.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jjin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jiina_28" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[운영체제_POSIX Semaphore functions]]></title>
            <link>https://velog.io/@jiina_28/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9CPOSIX-Semaphore-functions</link>
            <guid>https://velog.io/@jiina_28/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9CPOSIX-Semaphore-functions</guid>
            <pubDate>Wed, 26 May 2021 01:51:02 GMT</pubDate>
            <description><![CDATA[<h4 id="⬇️헤더파일⬇️">⬇️헤더파일⬇️</h4>
<pre><code class="language-c">#include &lt;semaphore.h&gt;</code></pre>
<h3 id="sem_init">sem_init()</h3>
<ul>
<li>익명 세마포어 만듦<pre><code class="language-c">int sem_init(sem_t *sem, int pshared, unsigned int value);
</code></pre>
</li>
</ul>
<p>sem_init(&amp;sem_name, 0, 1);</p>
<pre><code>- **sem** : 초기화할 세마포어 객체
- **pshared** : 0이 아니면 프로세스들 간에 세마포어 공유, 0이면 프로세스 내부에서만 사용
- **value** : 세마포어 초기값

### sem_wait()
- 세마포어를 얻을 때까지 기다림 (잠금)
- 만약 세마포어 값이 0보다 크면 프로세스는 세마포어를 얻고 세마포어 감소 후 즉시 반환
- 세마포어 값이 0이면 세마포어가 0보다 커지거나 시그널이 발생할 때까지 대기
```c
sem_wait(&amp;sem_name);</code></pre><h3 id="sem_post">sem_post()</h3>
<ul>
<li>세마포어 되돌려주며 세마포어 값이 하나 증가함 (잠금 해제)<pre><code class="language-c">sem_post(&amp;sem_name);</code></pre>
</li>
</ul>
<h3 id="sem_getvalue">sem_getvalue()</h3>
<ul>
<li>세마포어의 정보를 가져오며 현재 세마포어의 값을 sval로 넘겨 알려줌<pre><code class="language-c">int sem_getvalue(sem_t *sem, int *sval);
</code></pre>
</li>
</ul>
<p>sem_getvalue(&amp;sem_name, &amp;i);</p>
<pre><code>
### sem_destroy()
- 익명 세마포어 파기
```c
sem_destroy(&amp;sem_name);</code></pre><hr>
<h3 id="💁🏻♀️mutex-와-semaphore의-차이는">💁🏻‍♀️Mutex 와 semaphore의 차이는?</h3>
<p>1) - <strong>세마포어</strong>는 공유 자원에 세마포어의 변수만큼의 프로세스(또는 스레드)가 접근 가능
    - <strong>뮤텍스</strong>는 단 1개의 프로세스(또는 스레드)만 접근 가능
2) - 현재 수행중이 아닌 다른 프로세스가 <strong>세마포어</strong> 해제 가능
    - <strong>뮤텍스</strong>는 lock을 획득한 프로세스가 반드시 그 lock을 해제해야함 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제_POSIX Mutex functions]]></title>
            <link>https://velog.io/@jiina_28/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9CPOSIX-Mutex-functions</link>
            <guid>https://velog.io/@jiina_28/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9CPOSIX-Mutex-functions</guid>
            <pubDate>Wed, 26 May 2021 01:48:38 GMT</pubDate>
            <description><![CDATA[<h4 id="⬇️헤더파일⬇️">⬇️헤더파일⬇️</h4>
<pre><code class="language-c">#include &lt;ptrhead.h&gt;</code></pre>
<h3 id="pthread_mutex_init">pthread_mutex_init()</h3>
<ul>
<li>정적 초기화<pre><code class="language-c">pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;</code></pre>
</li>
<li>동적 초기화<pre><code class="language-c">pthread_mutex_t mutex;
pthread_mutex_init(&amp;mutex, NULL);</code></pre>
</li>
</ul>
<h3 id="pthread_mutex_lock">pthread_mutex_lock()</h3>
<ul>
<li>critical section(임계구역) 진입시 잠금<pre><code class="language-c">pthread_mutex_lock(&amp;mutex);</code></pre>
</li>
</ul>
<h3 id="pthread_mutex_unlock">pthread_mutex_unlock()</h3>
<ul>
<li>critical section(임계구역) 나올 때 잠금 풀기<pre><code class="language-c">pthread_mutex_unlock(&amp;mutex);</code></pre>
</li>
</ul>
<blockquote>
<p>중요한 점은 한 스레드에서 lock을 걸고 critical section에 들어가 있는데 다른 스레드에서 lock을 호출하면 그 스레드는 이전의 스레드가 critical section을 나올 때까지 (unlock할 때까지) 기다려야함</p>
</blockquote>
<h3 id="pthread_mutex_trylock">pthread_mutex_trylock()</h3>
<ul>
<li>lock을 얻을 수 없을 경우 해당 code에서 block되지 않고 바로 error code를 return (pthread_mutex_lock의 비봉쇄버전)</li>
<li><strong>EBUSY</strong> : mutex가 잠겨있어서 잠금을 얻을 수 없음</li>
<li><strong>EINVAL</strong> : mutex가 잘못 초기화 됨<pre><code class="language-c">pthread_mutex_trylock(&amp;mutex);</code></pre>
</li>
</ul>
<h3 id="pthread_mutex_destry">pthread_mutex_destry()</h3>
<ul>
<li>더이상 mutex를 사용할 일이 없다면 반드시 mutex 객체 삭제하고 자원 되돌려줌 (free)</li>
<li>만약 mutex 자원을 사용하는 스레드가 하나라도 존재한다면 error<pre><code class="language-c">pthread_mutex_destroy(&amp;mutex);</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 시스템 프로그래밍_Ch 3]]></title>
            <link>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-3</link>
            <guid>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-3</guid>
            <pubDate>Sun, 24 Jan 2021 07:01:55 GMT</pubDate>
            <description><![CDATA[<h2 id="ch-3-버퍼-입출력">Ch 3 버퍼 입출력</h2>
<p>3장에서는 표준 c 라이브러리를 통해 파일을 작업하는 것을 설명한다. </p>
<hr>
<h3 id="1-사용자-버퍼-입출력">1. 사용자 버퍼 입출력</h3>
<h4 id="블록-크기">블록 크기</h4>
<ul>
<li>파일 시스템의 최소 저장 단위는 <strong>블록</strong>임.</li>
<li>모든 입출력 연산은 블록 크기의 정수배.</li>
</ul>
<p>❗️하지만 보통 사용자 application에서는 필드나 문자열 등의 입출력 연산이 필요한 경우가 대부분임.
❗️시스템 콜을 이용하여 파일 입출력 작업을 하면 호출할 때마다 커널을 통해 입출력을 수행하여 시스템 효율이 떨어질 수 있음. 
❗️따라서, 사용자 application code 레벨에서 인위적으로 버퍼링을 구현하는데, 이미 있는 사용자 버퍼링 구현체를 가져다 씀 (표준 입출력 라이브러리나 표준 c++ iostream).</p>
<h3 id="2-파일-열기">2. 파일 열기</h3>
<p>표준 입출력에서는 파일 디스크립터 대신 <code>파일 포인터</code>를 사용. 파일 포인터는 c 라이브러리 내부에서 파일 디스크립터로 맵핑됨.</p>
<h4 id="fopen">fopen()</h4>
<p>읽거나 쓰기 위해 fopen()을 사용해서 파일을 엶.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

FILE * fopen (const char *path, const char *mode);</code></pre>
<ul>
<li>파일 <code>path</code>를 <code>mode</code>에 따라 원하는 용도로 새로운 스트림을 만듦.</li>
<li><code>mode</code>
(예시)<ul>
<li><strong>r</strong>: 읽기 목적으로 파일을 엶.</li>
<li><strong>w</strong>: 쓰기 목적으로 파일을 엶.</li>
<li><strong>a</strong>: 덧붙이기 상태에서 쓰기 목적으로 파일을 엶.
...</li>
</ul>
</li>
</ul>
<h4 id="fdopen">fdopen()</h4>
<p>이미 열린 파일 디스크립터(fd)를 통해 스트림을 만듦.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

FILE * fdopen (int fd, const char *mode);</code></pre>
<ul>
<li>mode는 fopen()과 동일.</li>
</ul>
<h3 id="3-스트림-닫기">3. 스트림 닫기</h3>
<h4 id="fclose">fclose()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fclose (FILE *stream);</code></pre>
<ul>
<li>버퍼에 쌓여있지만 아직 스트림에 쓰지 않은 데이터를 먼저 처리.</li>
</ul>
<h4 id="fcloseall">fcloseall()</h4>
<pre><code class="language-c">#define _GNU_SOURCE
#include &lt;stdio.h&gt;

int fcloseall (void);</code></pre>
<ul>
<li>표준 입력, 출력, 에러를 포함하여 현재 프로세스와 관련된 모든 스트림을 닫음.</li>
</ul>
<h3 id="4-스트림에서-읽기">4. 스트림에서 읽기</h3>
<h4 id="한-번에-한-문자씩-읽기-fgetc">한 번에 한 문자씩 읽기: fgetc()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fgetc (FILE *stream);</code></pre>
<ul>
<li><code>stream</code>에서 다음 문자를 읽고 unsigned char 타입을 int 타입으로 변환해서(EOF나 에러를 알려주기 위해) 반환.</li>
</ul>
<h4 id="한-줄씩-읽기-fgets">한 줄씩 읽기: fgets()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

char * fgets (char *str, int size, FILE *stream);</code></pre>
<ul>
<li><code>stream</code>에서 <code>size</code>보다 하나 적은 내용을 읽어서 결과를 <code>str</code>에 저장.</li>
<li>마지막 바이트를 읽은 후 버퍼 마지막에 null 문자(\0) 저장.</li>
<li>EOF나 &#39;\n&#39;을 만나면 읽기 중단함, 만일 &#39;\n&#39; 읽으면 str에 &#39;\n&#39; 저장.</li>
</ul>
<h4 id="바이너리-데이터-읽기-fread">바이너리 데이터 읽기: fread()</h4>
<p>c 구조체 같은 복잡한 바이너리 데이터를 읽을 때 사용.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

size_t fread (void *buf, size_t size, size_t nr, FILE *stream);</code></pre>
<ul>
<li><code>stream</code>에서 각각 크기가 <code>size</code> 바이트인 element를 <code>nr</code>개를 읽어서 <code>buf</code>가 가리키는 버퍼에 저장.</li>
</ul>
<h3 id="5-스트림에-쓰기">5. 스트림에 쓰기</h3>
<h4 id="한-번에-한-문자만-쓰기-fputc">한 번에 한 문자만 쓰기: fputc()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fputc (int c, FILE *stream);</code></pre>
<ul>
<li><code>c</code>로 지정한 바이트를 (unsigned char로 변환한 후에) <code>stream</code>이 가리키는 스트림에 씀.</li>
</ul>
<h4 id="문자열-기록하기-fputs">문자열 기록하기: fputs()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fputs (const char *str, FILE *stream);</code></pre>
<ul>
<li><code>str</code>이 가리키는 NULL로 끝나는 문자열 전부를 <code>stream</code>이 가리키는 스트림에 기록.</li>
</ul>
<h4 id="바이너리-데이터-기록하기-fwrite">바이너리 데이터 기록하기: fwrite()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

size_t fwrite (void *buf, size_t size, size_t nr, FILE *stream);</code></pre>
<ul>
<li><code>buf</code>가 가리키는 데이터에서 <code>size</code> 크기의 element <code>nr</code>개를 <code>stream</code>에 씀.</li>
</ul>
<h3 id="6-스트림-탐색하기">6. 스트림 탐색하기</h3>
<h4 id="fseek">fseek()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fseek (FILE *stream, long offset, int whence);</code></pre>
<ul>
<li>lseek() 시스템 콜과 동일한 기능.</li>
<li><code>offset</code>과 <code>whence</code>에 따라 <code>stream</code>에서 파일 위치를 조작.</li>
</ul>
<h3 id="7-스트림-비우기">7. 스트림 비우기</h3>
<p>여기 3장에서 설명하는 함수들은 모두 c라이브러리가 관리하는 버퍼를 사용하는데 이 버퍼는 사용자 영역에 위치함. 커널이 유지하는 버퍼는 커널영역에 위치함.
fflush()는 사용자 버퍼를 커널로 비워서 스트림에 쓴 모든 데이터가 write()를 통해 실제로 디스크에 기록되도록 만드는 인터페이스.</p>
<h4 id="fflush">fflush()</h4>
<pre><code class="language-c">#include &lt;stdio.h&gt;

int fflush (FILE *stream);</code></pre>
<ul>
<li><code>stream</code>에 있는 쓰지 않은 데이터를 커널로 비움.</li>
<li>한마디로 사용자 버퍼에 있는 데이터를 커널 버퍼로 쓰는 것임. 이는 사용자 버퍼를 사용하지 않고 <strong>write()</strong> 를 직접 사용하는 효과와 동일.</li>
</ul>
<h3 id="8-스레드-세이프">8. 스레드 세이프</h3>
<blockquote>
<p><strong>스레드 세이프(Thread-safe)</strong> 는 멀티 스레드 프로그래밍에서 일반적으로 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 뜻한다. <em>_위키피디아</em></p>
</blockquote>
<p>❗️멀티스레드 환경에서 동작해도 원래 의도한 대로 동작하는 것을 <strong>스레드세이프</strong> 하다고 할 수 있음.
❗️스레드를 지원하는 운영체제는 락 메커니즘을 지원하는데, 표준 입출력은 이를 활용해 단일 프로세스 내의 여러 스레드가 동시에, 심지어는 같은 스트림에 대해서 표준 입출력을 호출할 수 있도록 함.
❗️락을 걸면 critical section(다른 스레드의 간섭 없이 실행할 수 있는 코드)을 설정할 수 있음.</p>
<h4 id="flockfile">flockfile()</h4>
<p>락을 거는 함수</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

void flockfile (FILE *stream);</code></pre>
<ul>
<li><code>stream</code>의 락이 해제될 때까지 기다린 후에 락 counter를 올리고 락을 얻은 다음, 스레드가 <code>stream</code>을 소유하도록 만듦.</li>
</ul>
<h4 id="funlockfile">funlockfile()</h4>
<p>락을 해제하는 함수</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;

void funlockfile (FILE *stream);</code></pre>
<ul>
<li><code>stream</code>과 연관된 락 counter를 하나 줄임.</li>
<li>만일 락 counter가 0이 되면 현재 스레드는 <code>stream</code>의 소유권을 포기해서 다른 스레드가 락을 얻을 수 있도록 함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 시스템 프로그래밍_Ch 2]]></title>
            <link>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-2</link>
            <guid>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-2</guid>
            <pubDate>Sat, 23 Jan 2021 08:31:55 GMT</pubDate>
            <description><![CDATA[<h2 id="ch-2-파일-입출력">Ch 2 파일 입출력</h2>
<p>2장부터 4장까지는 파일에 대해 다루는데, 2장에서는 파일 입출력의 기본을 알아보고 파일을 다루는 가장 기본적이고 간단한 방법인 시스템 콜(syscall)에 대해 이야기한다.</p>
<hr>
<h3 id="1-os">1. OS</h3>
<ul>
<li><p>OS = Kernel+ Shell</p>
<ul>
<li>커널은 운영체제의 핵심으로 운영체제가 수행하는 모든 것이 저장됨.</li>
<li>쉘은 리눅스 환경에서 일반적으로 쓰는 명령어들을 읽어들여 해석하고 결과를 출력해주는 역할을 함.</li>
</ul>
</li>
<li><p>사용자 영역과 커널 영역이 나뉘어져 있고 각 영역엔 버퍼가 있음.</p>
<ul>
<li>syscall을 통해 파일 입출력을 하게 되면 커널을 통해 입출력을 수행함.</li>
<li>예를 들어 open() 이라는 시스템콜을 호출하면, kernel 모드에 들어가서 시스템 콜에 대한 명령어를 수행함.
<img src="https://images.velog.io/images/jiina_28/post/346fe92e-5d20-467b-9912-4af5fd05a6bc/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="2-파일-디스크립터fd">2. 파일 디스크립터(fd)</h3>
<ul>
<li>본격적으로 시스템 콜을 보기 전에 파일 디스크립터(fd)에 대한 개념을 간단하게 살펴보자면,<ul>
<li>프로세스마다 각각 파일 디스크립터 테이블을 가지고 있음. 이 테이블은 사용자 영역과 커널 영역 모두에서 프로세스 내에서 고유한 식별자로 사용됨.</li>
<li>커널은 프로세스 별로 열린 파일 목록인 파일 테이블을 관리함.
<img src="https://images.velog.io/images/jiina_28/post/6ec71bd8-08a2-4485-b089-e8f2b787cef1/image.png" alt=""></li>
<li>파일을 열면 파일 디스크립터가 반환되고 이 파일 디스크립터를 이용하여서 관련 시스템 콜의 다양한 연산을 수행.</li>
<li>프로세스는 최소한 0,1,2 세 가지 파일 디스크립터를 열어둠
<img src="https://images.velog.io/images/jiina_28/post/765dd95d-d11b-42e7-ad0c-fb4b3a527998/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="3-파일-열기">3. 파일 열기</h3>
<p>파일을 읽고 쓰기 전에 open()이나 creat() 시스템 콜을 사용해서 파일을 열어야 한다.</p>
<h4 id="open">Open()</h4>
<pre><code class="language-c">#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;

int open (const char *name, int flags);
int open(const char *name, int flags, mode_t mode);</code></pre>
<ul>
<li>파일을 열고 파일 디스크립터를 얻음.</li>
<li>경로 이름이 <code>name</code>인 파일을 파일 디스크립터에 맵핑하고, 성공하면 이 파일 디스크립터를 반환함.</li>
<li>Open() <code>flags</code><ul>
<li><strong>O_RDONLY(읽기 전용 모드), O_WRONLY(쓰기 전용 모드), O_RDWR(읽기/쓰기 모드)</strong> 중 하나를 포함해야함.</li>
<li><code>flags</code> 매개 변수에 비트 OR연산으로 값을 더 추가해서 열기 동작 변경 가능
(예시)</li>
<li><strong>O_APPEND</strong>: 덧붙이기 모드로 파일을 엶.</li>
<li><strong>O_CREAT</strong>: name에 적은 파일이 없으면 파일을 새로 만듦.</li>
<li><strong>O_TRUNC</strong>: 파일이 존재하고, 일반 파일이며 flags 인자에 쓰기가 가능하도록 명시되어 있으면 파일 길이를 0으로 자름.
...</li>
</ul>
</li>
<li><code>mode</code><ul>
<li>파일의 접근 권한 설정
(예시)</li>
<li><strong>S_IRWXU</strong>: 소유자에게 읽기, 쓰기, 실행 권한 있음.</li>
<li><strong>S_IRWXG</strong>: 그룹에게 읽기, 쓰기, 실행 권한 있음.</li>
<li><strong>S_IRWXO</strong>: 그 외 모든 사용자에게 읽기, 쓰기, 실행 권한 있음.
...</li>
</ul>
</li>
</ul>
<h4 id="creat">creat()</h4>
<pre><code class="language-c">#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;

int creat (const char *name, mode_t mode);</code></pre>
<ul>
<li><strong>O_WRONLY | O_CREAT | O_TRUNC</strong> 조합이 일반적이라 아예 이런 동작 방식을 지원하는 creat() 시스템 콜을 만듦.</li>
</ul>
<h3 id="4-읽기">4. 읽기</h3>
<p>가장 대표적인 읽기 메커니즘은 POSIX.1에 정의된 read() 시스템 콜을 사용하는 것.</p>
<h4 id="read">read()</h4>
<pre><code class="language-c">#include &lt;unistd.h&gt;

ssize_t read (int fd, void *buf, size_t len);</code></pre>
<ul>
<li>fd가 참조하는 파일의 현재 파일 offset에서 <code>len</code> 바이트만큼 <code>buf</code>로 읽어 들임.</li>
<li>성공시 <code>buf</code>에 쓴 바이트 숫자를 반환. 실패시 -1 반환.</li>
<li>읽을 데이터가 충분하면 한 번에 <code>len</code> 바이트만큼 읽지만, 읽을 데이터가 <code>len</code> 바이트 보다 더 적을 경우 더 적게 읽기도 함.</li>
</ul>
<h3 id="5-쓰기">5. 쓰기</h3>
<p>파일에 데이터를 기록하기 위해 사용하는 가장 기본적이며 일반적인 시스템 콜은 write()임.</p>
<h4 id="write">write()</h4>
<pre><code class="language-c">#include &lt;unistd.h&gt;

ssize_t write (int fd, const void *buf, size_t count);</code></pre>
<ul>
<li><code>count</code> 바이트 만큼 파일 디스크립터 <code>fd</code>가 참조하는 파일의 현재 파일 위치에 시작지점이 <code>buf</code>인 내용을 기록.</li>
<li>read()와 비슷하게 동작.</li>
</ul>
<h3 id="6-파일-닫기">6. 파일 닫기</h3>
<p>파일 디스크립터로 읽고 쓰는 작업을 마치고 나면 close() 시스템 콜을 이용해서 파일 맵핑을 끊어야 함.</p>
<h4 id="close">close()</h4>
<pre><code class="language-c">#include &lt;unistd.h&gt;

int close (int fd);</code></pre>
<ul>
<li>열려있는 파일 디스크립터 <code>fd</code>에 연관된 파일과의 맵핑을 해제하며 프로세스에서 파일을 떼어냄.</li>
<li>해제된 파일 디스크립터는 더이상 유효하지 않음.<pre><code>코드를 입력하세요</code></pre><h3 id="7-탐색하기">7. 탐색하기</h3>
파일 디스크립터에 연결된 파일의 오프셋을 특정 값으로 지정함.<h4 id="lseek">lseek()</h4>
<pre><code class="language-c">#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
</code></pre>
</li>
</ul>
<p>off_t lseek(int fd, off_t pos, int origin);</p>
<pre><code>- `origin` 인자
    - **SEEK_CUR**: `fd`의 파일 오프셋을 현재 오프셋에서 `pos`를 더한 값으로 설정.
    - **SEEK_END**: `fd`의 파일 오프셋을 현재 파일 크기에서 `pos`를 더한 값으로 설정.
    - **SEEK_SET**: `fd`의 파일 오프셋을 `pos` 값으로 설정.

### 8. 동기식 입출력
애플리케이션에서 직접 데이터가 디스크에 기록되는 시점을 제어하고 싶을 때가 있음. 이런 때를 위해 리눅스 커널에서는 성능을 희생하는 대신 입출력을 동기화하는 옵션 제공.
#### fsync()
```c
#include &lt;unistd.h&gt;

int fsync (int fd);</code></pre><ul>
<li>파일 디스크립터 <code>fd</code>에 맵핑된 파일의 모든 변경점을 디스크에 기록.</li>
<li>이때 파일 디스크립터 <code>fd</code>는 반드시 쓰기 모드로 열려야 함.</li>
</ul>
<h3 id="9-다중-입출력">9. 다중 입출력</h3>
<p>애플리케이션이 여러 개의 파일 디스크립터를 동시에 블록하고 그중 하나라도 블록되지 않고 읽고 쓸 준비가 되면 알려주는 기능. 어떤 파일 디스크립터에 이벤트가 발생했는 지 주기적으로 확인.</p>
<h4 id="select">select()</h4>
<pre><code class="language-c">#include &lt;sys/select.h&gt;

int select (int n, fd_set *readfds, fd_set *writefds, fd_set, *exceptfds, struct timeval *timeout);

FD_CLR (int fd, fd_set *set);
FD_ISSET (int fd, fd_set *set);
FD_SET (int fd, fd_set *set);
FD_ZERO (fd_set *set);</code></pre>
<ul>
<li>파일 디스크립터가 입출력을 수행할 준비가 되거나 옵션으로 정해진 시간이 경과할 때까지만 블록됨.</li>
<li>검사 대상 파일 디스크립터는 세 가지 집합으로 나뉘어 각각 다른 이벤트를 기다림.<ul>
<li><code>readfds</code>: 데이터 읽기가 가능한지(블록되지 않고 read() 작업이 가능한지) 파악</li>
<li><code>writefds</code>: 블록되지 않고 write() 작업이 가능한지 감시</li>
<li><code>exceptfds</code>: 예외가 발생했거나 대역을 넘어서는 데이터가 존재하는지 감시</li>
</ul>
</li>
<li>select()에서 사용하는 파일 디스크립터 집합은 직접 조작하지 않고 <strong>매크로</strong>를 사용해서 관리<ul>
<li><strong>FD_ZERO</strong>: 지정한 집합 내의 모든 파일 디스크립터 제거 (항상 select() 호출 전에 사용)</li>
<li><strong>FD_SET</strong>: 주어진 집합에 파일 디스크립터를 추가</li>
<li><strong>FD_CLR</strong>: 주어진 집합에서 파일 디스크립터 하나 제거</li>
</ul>
</li>
</ul>
<h4 id="poll">poll()</h4>
<pre><code class="language-c">#include &lt;poll.h&gt;

int poll (struct pollfd *fds, nfds_t nfds, int timeout);</code></pre>
<ul>
<li>select()의 몇 가지 결점을 보완하지만, 여전히 습관이나 이식성의 이유로 select()를 더 많이 사용함.</li>
<li><code>fds</code>가 가리키는 단일 pollfd 구조체 배열을 <code>nfds</code> 개수만큼 사용.</li>
<li>pollfd 구조체<pre><code class="language-c">#include &lt;poll.h&gt;
</code></pre>
</li>
</ul>
<p>struct pollfd{
int fd;     //파일 디스크립터
short events;   //감시할 이벤트
short revents;  //발생한 이벤트</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 시스템 프로그래밍_Ch 1]]></title>
            <link>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-1</link>
            <guid>https://velog.io/@jiina_28/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DCh-1</guid>
            <pubDate>Sat, 23 Jan 2021 07:31:48 GMT</pubDate>
            <description><![CDATA[<p>앞으로 쓸 리눅스 시스템 프로그래밍_ 글들은 &#39;Linux System Programming&#39; (Robert Love) 책의 내용들을 바탕으로 정리하는 글입니다. <del>내 마음대로 쉬운 말로 정리할거임</del></p>
<hr>
<h2 id="ch1-핵심-개념-소개">Ch1 핵심 개념 소개</h2>
<ul>
<li><p><strong>system call(syscall)</strong>: 사용자 영역에서 커널 내부로 들어가기 위한 함수 호출로 read()나 write() 등이 있음.</p>
</li>
<li><p><strong>API와 ABI</strong></p>
<ul>
<li>API: 소프트웨어의 <strong>소스 코드 레벨</strong>에서, 즉 <strong>프로그래밍 언어 레벨</strong>에서 인터페이스 제공</li>
<li>ABI: <strong>바이너리 레벨</strong>에서 인터페이스 제공</li>
</ul>
</li>
<li><p><strong>POSIX</strong>: os의 표준이라고 할 수 있음. (상식적으로) 스레드나 유틸리티 등을 정의함. 강제성은 없음.</p>
</li>
<li><p>리눅스는 모든 것이 <strong>파일</strong>이므로 모든 인터렉션은 파일을 읽고 쓰는 것</p>
<ul>
<li><strong>리눅스에서 파일이 매우 중요!!!</strong></li>
<li>file descriptor(fd)로 컨트롤</li>
<li>일반 파일, 디렉토리, 링크 등 모두 다 파일 형태임</li>
</ul>
</li>
<li><p>inode: 파일 시스템 내에서만 고유한 정수값</p>
</li>
<li><p><strong>하드링크(Hard link)</strong>: 동일한 inode에 대한 여러가지 파일 이름 mapping. (바로가기 느낌)</p>
</li>
<li><p><strong>심볼릭링크(Symbolic link)</strong>: 여러 파일 시스템에 걸쳐 사용할 수 있도록 제공. (서로가 같은 것을 공유하는 같이 업데이트하는 느낌)</p>
</li>
<li><p><strong>스레드(Thread)</strong>: 프로세스 내에서의 흐름. 코드시퀀스의 집합들. 프로세스 내부에서 실행하는 단위.</p>
<ul>
<li>각 프로세스는 스레드 하나 이상을 가짐. (하나: 싱글 스레드, 여러개: 멀티 스레드)</li>
</ul>
</li>
<li><p><strong>시그널(Signal)</strong>: 인터럽트(interrupt)를 갑자기 발생시켜서 어떠한 사건(Event)을 발생시킴(예를 들면 ctrl+c(복사) 같은거)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Parkinsons Telemonitoring Using Deep Learning]]></title>
            <link>https://velog.io/@jiina_28/Parkinsons-Telemonitoring-Using-Deep-Learning</link>
            <guid>https://velog.io/@jiina_28/Parkinsons-Telemonitoring-Using-Deep-Learning</guid>
            <pubDate>Thu, 21 Jan 2021 04:46:32 GMT</pubDate>
            <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p><img src="https://images.velog.io/images/jiina_28/post/c4cdb62b-b301-41df-8046-13b3d53ac81a/image.png" alt="">
이 글은 20-2학기 머신러닝 수업에서 진행한 term project에 관해 정리한 글이다. 나는 Parkinsons telemonitoring에 관한 dataset을 선택하여 프로젝트를 진행하였다. 옥스포드 대학교의 Atanasios Tsanas와 Max Little, 그리고 10개의 미국 메디컬센터, 인텔 기업이 함께 이 dataset을 만들었다. 원래 이 연구는 다양한 선형 및 비선형 회귀 방법을 사용하여 통합 파킨슨병 등급 척도(UPDRS)로 임상의사의 파킨슨병 증상 점수를 예측했으며, 나는 여기서 딥러닝 DNN 모델을 통해 UPDRS 점수를 예측하는 프로젝트를 진행하였다.</p>
<h2 id="information-of-data">Information of data</h2>
<p>이 dataset은 초기 파킨슨병을 앓고 있는 42명의 생체 의학 음성 측정 범위로 구성되어 있다. 다음은 dataset에 포함된 모든 변수의 이름과 해당 사용에 대한 설명이다. &#39;parkinsons_updrs.data&#39;에는 22개의 특성과 5875개의 인스턴스가 있다. 그 중 <strong>motor_UPDRS</strong> 및 <strong>total_UPDRS</strong>는 target variables이다. <strong>motor_UPDRS</strong>는 환자들의 motor UPDRS 점수이고, <strong>total_UPDRS</strong>는 환자들의 total UPDRS 점수이다.
<strong>subject#</strong> 는 각 주체를 고유하게 식별하는 정수이다. 한 마디로 각 개인마다 고유 번호가 있다.
<strong>나이</strong>는 각 주체의 연령이며, 성별은 각 주체의 성별입니다. (&#39;0&#39;은 남성, &#39;1&#39;은 여성)
<strong>test_time</strong>은(는) 평가판에 채용된 이후 시간이고 정수 부분은 채용 후 일수입니다. <strong>Jitter(%), Jitter(Abs), Jitter:RAP, Jitter:PPQ5, Jitter:DDP</strong>는 기본 주파수의 변동에 대한 몇 가지 측정값이다. 
<strong>Shimmer, Shimmer(dB), Shimmer:APQ3, Shimmer:APQ5, Shimmer:APQ11, Shimmer:DDA</strong>는 진폭의 변동에 대한 몇 가지 측정치이다. 
<strong>NHR, HNR</strong>은 음성의 톤 성분에 대한 소음 비율 측정치이다. 
<strong>PRDE</strong>는 비선형 동적 복잡도 측정이다.
<strong>DFA</strong>는 신호 프랙탈 스케일링의 지수이다. 
<strong>PPE</strong>는 기본 주파수 변동에 대한 비선형 측정이다.</p>
<p>전체 데이터를 훈련 데이터(70%)와 테스트 데이터(30%)로 분할하였고, 데이터의 스케일이 크게 다를 경우 문제가 발생하지 않도록 데이터를 정규화하였다.</p>
<p>파이토치의 <strong>Dataset</strong>과 <strong>Dataloader</strong>를 통해 대량의 학습을 batch 단위로 처리하여 손실을 줄일 수 있다. 따라서 dataset을 훈련(train) dataset과 테스트(test) dataset으로 나누는 두 가지 클래스를 만들었다: <strong>ParkinsonsTrainDataset, ParkinsonsTestDataset.</strong> 만들어진 Dataloader는 크게 세 부분으로 나뉜다.</p>
<ol>
<li><p><strong>init</strong>(self) 는 데이터를 읽거나 다운로드 하는 부분이다.</p>
</li>
<li><p><strong>getitem</strong>(self, index) 는 인덱스에 해당하는 항목을 넘기는 부분이다. </p>
</li>
<li><p><strong>len</strong>(self) 는 데이터의 크기를 전달하는 부분이다.</p>
</li>
</ol>
<pre><code class="language-python">class ParkinsonsTrainDataset(Dataset):
    def __init__(self):
        xy = np.loadtxt(&#39;./parkinsons_updrs.data&#39;,
                        delimiter=&#39;,&#39;, skiprows=1, dtype=np.float32)
        train_len = int(xy.shape[0] * 0.7)
        train_xy = xy[:train_len]
        self.len = train_xy.shape[0]
        y_label_indx = [5, 6]
        self.train_x = np.delete(train_xy, y_label_indx, axis=1)
        self.train_x = torch.from_numpy(self.train_x)
        self.train_y = torch.from_numpy(train_xy[:, y_label_indx[0]: y_label_indx
        [1] + 1])

        self.train_x, mu, sigma = normalize(self.train_x)
        self.train_y, mu, sigma = normalize(self.train_y)

    def __getitem__(self, index):
        return self.train_x[index], self.train_y[index]

    def __len__(self):
        return self.len</code></pre>
<p>훈련할 dataloader와 테스트할 dataloader를 만들 때 각 dataset으로 만든 클래스를 넘겨준다.</p>
<pre><code class="language-python">train_dataset = ParkinsonsTrainDataset()
test_dataset = ParkinsonsTestDataset()

train_loader = DataLoader(dataset=train_dataset,
                          batch_size=32,
                          shuffle=True,
                          num_workers=0, drop_last=True)
test_loader = DataLoader(dataset=test_dataset,
                          batch_size=32,
                          num_workers=0)</code></pre>
<h2 id="approach">Approach</h2>
<p>나는 회귀와 함께 여러 FC(Full Connected) 계층으로 구성된 심층 신경망(DNN) 모델에서 학습한 후 UPDRS 점수를 예측했다.
<img src="https://images.velog.io/images/jiina_28/post/0efe0f46-26cf-4e49-9636-989bb6a45d9b/image.png" alt=""></p>
<p>심층 신경망(DNN)은 입력 계층과 출력 계층 사이의 여러 숨겨진 계층으로 구성된 인공 신경망으로, 학습 결과를 개선하기 위해 숨겨진 계층을 크게 증가시킨다. DNN은 dropout, Rectified Linear Unit(ReLU)과 같은 방법이 적용됨에 따라 딥 러닝의 핵심 모델로 사용되고 있다. DNN은 더 적은 수의 장치만이 복잡한 데이터를 모델링할 수 있도록 한다. </p>
<p>나는 hidden nodes를 300, input nodes는 20, output nodes는 2로 정하였다. 또한 평균 제곱 오차(MSE)와 optimizer를 사용하기 위해 손실 함수를 정하였다. model.parameters()를 호출하여 SGD를 사용하면 모델의 계층이 학습할 파라미터를 넘겨준다. 여기서 러닝레이트는 0.0001 정하였다.</p>
<pre><code class="language-python">class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.l1 = nn.Linear(20, 300) 
        self.relu = nn.ReLU()
        self.l2 = nn.Linear(300, 2)  

    def forward(self, x):
        out1 = self.l1(x)
        out2 = self.relu(out1)
        out = self.l2(out2)
        return out

model = Model()

criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.0001)</code></pre>
<h2 id="training">Training</h2>
<p>이제 예측하기 전에 모델을 훈련시키는 과정이 필요하다. 그런데 <strong>total_UPDRS</strong> 손실과 <strong>motor_UPDRS</strong> 손실 사이에 큰 스케일 차이가 있었다. 따라서 <strong>motor_UPDRS</strong> 손실에 가중치(3)를 곱해주었다.</p>
<pre><code class="language-python">for epoch in range(30):
    for i, data in enumerate(train_loader):
        inputs, labels = data

        y_pred = model(inputs)

        motor_updrs_mse = criterion(y_pred[:, 0], labels[:, 0])
        total_updrs_mse= criterion(y_pred[:, 1], labels[:, 1])

        total_loss = 3 * motor_updrs_mse + total_updrs_mse

        print(f&#39;Epoch {epoch + 1} | Step: {i+1} | Loss: {total_loss.item():.4f}&#39;)

        optimizer.zero_grad()
        total_loss.backward()
        optimizer.step()</code></pre>
<p><img src="https://images.velog.io/images/jiina_28/post/e6198379-d930-4f85-b20f-75c4d4804f9e/image.png" alt=""></p>
<h2 id="prediction">Prediction</h2>
<p>After training, the result of average of all UPDRS mse, Motor_UPDRS mse and Total_UPDRS mse is below.
훈련 후 모든 UPDRS mse, <strong>motor_UPDRS</strong>의 mse, <strong>total_UPDRS</strong>의 mse는 아래와 같다.
<img src="https://images.velog.io/images/jiina_28/post/cfc0a066-b062-4bdd-834c-578c409f56b3/image.png" alt="">
모델링할 때마다 모든 UPDRS mse 값이 계속 많이 바뀌어서 초기 값을 고정해주었다.</p>
<pre><code class="language-python">torch.manual_seed(1234)</code></pre>
<p>첫번째 그래프는 <strong>motor_UPDRS</strong>를 예측한 결과를 나타내는 그래프이고, 두번째 그래프는 <strong>toal_UPDRS</strong>를 예측한 결과를 나타내는 그래프이다. 그래프를 보면 <strong>total_UPDRS</strong>는 매우 잘 예측이 되지만, <strong>motor_UPDRS</strong>는 살짝 불완전하게 예측이 잘 안되는 부분이 있는 것을 확인할 수 있다. 나중에 결론 부분에서 다시 언급을 할 예정이지만, 나는 이러한 <strong>motor_UPDRS</strong>의 예측 성능을 높이기 위해 여러가지 노력을 하였다.</p>
<p><img src="https://images.velog.io/images/jiina_28/post/516d7b55-35b5-442d-86d0-faee40a11427/image.png" alt=""><img src="https://images.velog.io/images/jiina_28/post/84d67570-7a62-43f4-88d9-39efb0a1ae17/image.png" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p>솔직히 이 프로젝트를 하는 과정이 쉽지는 않았다. 딥러닝을 처음 접해보면서 혼자서 이렇게 프로젝트를 진행하는 것도 처음이었기 때문에, 이러한 결과들을 도출해내기까지 나에게는 많은 시간과 노력이 필요했다. 처음에는 데이터 정규화 과정을 거치지 않고 모델링 한 후 예측을 시도했더니 스케일이 너무 크고 차이가 심해서 제대로 예측이 안되었다. 특히 <strong>total_UPDRS</strong>의 손실이 <strong>motor_UPDRS</strong>의 손실보다 심하게 작았기 때문에 <strong>total_UPDRS</strong>를 잘 예측하기가 힘들었다. 이 과정을 통해 데이터 정규화의 중요성을 깨달았다.
(아래 그래프는 정규화하지 않은 채 예측한 <strong>total_UPDRS</strong>이다)
<img src="https://images.velog.io/images/jiina_28/post/5c9f3742-5560-467a-976f-75fa9445667d/image.png" alt=""></p>
<p>데이터를 정규화한 후 어느 정도 예측이 가능한 것처럼 보였지만, <strong>motor_UPDRS</strong>와 <strong>total_UPDRS</strong> 둘 다 잘 예측하기 위한 적절한 기준점을 찾기가 어려웠다. 정규화를 통해 값이 어느 정도 매끄러워졌지만, <strong>motor_UPDRS</strong>의 스케일에서 예측이 필요했기 때문에(?) <strong>motor_UPDRS</strong>의 손실에 가중치를 곱할 수 밖에 없었고, 그제서야 예측이 잘 되었다.</p>
<h4 id="entire-code"><a href="https://github.com/jiina28/ParkinsonsDNN.git">Entire Code</a></h4>
<h3 id="reference">Reference</h3>
<p>Telemonitoring of Parkinson&#39;s disease progression by non-invasive speech tests. (n.d.). Retrieved December 26, 2020, from <a href="https://www.neuraldesigner.com/learning/examples/parkinsons-disease-telemonitoring">https://www.neuraldesigner.com/learning/examples/parkinsons-disease-telemonitoring</a></p>
]]></description>
        </item>
    </channel>
</rss>