dirty_inode를 sync하는것에 대해 궁금합니다.
리눅스에서
file의 중간 truncate function을 만들기위한 작업의 일환으로 데이타블록의 위치를 바꿔보는
시도를 하던중, dirty_inode의 sync를 해결하지 못하겠습니다.
다음과같은 source를 짜서..
int *add_temp;
struct nameidata nd;
user_path_walk(filename,&nd);
inode=nd.dentry->d_inode;
printk("inode number = %d\n",inode->i_ino);
printk("i_data [0] address = %d \n",inode->u.ext3_i.i_data[0]);
printk("i_data [1] address = %d \n",inode->u.ext3_i.i_data[1]);
printk("i_data [2] address = %d \n",inode->u.ext3_i.i_data[2]);
add_temp=inode->u.ext3_i.i_data[0];
inode->u.ext3_i.i_data[0]=inode->u.ext3_i.i_data[2];
inode->u.ext3_i.i_data[2]=add_temp;
printk("i_data [0] address = %d \n",inode->u.ext3_i.i_data[0]);
printk("i_data [1] address = %d \n",inode->u.ext3_i.i_data[1]);
printk("i_data [2] address = %d \n",inode->u.ext3_i.i_data[2]);
inode->i_sb->s_op->write_inode(inode,1);
대략 내용은, user_path_walk로 nameidata를 얻고, 그것으로부터 inode에 대한 정보를
얻어. 4k*3짜리의 간단한 파일을 순서를 바꿔보는 code입니다.(0-1-2 => 2-1-0)
물론 시스템콜이고요..
데이타블록 하나(4k)에 각각 1,2,3을 주루룩 채워놓고.. 실행해보면
1...2...3...이 실행후 바뀌지 않고. 재부팅을 하면 3...2...1..로 바뀌네요.
뭐 이거야 바로 디스크에 쓰지 않는 리눅스 특성상 당연한 얘기이겠지만,
sync와 관련된 모든 system call을 추가해봐도 상황이 똑같습니다;;
bdflush()나 fsync()부터.. emergency까지 동원해도 계속 그러네요.
어떻게하면 바로 디스크에 쓸 수 있도록 할 수 있을까요?
디스크에 바로
디스크에 바로 쓰도록 할 필요는 없을 것 같습니다. 저렇게 변경된 내용으로 보이게 하는 것이 목적이신것 같은데, 아마 처음에 데이터 블럭을 읽어온 녀석이 여전히 페이지 캐시에 남아있어서 그런것이 아닐까 생각되어지는데요.
최초에 데이터 블럭을 읽은 경우라면 page->index 에 최초의 index 가 남아 있어서 저렇게 보이는 것으로 생각됩니다.
페이지 캐시를 inval 시키시면 해결되지 않을까 싶은데, 정확한건 커널소스를 자세히 보셔야할 것 같네요.
==========================
별은 바라보는 자에게 빛을 준다
==========================
별은 바라보는 자에게 빛을 준다
답변
답변 감사드립니다.
이 call의 목표가 최종적으로, 정확히는 call이 종료된 즉시 물리적 디스크상에서 각 data block을 향하는 포인터가 바뀌어져 있는것을 원합니다.
위와같이 하면, 슈퍼블록의 operation으로 디스크에 바로 쓰여지는것이 보장되는지.
그렇지 않다면 어떠한 콜을 사용해야 디스크상에 바로 갱신이 되고 확인할 수 있는지 알고싶습니다
온갖 sync관련 콜은 찾아다가 써붙이고있는데 바로 되질 않고 꼭 재부팅을 해야 cat으로 열어봤
을때 원하는 배치가 되어있네요.
궁금해지네요
이 질문을 보니까 궁금해집니다.
잘 몰라서 그러는데 그럼 리눅스에서는 fflush()같은 함수들을 호출해도 실제로는 쓰기가 되지 않나요?
표현하신대로
표현하신대로 dirty_inode가 sync되지 않기 때문에 생기는 문제가 아닐 듯 합니다.
작업간의 타이밍에 따라 해당 Page가 Dirty가 아닐 가능성도 많습니다.
다음은 가능한 타이밍 중 하나입니다.
1. User Mode에서 데이타블록 하나(4k)에 각각 1,2,3을 주루룩 채워놓는다.
=> 각 블록의 내용을 가지는 Page가 Page Cache에 위치하게 되며 모두 PG_dirty, PG_uptodate 상태가 됩니다.
2. 커널에서 pdflush 데몬이 그 Page를 디스크에 쓴다.
=> 쓰여진 Page는 PG_ditry 플래그가 없어지고 Clean 상태가 됩니다. PG_uptodate 플래그는 그대로구요. 또한 파일에 해당 블록이 처음으로 쓰여지면 비로소 디스크 상에 블록이 할당되고 inode->u.ext3_i.i_data[] 할당된 정보가 쓰여집니다.
3. inode의 블록 위치 정보 i_data[]를 수정한다.
위와 같은 순서로 진행된다면 결국 각 Page는 PG_update 상태로 남아 있기때문에 파일을 읽어도 Page Cache에서 Hit가 되어 순서가 바뀌어서 나오지 않으며, Sync를 하더라도 PG_dirty 플래그가 Clear된 상태이기 때문에 디스크에 쓰여지지 않습니다.
Page를 invalidate 시키거나 PG_uptodate 플래그를 Clear 하는 방식으로 일단 원하시는 바를 이룰 수 있을 듯 합니다.
하지만 invalidate시킬 Page에 쓰기 작업이 일어나거나 (PG_dirty가 설정됨) 디스크에 쓰여지는 중이거나 (PG_writeback가 설정됨) 하는 경우도 고려해야 하기 때문에 제대로 구현하는 일은 쉽지만은 않을 듯 하네요.
정확히 구현하고자 하신다면
1. inode의 블록 위치 정보를 수정하기 전에 해당 파일의 입출력을 막는다.
2. 파일의 Page 블록을 invalidate 시킨다.
3. indoe 블록 위치 정보를 수정한다.
위의 방식으로 하셔야 할 듯 합니다.
voodoochild 님이
voodoochild 님이 재부팅하면 바뀌어서 원하는 결과가 보인다는 말은 수정후 dirty 까지 제대로 되었다는 것으로 추정됩니다. 결국 디스크에 쓰여진것과 다름 없다는 말이죠.
그럼 왜 재부팅하기 전에는 바뀌어진 채로 보이지 않느냐 인데...
제가 아는 지식으로 voodoochild 님이 바꾸신 것은 해당 파일의 metadata 입니다. 즉 데이터 블럭을 포인팅하고 있는 값을 바꾼 것이죠.
하지만 데이터 블럭이 metadata 의 값을 바꾸기 전에 읽혀졌다면, 이미 0, 1, 2 번 페이지는 이미 메모리 상에 올라와서 캐싱되어 있을 것입니다.
즉 페이지를 포인팅하는 값을 바꾸어도 페이지 캐시를 조정해주지 않으시면, 해당 페이지 캐시가 메모리 부족으로 사라지고 다시 읽혀오기 전까지 처음에 읽었던 페이지 캐시로 찾아서 데이터를 넘겨줄 것입니다.
처음에 파일을 읽지 마시고 위 코드를 수행한 후에 읽어보세요. 만약 그 때 원하시는 대로 바뀌었다면, 제가 말씀드린 내용이 맞을 겁니다.
제가 알고 있는 한 저것을 바꾸는 시스템 콜은 없을 겁니다. 추가하신 시스템콜에서 관련된 커널 함수를 찾아서, 직접 호출해서 page cache 에서 관련 페이지를 inval 시키시거나, 제거하셔야 할 것 같네요.
fflush 가 어떤 시스템 콜을 호출하는지 잘 모르겠네요. bdflush 나 fsync 등을 사용하면 디스크에 기록됩니다. 다만 voodoochild 님의 경우에는 좀 다른 이유는 정해진 시스템콜을 통해 들어온 것이 아니고, 직접 작성하신 시스템 콜을 통해서 metadata 를 수정하셨기 때문에, 시스템콜을 호출을 통해서가 아니고 직접 커널 데이터를 관리해주셔야 하는 것입니다.
그리고 참고~ 재부팅하시지 마시고, 그냥 umount 만 하시고 다시 mount 하셔도 됩니다 ^^
==========================
별은 바라보는 자에게 빛을 준다
==========================
별은 바라보는 자에게 빛을 준다
와우..
tangtung님 감사드립니다. 정말 invalid_inode_pages()하나 추가해주니 깔끔하군요..
마지막으로 하나 궁금한것이, i_data[12]~i_data[14]는 각각 indirect 공간인데,
i_data[12]=100이라 가정했을대, 저 100이란 value로 어떻게 1-indirect i_data[]를 얻을 수 있을지 궁금하네요.
소스를 3시간을 뒤져도 눈에띄는 call이 없어서요.
댓글 달기