E D R , A S I H C RSS
Might as well be frank, monsieur. It would take a miracle to get you out of Casablanca.

No older revisions available

No older revisions available




3일차 #

geshi c
int open( char *, int, int );
open함수는 저수준함수(커널과 1:1매칭, eax에는 어떠한 인터럽트가 호출될것인가의 인덱스 넘버가 저장)이다. 반면에, fopen함수는 저수준함수를 이용한 라이브러리함수이다. 저수준함수는 리턴값이 커널과의 통신이 제대로 되었는지 정도의 값이라고 보면 된다. 저수준함수와 라이브러리 함수는 같이 쓰지 않는것이 좋다.(일반적으로 계층이 달라지면 일반적으로 오버헤드를 줄이기 위해 버퍼링 기술을 사용된다)

const는 한번 초기화 되면서 값이 변하지 않음....
const의 위치는 포인터를 상수화 시키느냐? 포인터가 가리키는 위치를 상수화 시키느냐의 차이.
geshi c
const char * // 일반적인 라이브러리에서 많음, 참조만 할것이기 때문에, 자체를 복사하지 않고
char const * // 포인터를 가져온다.
char * const
char const * const



read함수를 통해 커널이 돌아가는 원리를 살펴보자.

geshi c
int read( int fd, char *buf, int cnt );
EAX = __NR_read, EBX = fd, ECX = buf, EDX = cnt 를 넣고, INT 0x80을 호출할 것이다.

이렇게 되면 커널 모드의
int sys_read( int fd, char *buf, int cnt )을 부를 것이다.
sys_read를 분석해 보자.

커널 함수 분석을 위해서는 함수 위주가 아니라, 객체 위주의 분석을 해야 한다.
task_struct 내부를 살펴보면,
struct files_struct *files;
위와 같이 files_struct가 있다. 해당 내용을 살펴보면.

geshi c
struct files_struct {
        atomic_t count;
        rwlock_t file_lock;     /* Protects all the below members.  Nests inside tsk->alloc_lock */
        int max_fds;
        int max_fdset;
        int next_fd;
        struct file ** fd;      /* current fd array */
        fd_set *close_on_exec;
        fd_set *open_fds;
        fd_set close_on_exec_init;
        fd_set open_fds_init;
        struct file * fd_array[NR_OPEN_DEFAULT];
};
구조 안에 ** fd, * fd_array[]가 있다.
** => 포인터 배열에 대한 선두번지를 잡는 것.
** fd는 * fd_array[]에 대한 선두번지를 잡는다고 보자.(최근 버전에서는 동적 파일을 할당해 다른 곳을 가르킴)
포인터 배열의 원소는 포인터.
fd_array의 원소는 어딘가에 선언된 file 구조체들을 링크하고 있다.
STD IN, STD OUT, STD ERR 다음에 해당 객체를 만들고 * fd_array의 3번째(file descriptor number)에 링크한다.

geshi c
struct file {
        struct list_head        f_list;
        struct dentry           *f_dentry;
        struct vfsmount         *f_vfsmnt;
        struct file_operations  *f_op;
        atomic_t                f_count;
        unsigned int            f_flags;
        mode_t                  f_mode;
        loff_t                  f_pos;
        unsigned long           f_reada, f_ramax, f_raend, f_ralen, f_rawin;
        struct fown_struct      f_owner;
        unsigned int            f_uid, f_gid;
        int                     f_error;

        unsigned long           f_version;

        /* needed for tty driver, and maybe others */
        void                    *private_data;

        /* preallocated helper kiobuf to speedup O_DIRECT */
        struct kiobuf           *f_iobuf;
        long                    f_iobuf_lock;
};
struct file_operations *f_op; 를 유의해서 보면

geshi c
struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
이런식으로 내부에는 함수가 존재한다.

이러한 배경 지식으로 다시 한번 sys_read를 검토해보자.
read( 3, buf, cnt );
INT 0x80, EAX = __NR_read 가 넘어온다.
sys_read( 3, buf, cnt );

geshi c
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
        struct file *file; // file 포인터를 만들고.
        ssize_t ret = -EBADF;
        int fput_needed;

        file = fget_light(fd, &fput_needed); // fget_light(3, &fput_needed)
        if (file) {
                loff_t pos = file_pos_read(file); // pos = file->f.pos
                ret = vfs_read(file, buf, count, &pos); // vfs_read( file, buf, cnt, &pos)
                file_pos_write(file, pos);
                fput_light(file, fput_needed);
        }

        return ret;
}

geshi c
struct file fastcall *fget_light(unsigned int fd, int *fput_needed)
{
        struct file *file; // 여기에서도 file 포인터를 만드네..
        struct files_struct *files = current->files; //current는 현재 실행되는 task의 task_struct를 이야기한다.

        *fput_needed = 0;
        if (likely((atomic_read(&files->count) == 1))) {
                file = fcheck_files(files, fd);
        } else {
                spin_lock(&files->file_lock);//공유를 위한 확인. 필요 없음.
                file = fcheck_files(files, fd);//결국엔 이런 함수를 호출한다.
                if (file) {
                        get_file(file);
                        *fput_needed = 1;
                }
                spin_unlock(&files->file_lock);//마찬가지
        }
        return file;
}

