사용하는 OS 환경에서의 default compilers와 linkers는 그 OS에서 동작할 실행 파일을 빌드할 수 있도록 구성되어있다. 이를 native tools이라고 한다. 하지만, 꼭 build를 위해 native tools를 사용하지 않아도 된다. cross-compiler는 build하는 환경(host)의 code가 아닌 run할 환경(target)의 code를 build한다. 이를 cross-compilation이라 한다.
Raspberry Pi kernel의 cross-compilation은 두 가지 이유에서 유용하다.
1. 32bit OS에서 64bit kernel을 build할 수 있고 반대도 가능하다.
2. 일반 노트북에서도 Pi에서 보다 더 빠르게 cross-compile할 수 있다.
아래의 가이드는 native builds와 cross-compilation으로 구분된다. 읽는 이의 상황에 맞게 적절한 section을 선택한다. - 두 방법 사이엔 많은 공통점이 있지만 몇 가지 중요한 차이점도 있다.
Building the Kernel Locally
먼저 Raspberry Pi OS의 최신 버전을 설치한다. Pi를 boot하고 source에 접근할 수 있게 Ethernet을 연결한다.
Pi에서 log-in후 git과 build dependencies를 설치한다.
sudo apt install git bc bison flex libssl-dev make
위의 "git clone" 명령은 현재 active branch를 다운로드한다(without any history). "--depth=1" 옵션을 생략하면 모든 repository를 다운로드한다(full history of all branches). 하지만 이경우 더 많은 시간과 용량이 필요하다.
kernel을 설정한다. 때때로 더 상세하게 설정하거나 필요한 기능을 add or remove하기 위해 다른 source의 patches를 적용하고 싶을 수 있다.
- Apply the Default Configuration
사용하는 Raspberry Pi version에 따라 다음의 명령을 실행하여 default configuration을 준비한다:
For Raspberry Pi 1, Pi Zero, Pi Zero W and Comute Module default build configuration
cd linux
KERNEL=kernel
make bcmrpi_defconfig
For Raspberry Pi 2, Pi 3, Pi 3+ and Compute Module 3 default build configuration
cd linux
KERNEL=kernel7
make bcm2709_defconfig
- Customising the Kernel Version Using LOCALVERSION
설정한 kernel이 upstream kernel과 같은 version string을 사용하지 않도록 LOCALVERSION 변수를 사용할 수 있다. 이를 통해 "uname" 명령의 출력 결과에서 사용중인 kernel이 내가 build한 kernel임을 알 수 있고 내가 만든 modules가 "/lib/modules"의 기본 modules를 덮어쓰지 않을 수 있다.
휴대폰, 서버, 네트웍 장비, 전기차, IoT 등 많은 곳에서 리눅스를 사용한다. 데스크탑의 대세는 windows지만 그 외 분야에선 linux가 갑이다. 왜 그럴까? 공짜니까, 소스를 오픈해주니까.
리눅스 커널을 공부하면 파일 시스템, 메모리 관리, 네트워크 등 기술을 구현하는 방법을 알 수 있다. 공부 방법은 두 가지다. 1) 실행한다 2) 코드를 분석한다.
역사
옛날엔 컴퓨터가 싱글 스레드였다. 멀티 스레드를 만들어 보려고 노력을 많이했다. 60년대 AT&T, MIT, GE에서 Multics를 만들었다. 하지만 Multics는 실패했고 이후 멀티 스레드를 지원하는 UNIX를 만든다.
그런데 UNIX를 어셈블리어로 만들어서 CPU Architecture(x86, amd64, ARMv7 등)가 다르면 그에 맞는 UNIX를 다시 개발해야했다. 즉, 호환성 문제가 있었다. 70년대 데니스 리치가 C언어를 만들었다. 이식성, 호환성 문제를 해결했다.
AT&T회사의 벨 연구소에서 만든 UNIX로 회사는 장사를 하려했으나 당시 전화부터 전자, 컴퓨터까지 독점하던 대기업 AT&T는 반독점 소송을 당해 7개의 회사로 쪼개지고 컴퓨터 사업을 할 수 없게된다. 만든 UNIX는 여러 회사에 돈을 받고 팔았는데 그 결과 변종 버전의 UNIX가 많이 탄생한다.
이에 IEEE(전기 전자 기술자 협회; Institute of Electrical and Electronis Engineers)에서 서로 다른 UNIX OS의 공통 API를 정리하여 표준을 만들었다. 그게 POSIX다. (이식 가능 운영 체제 인터페이스; Portable Operating System Interface) 이는 미국의 정부기관에서 컴퓨터 시스템 도입조건으로 요구하기 때문에 windows도 지키는 표준 규격이다.
AT&T로부터 구매해서 자체 개발한 여러 UNIX는 유료였다. 공짜 버전의 UNIX를 만들어보자는 취지로 리처드 스톨만(Richard Matthew Stallman)을 중심으로 사람들이 모여 GNU(Gnu is Not Unix)라는 단체와 FSF(Free Software Foundation)라는 재단을 만든다. 유닉스는 유틸리티 + 커널이다. GNU는 HURD라는 이름의 커널을 만들고 있었는데 진행이 잘 안됐다.
90년대 리누스 토발즈(Linus Tovalds)라는 대학생이 개발한 무료 OS를 GNU에서 GNU 커널로 채택한다. 공짜 UNIX, Linux가 탄생한다. 공짜고 소스가 오픈되어 있으니 사람들이 찾아서 많이 발전했다.
리눅스는 누구나 코드를 작성하고 배포할 수 있다. 다양한 버전의 리눅스가 있다. (Ubuntu, Fedora, Mint, 등)
리눅스는 다양한 개인과 단체가 함께 개발한다.
CPU 회사는 리눅스 커널 중 CPU에 따라 구현이 달라지는 부분을 개발한다. (시스템 콜, 익셉션, 컨텍스트 스위칭)
SoC(System on Chip) 집적회로 회사는 CPU 회사로 부터 toolchain을 제공받아 본인 회사의 제품에 맞게 리눅스 커널 코드를 수정하고 드라이버를 추가한다.
보드 회사는 SoC회사로 부터 리눅스 코드(커널 + 드라이버)를 제공받아 임베디드 리눅스를 개발한다.
QML과 Qt Quick이 제공하는 이점에도 불구하고 어떤 상황에선 문제가 발생하기도 한다. 다음 sections에선 몇 가지 the best practices를 설명한다. 이는 applications 개발에 도움이 될 것이다.
Custom UI Controls
유연하고 세련된 UI는 오늘날 application 성공의 key이다. QML은 이를 만족한다. Qt는 세련된 look의 UI를 만드는데 필요한 기본 UI controls을 제공한다. Custom UI control을 만들기 전에 기본 UI controls를 확인하기를 권한다.
Qt Quick 자체에서 제공되는 기본 UI controls 외에도, Qt Quick Controls2가 제공하는 다양한 UI controls set이 있으며 별다른 변경없이 대부분의 common use cases를 커버할 수 있다 더불어 customization도 가능하다. 특히, Qt Quick Controls 2는 styling options를 제공하는데, 이는 최신 UI design trends를 따른다. UI controls가 needs를 충족하지 못할 경우에만 custom control을 만들 것을 권한다.
-> QML 칭찬. 유연하며 세련되고 다양한 기본 UI controls가 제공되는데 이걸로 어지간한 것은 다 만들 수 있다. 싫으면 customization도 가능하다.
Bundle Application Resources
대부분 applications는 풍부한 user experience를 제공하기 위해 images나 icons같은 resources을 사용하는데 종종 target OS에 따라 resources를 사용할 수 없는 문제가 발생한다. 대중적인 OS는 엄격한 보안 정책을 가지며 file system 접근을 제한한다. 이 때문에 Resources를 load하기가 어렵다. Qt는 해결방안으로 독자적인 resource system을 제공한다. Resources를 application binary안에 built-in 시키는 것이다. 결과적으로 target OS에 관계없이 resources에 접근할 수 있다.
예를 들어, 아래 directory structure에 대해 생각해보자.
.pro 파일에 다음 구문을 작성하여 resources가 built-in 되도록 한다.
더 편리하게 wildcard syntax를 이용해서 여러 파일을 한 번에 추가할 수도 있다.
이 방법은 resources가 몇 개 안될 때 편리하다. 하지만, new file이 RESOURCES에 추가될 때 마다 RESOURCES에 있는 모든 the other files 또한 recompile해야한다. 이는 files를 많이 가지고 있는 application일수록 비효율적이다. 해결 방법은 resource type에 따라 다른 qrc file로 분리하는 것이다. 예를 들어, 위의 snippet은 아래와 같이 바꿀 수 있다.
(* qml.qrc, images.qrc)
이제 QML file이 변경될 때마다 변경된 파일들만 recompile하면 된다.
때때로 resource system에서 관리하는 특정 file에 많은 control이 필요할 때가 있다. 예를 들어, image2.png 파일에 alias가 필요하다면 명시적인 qrc file로 전환할 필요가 있다. 이와 관련한 상세 내용은 Creating Resource Files에서 확인한다.
Separate UI from Logic
많은 application 개발자의 목표 중 하나는 관리가 용이한 application을 만드는 것이다. 이를 이루는 방법 중 하나는 user interface를 business logic으로부터 분리하는 것이다. 아래에서 applications의 UI를 QML로 작성해야 하는 몇 가지 이유를 설명한다.
> UI 정의엔 declarative languages가 매우 적합하다.
> QML code는 C++보다 구문이 짧고 type에 엄격하지 않기 때문에 쓰기가 쉽다. 이는 또한, prototype 작성에 좋고 designer와 협업하기에 좋다.
> QML에서 java script를 쉽게 사용할 수 있어 Events에 respond할 수 있다.
Type에 엄격한 C++은 application’s logic에 적합하다. 이는 일반적으로 QML보다 C++에서 더 빠른 복잡한 계산이나 데이터 처리와 같은 작업을 수행한다.
Qt는 QML과 C++ code를 integrate하기 위한 다양한 접근법을 제공한다. 전형적인 use case로는user interface에 data list를 보여주는 case가 있다. 만약 data set이 static, simple, small 하다면 QML에서 작성한 model로도 충분하다.
다음 snippet은 QML에서 model을 작성한 예시이다.
Data set의 크기가 크거나 자주 변경된다면 C++을 사용해라.
Interacting with QML from C++
비록 Qt는 C++에서 QML을 조작할 수 있도록 허용하지만 꼭 그렇게 해야만 하는 것은 아니다. 이유를 설명하기 위해 간단한 예를 확인해보자.
Pulling References from QML
Settings page를 위한 UI를 아래와 같이 작성했다고 가정한다.
예시의 button이 click될 때 C++에서 임의의 처리를 하기를 원한다고 하자. C++의 objects처럼 QML의 objects도 change signals를 emit할 수 있다. C++에서 위의 button object를 찾기 위해 button에 “objectName”을 할당해야 한다.
그러면, C++에서 object를 찾을 수 있고 object의 change signal에 callback을 연결할 수 있다.
이 방법으로 QML objects의 reference를 “pulled”할 수 있다. 이 방법의 문제점은 C++ logic layer가 QML presentation layer에 의존한다는 점이다. 만약, QML을 refactoring하는 과정에서 “objectName”이 변경되는 등의 결과로 C++에서 QML object를 찾는 구문이 깨질 수 있다.
-> 위 예제 실행시 에러가 난다. key는 아래의 #include "main.moc"이다. QObject를 상속받은 class를 별도 파일로 구현하면 에러가 나지 않는다.
Pushing References to QML
QML Refactoring이 C++ Refactoring보다 쉽다. 그래서 유지보수의 고통을 줄이기 위해 C++ types이 QML types을 모르게 만들어야 한다. 이는 C++ types의 references를 QML로 “pushing”하는 방법으로 해결할 수 있다.
이후 QML에서 C++의 slot을 직접적으로 호출하게 한다.
이 방법으로 C++는 나중에 QML이 refactoring하더라도 unchanged 할 수 있다.
위의 예시에선 C++ object를 QML에 push하기위해 root context의 context property로 해당 object를 set했다. 이렇게 하면 the engine에 의해 load되는 모든 components에서 해당 property를 사용할 수 있다. QML이 load되자마자 사용 가능해야 하는 경우 context properties를 유용하게 사용할 수 있다. 이는 QML에서 instantiated되면 안 된다.
Qt는 layout안에 있는 Qt Quick items을 배치하기 위해 Qt Quick Layouts를 제공한다. item positioners와 다르게 Qt Quick Layouts는 window resize시에 children도 resize할 수 있다. 이는 대게 좋은 선택지가 되지만 사용시 아래와 같은 참고 사항이 있다.
Dos
> Layout item의 size를 정해야 하는 데 parent가 Non-Layout이라면 anchors, width, height properties를 사용하라.
> Layout 바로 아래의 children을 배치하기위해 property를 attach한 Layout을 사용해라.
> Layout 바로 아래의 children에 anchors를 사용하지 마라. 그 대신, Layout.preferredWidth, Layout.preferredHeight을 사용하라.
>
Note: Layouts과 anchors는 instantiation시 더 많은 memory와 시간을 필요로 한다. List나 table delegates, styles에서 이런 것을 사용하지 않도록 유의하라. 이경우 간단하게 x, y, width, height properties를 사용하는 것이 좋다.
Type Safety
QML에서 properties를 선언할 때 “var” type을 사용하면 쉽고 편하다.
하지만, 이런 사용은 몇 가지 단점이 있다.
> 만약, 변수에 잘못된 type의 값이 할당되면 error report는 사용문이 아닌 선언문을 가리킨다. 즉, 오류 추적이 어렵다.
Display resolutions가 향상될수록 scalable application UI는 더욱 중요하다. 이를 위한 한 방법은 screen resolutions마다 UI의 copies를 준비해서 가능한 resolution에 따라 적절한 UI를 load하는 것이다. 이는 비록 잘 동작하겠지만 유지보수에 overhead를 더한다.
Qt는 더 나은 solution을 제공하며 이를 따를 것을 권한다.
> Qt Quick Layouts module이나 anchors를 사용하라.
> Visual item에 명확한 width, height의 값을 설정하지 마라.
> Application이 지원하는 display resolution 마다 images와 icons 같은 resources를 제공하라. Qt Quick Controls 2 gallery example은 qt-log.png 파일을 @2x, @3x, @4x resolutions 마다 제공한다. 이는 application이 더 높은 품질의 해상도를 제공할 수 있도록 한다. Qt는 high DPI scaling feature를 명시적으로 enable한 경우 자동으로 주어진 display에 적합한 image를 선택한다.
> 작은 icon은 SVG images를 사용하라. 큰 이미지를 SVG로 하면 render 속도가 느리지만 작은 이미지는 괜찮다. Vector 이미지와 Bitmap 이미지는 resolution에 따라 여러 이미지를 제공할 필요가 없다.
> “Font Awesome”과 같은 font-based icons를 사용하라. 이는 모든 display resolution에 따라 크기 변환이 가능하며 colorization도 가능하다. Qt Quick Controls 2의 Text Editor example이 이를 잘 보여준다.
설계의 주목적은 모듈형식의 카메라 앱을 만드는 것이다. 비디오 녹화, 이미지 캡쳐 같은 기능을 모듈 안에 캡슐화한다. 모듈은 쉽게 앱에 추가 삭제할 수 있다. 'Dispatcher'라는 중앙 객체를 만들어 현재 장치, 센서 모드와 같은 여러 상태를 관리하도록 한다. dispatcher는 모듈에서 argus를 간단하게 사용할 수 있도록 기능을 제공한다.
디렉토리 구조
common
- mutex, timing, conditional 변수와 같은 공유 기능. 'sample/utils' 디렉토리에도 공유되는 코드가 있다.
docs
- doxgen 문서
modules
- xml 설정 파일을 읽고 쓰는 기능, 퍼포먼스 확인 기능 등 기능적 개체들 (dispatcher 포함)
jetson multimedia api package는 유연한 앱 개발을 위해 low level apis를 제공한다.
* 카메라 앱 api : libargus는 저수준 프레임 동기화 api다. 프레임별 파라미터 제어, EGL stream outputs, 그리고 여러 대의 카메라를 사용할 수 있게 지원한다(동기화 포함). ISP가 필요한 raw output CSI 카메라는 libargus 또는 gstreamer plugin을 사용할 수 있다. 두 경우 모두 V4L2 미디어 컨트롤러 센서 드라이버 api가 사용된다.
* 센서 드라이버 api: V4L2 프레임웍은 video decode, encode, format conversion, scaling functionality를 할 수 있다. 예를들어, video encode 관점에서 V4L2는 bit rate control, quality presets, low latency encode, temporal tradeoff, motion vector maps 기타 등등 많은 기능을 제공한다.
1.3 samples
각 jetpack components 별로 사용법을 알 수 있는 샘플들이 제공된다. 각 샘플들은 reference file system에 포함되 있으며 컴파일도 가능하다. 컴포넌트별 샘플 위치는 아래와 같다.
1.4 developer tools
jetpack은 앱 개발, 디버깅, profiling, 최적화를 위해 developer tools를 제공한다. jetson system과 연결된 linux host에서 동작하는 tools도 있고 직접적으로 jetson system 위에서 동작하는 tools도 있다.
앱 개발과 디버깅을 위한 도구들:
* nvidia Nsight eclipse edition (GPU accelerated 앱 개발을 위한).
nvidia nsight eclipse edition (bundled with cuda toolkit)은 eclipse platform에서 동작하는 강력한 ide다. 코드 편집, 크로스 컴파일, debug cuda-C applications 를 위한 all-in-one 통합 환경을 제공한다. 유료 plugin, 무료 plugin 등 이것저것 많이 제공한다. linux host 위에서 동작하며 모든 jetson 제품을 지원한다.
* cuda-gdb (앱 디버깅을 위한).
command line tool이다. 앱을 동시에 cpu, gpu 측면에서 모두 디버깅할 수있다?! linux host와 jetson system 에서 모두 동작시킬수 있다.
* cuda-memcheck (앱의 메모리 에러를 디버깅하기 위한).
command line tool 이다. your gpu code에서 memory access error의 원인을 감지할 수 있다. 또한 runtime errors와 unspecified launch failure인 상황을 식별할 수 있다. jetson system 위에서 동작한다.
앱 pofiling과 최적화를 위한 도구들:
* nvidia nsight systems (cpu, gpu 앱 profiling을 위한).
소프트웨어 퍼포먼스를 분석하고 최적화할 수 있도록한다. gpu tracing과 cpu sampling, tracing 그리고 os thread state tracing을 한다. 이를 통해 앱의 알고리즘을 시각화할 수 있고 코드 최적화를 돕는다. linux host 위에서 동작한다.
* nvprof (cpu, gpu 앱 profiling을 위한).
command line tool 이다. view profiling data를 수집할 수 있도록한다. cpu, gpu 에서 cuda와 관련된 활동들의 timeline이다. jetson system 위에서 동작한다.
* visual profiler (cpu, gpu 앱 profiling을 위한).
deprecated 이다. cpu, gpu의 sampling, tracing을 위해서는 nvidia nsight systems 그리고 cuda kernel profiling을 위해서는 nsight compute 의 사용을 권장한다. linux host 위에서 동작한다.
* nvidia nsight graphics (graphics app 의 디버깅과 profiling을 위한).
graphics apps 를 디버깅하고 profiling하기 위한 standalone 앱이다. 최적화를 위한 강력한 도구다. linux host 위에서 동작한다.
* nvidia nsight compute (interactive cuda kernel profiling을 위한).
cuda apps를 위한 interactive kernel profiler이다. 자세한 performance metrics를 제공하며 baseline과 current 간의 결과를 비교할 수 있다. post-processing results를 위해 analysis scripts로 확장할 수 있다. linux host 위에서 동작한다. jetson agx xavier와 jetson xavier nx를 지원한다.
* nvidia nsight compute cli (non-interactive cuda kernel profiling을 위한).
non-interactive kernel profiler이다. 결과를 커맨드라인에 바로 출력하거나 파일에 저장할 수 있다. linux host와 jetson system 에서 모두 동작시킬수 있다. jetson agx xavier와 jetson xavier nx를 지원한다.
Shell 이란? 리눅스 커널과 사용자 간의 가교 역할을 하는 소프트웨어로 본쉘, C쉘, 콘쉘, bash, dash 등 다양한 쉘이 있다. 일반적으로 우분투의 경우 사용자는 bash 쉘, 시스템은 dash 쉘을 사용하도록 기본 설정되어 있다.
* 사용자 쉘 확인
echo $SHELL cat /etc/passwd
* 시스템 쉘 확인
ls -l /bin/sh
* 사용자 쉘 변경
/etc/passwd 파일의 내용을 수정하거나 chsh(change shell)명령을 이용한다. 변경후엔 재시작을 해야 적용된다.
* 사용 가능한 Shell 목록 확인
cat /etc/shells
Shell은 크게 3가지 역할을 한다. (1. 명령어 해석기 기능, 2. 프로그래밍 기능, 3. 사용자 환경 설정 기능) Shell 의 종류에 따라 차이가 있으나 대개의 경우 shell 실행 후 사용자에 맞는 환경을 구성할 수 있도록 초기화 파일을 제공한다. 따라서 새롭게 로그인 할 때 마다 적용하고 싶은 명령이나 변수, 설정 등이 있다면 해당 파일에 적어 놓으면 된다. 초기화 파일은 크게 2가지로 시스템 환경 설정 파일과 사용자 환경 설정 파일로 나눌 수 있다. 시스템 환경 설정 파일은 전 사용자 공통 환경 설정 파일이며 /etc/ 에 위치한다. 사용자 환경 설정 파일은 개별 사용자의 환경을 설정하며 해당 사용자의 홈 디렉터리에 있다. 대표적인 bash shell의 환경 설정 파일은 아래와 같다.
* 시스템 환경 설정 파일
/etc/profile
각 파일, 디렉터리의 존재 유무를 확인후 실행한다. (/etc/bash.bashrc, /etc/profile.d/*.sh 실행)
vi는 텍스트 편집기입니다. 메모장이나 한글, 워드 같은 친숙한 편집 프로그램을 떠올리면 됩니다. 우리는 편집기에서 원하는 곳으로 커서를 이동하고 텍스트를 쓰고, 지우는 등의 일을 합니다. 가끔은 어떤 문자열을 검색해서 다른 문자열로 한꺼번에 치환하기도 합니다. 일반 편집기에서 하는 일을 vi로 할 수 있으면 vi를 쓸 줄 아는 것입니다.
vi는 세가지 모드를 제공합니다. 1. 명령모드, 2. 편집모드, 3. 마지막행 모드 입니다. 처음 vi 를 실행하면 명령모드로 실행됩니다. 명령모드에선 키보드의 키를 누르면 특정한 동작을 하도록 약속되어있습니다. 예를 들어 'x' 를 입력하면 커서가 있는 곳의 문자를 하나 지우고 'ZZ' 를 입력하면 작업중인 파일을 저장하고 닫습니다. 글을 편집하려면 편집 모드로 이동해야합니다. 당연히 편집 모드로 이동하기 위한 약속된 키가 있습니다.
vi를 처음 접하면 모드에 대한 개념이 없어서 알파벳을 눌렀는데 글이 안나오고 난데없이 문자가 삭제되거나 커서가 이동되니 당황하게되고 어렵다고 느낍니다. 하지만 vi는 CUI(Character User Interface) 환경에서 사용할 수 있는 좋은 편집기입니다. 알아두면 좋습니다. 모드간 전환하는 키는 아래 그림과 같습니다.
1. 편집
위 그림처럼 명령 모드에서 편집모드로 전환하는 키가 여러가지인 것은 현재 커서 위치를 기준으로 어디에서 편집을 시작할 것인지 정하기 위함입니다. 명령모드에선 커서가 한 문자의 위에 위치합니다. 편집모드가 되면 문자와 문자사이로 이동합니다.
위 그림에서 현재 커서는 별모양 아래 this 의 s 문자 위에 있습니다. 따라서 편집을 시작한다면 s 문자의 왼쪽에서 시작할건지 오른쪽에서 시작할건지를 알려줘야합니다. 명령모드에서 대소문자 i, a 와 I, A 키를 누르면 편집모드로 전환하며 위의 그림처럼 현재 커서를 기준으로 바로 앞, 바로 뒤, 현재 행의 맨 앞, 맨 뒤에서 편집을 시작할 수 있습니다. 대소문자 o와 O는 현재 행의 바로 밑 또는 위에 빈 행을 만들고 그 행의 처음부터 편집을 시작합니다. 당연하게도 편집모드에서 키보드의 문자키 입력은 바로바로 모니터에 출력됩니다. 명령모드로 돌아갈 때는 ESC 키를 누릅니다.
2. 커서 이동
원하는 위치에서 편집을 시작하려면 명령모드에서 커서의 위치를 이동하는 방법을 알아야합니다. 글자, 단어, 페이지 단위 등 다양한 방법의 커서이동이 가능합니다. 원하는 행의 시작 위치로 커서를 바로 이동시킬 수도 있습니다.
h, j, k, l 키는 한 글자만큼 왼쪽, 아래, 위, 오른쪽으로 커서를 이동합니다. vi 버전에 따라 방향키를 지원하기도 합니다. 단어간 커서 이동은 b, w, e 키를 이용합니다. 페이지 단위 커서 이동은 PgUp, PgDn 키를 이용하면 편리합니다. Ctrl + f, b 는 PgDn, PgUp 키와 같은 기능입니다. Ctrl + u, d 는 한 페이지의 절반만큼 위아래로 커서를 이동합니다. 임의의 행으로 이동하고자 할 땐 행번호를 누르고 G를 입력하면 됩니다. 예를 들어, 1행으로 이동하고자 할 경우 1G 를 입력합니다. 파일의 맨 끝으로 이동하고자 할 땐 그냥 G를 누르면 됩니다.