여기서 current를 한번 살펴 보자.
geshi c
static inline struct task_struct * get_current(void)
{
        struct task_struct *current;
        __asm__("andl %%esp,%0; ":"=r" (current) : "" (~8191UL));
        // 8191 = 0x001FFF, ~8191 = 0xFFFFE000 이다. 변환해보면
        // __asm__("andl %%esp,%0; ":current=EAX : EAX = 0xFFFFE000);
         return current;
  }
Task는 페이징 기법을 통해 메모리가 관리 된다. 페이지의 크기는 4Kbyte. 그리고 각 Task마다 2개의 페이지, 8Kbyte가 할당된다. 그러므로 위의 코드는 해당 Task의 선두번지를 가져오는것이다.

file * file = fcheck_files(files, 3);

geshi c
static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
{
        struct file * file = NULL;

        if (fd < files->max_fds) //오픈될 수 있는 파일의 갯수보다 작으면....
                file = files->fd[fd];
geshi c
                //file = files->fd[3], [#files_struct files_struct]의 3번째 엔트리에 있는 file의 선두번지를 넘겨준다.

        return file;
}

이제 *file이 원하는 파일을 가르키게 되었다. 다시 sys_read로 가라.
file_pos_read
geshi c
static inline loff_t file_pos_read(struct file *file)
{
        return file->f_pos;
}

geshi c
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
        ssize_t ret;

        if (!(file->f_mode & FMODE_READ)) // 파일 권한 확인
                return -EBADF;
        if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) // 실행 함수 확인
                return -EINVAL;
        if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
                return -EFAULT;

        ret = rw_verify_area(READ, file, pos, count);
        if (!ret) {
                ret = security_file_permission (file, MAY_READ);
                if (!ret) {
                        if (file->f_op->read)
                                ret = file->f_op->read(file, buf, count, pos);
// 결론적으로 read를 호출한다.(최종)

geshi c
                        else
                                ret = do_sync_read(file, buf, count, pos);
                        if (ret > 0) {
                                dnotify_parent(file->f_dentry, DN_ACCESS);
                                current->rchar += ret;
                        }
                        current->syscr++;
                }
        }

        return ret;
}




실제 디바이스는 어떻게 만드나

[http]http://linuxkernel.net/moduleprog/에서
[http]http://linuxkernel.net/moduleprog/lkp/source/minibuf.c character device에 대한 내용을 살펴보면 되겠다..^^;



open()함수 살펴보기

geshi c
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	int fd0, fd1 ;
	char buf[2][20] = { "Arminia-209", "GL-209" } ;
	fd0 = open("temp.dat", O_RDWR | O_CREAT | O_TRUNC, 0614) ;
	fd1 = open("temp.dat", O_RDWR | O_CREAT, 0614) ;
	write(fd1, buf[0], 12) ;
	lseek(fd1, SEEK_SET, 0) ;
	write(fd1, buf[1], 6) ;
	printf("[ fd0:%d ]\n", fd0) ;
	printf("[ fd1:%d ]\n", fd1) ;
	close(fd0);
	close(fd1);
}
  • O_RDONLY : 읽기 전용
  • O_WRONLY : 쓰기 전용
  • O_RDWR : 읽고 쓰기
추가적으로 다음의 선택적인 모드 조합이 가능
  • O_CREATE : 필요하다면 파일을 생성
  • O_APPEND : 데이터를 파일 끝에 계속 붙여서 씀
  • O_TRUNC : 기존의 내용을 모두 삭제
  • O_EXCL : 파일 중복 생성을 방지

위의 예제의 결과로 중첩된 데이터가 표시됨을 볼 수 있다. 이러한 결과는 files_struct에서 file을 공유하는 것이 아니라 물리적인 영역(inode)만 공유하고, file은 다르게 생성된다는 것을 알 수 있다.(pos가 달라짐을 통해)

여기에서 fd2 = dup(fd0) 를 실행할 경우에는 file을 공유하고 fd0의 정보를 fd2로 넘겨주는 작업을 하게 된다.



디렉토리

geshi c
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	int fd0, fd1, fd2 ;
	char buf[2][20] = { "Arminia-209", "GL-209" } ;
	fd0 = open("temp.dat", O_RDWR | O_CREAT | O_TRUNC, 0644) ;
	fd1 = open("temp.dat", O_RDWR | O_CREAT, 0614) ;
	fd2 = dup(fd0) ;
	write(fd0, buf[0], 12) ;
	write(fd2, buf[1], 6) ;
	printf("[ fd0:%d ]\n", fd0) ;
	printf("[ fd1:%d ]\n", fd1) ;
	close(fd0);
	close(fd1);
}

inode를 통해서 디렉토리를 찾아간다.



숙제

@source.zip (1.16 KB)
@hh.exe (188 KB)

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2024-02-11 16:46:58
Processing time 0.0136 sec