2018/04/30

Ubuntu 18.04 LTS 설치


지난 주에 우분투 18.04 LTS가 배포됐다. 향후 5년간 지원될 버전이니 이것 만으로도 18.04로 갈아탈 충분한 이유가 될 수 있다. 다만, 17.10 사용자라면 17.10에서 많은 변화가 있었기에 18.04에서 달라진 걸 크게 느끼지 못할 것이다. 원래는 Upgrade하려고 Sofware Update를 실행했는데 뭐가 문제인지 부분 Upgrade를 시킬 것이란 경고가 떴다. 찜찜해서 그냥 하드디스크의 iso 설치 이미지를 이용한 Clean install을 감행했다. 그런데, iso 이미지로 부팅시 오류가 발생해서 부팅이 안된다. iso 이미지 파일 내의 리눅스 커널 명이 vmlinuz.efi에서 vmlinuz로 다시 바뀌었기 때문이다. efi를 지원하면서 vmlinuz.efi로 바뀌었었는데 이제 보편화됐다고 생각한 건지 다시 vmlinuz로 돌아왔다. 다만, Release Note에서 변경 사항이 누락된 점은 아쉽다.

우분투 설치 iso Grub boot menu entry 수정

아래의 예와 같이 Grub boot menu entry에서 linux (loop)/casper/vmlinuz.efi ... 부분을 수정해 주어야 한다. USB에서 우분투 설치 iso 파일을 사용해 설치할 경우에도 커널 명을 수정해 주어야 한다.
menuentry "HDD Ubuntu 64-bit iso" {
   set isofile="/boot-isos/ubuntu-18.04-desktop-amd64.iso"
   loopback loop (hd0,9)$isofile
   linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject
   initrd (loop)/casper/initrd.lz
}
하드디스크의 우분투 iso 파일을 이용하는 경우에는 grub.cfg 파일을 직접 수정하는 것이 아니므로,

$ sudo update-grub

명령을 실행한 후 재부팅해야 한다. 자세한 내용은 이전 글을 참고하는 것이 좋다.

우분투 18.04의 새로운 점들

리눅스 커널 4.15가 채택되었다. 보안 이슈 해결이나 새로운 하드웨어 지원을 위해 필요한 부분이다. 우분투 자체적으로는 커널 4.0 이후 지원되는 Kernel Live Patching 기능을 Ubuntu One을 통해 지원한다. 우분투 설치시에 사용할 지 물어 본다.

Wayland 대신 X 서버가 다시 기본 display server가 되었고 Wayland는 Option이 되었다. Wayland를 지원하는 앱들이 부족해서 LTS 버전에 적합하지 않다고 판단한 것이다. 원격 데스크탑이나 화면 및 비디오 캡춰 소프트웨어 등이 Wayland 환경에서 동작하지 않는 문제가 대표적이다. 안정성 측면에서도 아직 Wayland를 사용하기에는 이르다는 생각이다.

설치 시에 minimal install 옵션을 제공하는 것도 달라진 점이다. 설치 iso 이미지가 우분투 17.10은 1.5GB였는데 18.04는 1.9GB가 되었다. 아무튼 Web browser와 핵심 시스템 유틸리티만 설치된다고 한다.

Gnome Shell 3.28을 채택했다. 다만 File 관리자(Nautilus)는 구 버전을 customize 했는데 최신 버전에서 Desktop Icon을 지원하지 않기 때문이다. LTS 버전이라 사용자들에게 급격한 변화를 강요하지 않으려는 관점이 들어간 건데 Gnome을 채택했으면 그대로 가져다 쓰는 것이 더 낫다는 관점도 수용할 필요가 있어 보인다. Gnome 3.28은 Thunderbolt 3 기기들을 지원한다. Gnome Shell의 Ubuntu Dock extension에서 앱 아이콘을 click하면 앱이 실행되고 다시 click 하면 minimize가 안되는데 아래와 같이 설정하면 minimize 기능을 사용할 수 있다.

$ gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize'

그런데 위 명령은 오류가 발생했고, dconf-editor를 사용해서 위의 key 값을 변경해 주면 잘 된다.

Color 이모티콘(Emoji)을 지원한다. 우분투 17.10부터 흑백 이모티콘을 지원하기 시작했다. Characters 앱이 이전의 Character Map 앱을 대체했는데 Color 이모티콘을 보다 원활히 지원하려는 의도일 수 있다.

우분투 Software 앱에서 Snap 앱들에 대한 지원을 강화했다. Calculator, Charaters, Logs, System Monitors 앱들은 Snap 앱으로 설치된다.

$ ls -l /snap

이 밖에도 Calendar 앱에서 날씨 예보를 지원하고, To Do 앱이 추가됐다.

또한, 17.10 이후 systemd 로그를 지원하기 위해 Logs 앱이 System Log를 대체했고, Disk Usage Analyzer, Files(Nautilus), Remmina, Settings, Ubuntu Software 앱 들의 UI가 새로운 디자인으로 바뀌었다.

새로운 점들에 대한 더 자세한 내용은 우분투 18.04 Release Note를 참고하기 바란다.

한글 입력기 및 fonts

기본 입력기인 ibus를 사용하거나 fcitx-hangul을 설치해서 사용하면 된다. 우분투 17.10과 설치 방법은 동일하다. ibus 입력기의 경우 Gnome Top Panel에 한글 입력기를 표시하거나 제거하는 올바른 방법은 Settings > Region& Language > Input Sources > + 또는 - [버튼] 으로 Korean (Hangul)을 추가하거나 삭제하면 된다. 다만 fcitx-hangul을 설치할 경우 여전히 ibus 프로세스가 살아 있는 문제는 남아 있으므로 /usr/bin/ibus-daemon 파일을 rename해 줄 필요가 있다.

한글 폰트는 Noto Sans 폰트가 기본 폰트가 되면서 나눔 글꼴 패키지가 기본으로 설치되지 않더라. 아래와 같이 설치할 수 있다.

$ sudo apt install fonts-nanum fonts-nanum-coding fonts-nanum-extra

그런데 우분투 전반적으로 폰트가 커지고 bold체가 강해진 느낌이라 한글 Web Site들이 좀 불편해졌다. firefox 폰트 설정에서 "Allow pages to choose their own fonts, instead of your selections above" 옵션을 끄면 내가 설정한 폰트를 사용할 수 있다.


2018/01/04

c++ Delegate 내지는 Callback


바퀴를 발명하지 말라는 격언이 있지만 공부할 때는 바퀴를 다시 발명해 보는 것도 좋은 방법일 수 있다. Qt의 Signals & Slots를 c++에서 쉽게 구현할 수 있는 방법이 있을까 궁금해서 찾다가 15년이 다 돼가는 아주 오래된 글이지만 옷깃으로 눈물을 훔칠 만큼 감동적인 글을 보게 되었다.

FastDelegate 이란 건데 같은 codeproject 사이트에서 Delegate로 검색해 보니 FastestDelegate로 알려질 만큼 유명한 글이었다. 자세한 설명은 링크와 소스 코드를 보는게 좋다. c++11 버전으로 내가 이해할 수 있게 다시 각색해 보았다. 다만, 원래 소스는 대부분의 c++ compiler를 지원하지만 내가 각색한 소스는 g++에서만 동작할 수도 있다. 특히, 원저자가 발명한(?) horrible_cast는 해커들이나 쓰는 방식이다.  적어도 Delegate에 관한 한 원저자가 주장하듯이 표준 c++을 따르는것 보다 portable code가 더 중요하다는 관점에 동의하지 않을 수 없다. 참고로, 표준 c++을 따르는 Delegate에 관한 글과 이를 modern c++로 구현한 글도 codeproject 사이트에 올라와 있다. 표준 방식의 문제는 과도한 template 사용으로 인해 사용자 인터페이스가 많이 불편하다는 것이다.

문제의 근원은 표준 c++ class의 member function pointer가 일반 pointer와는 달리 특정한 address를 갖지 못한다는데 있다. 구글링하다 보니 누군가 c++20에 넣어 달라는 글도 보이긴 하더라. 표준 c++에 Qt의 Signals & Slots를 넣어 달라는 요청도 번번히 거절 당해 왔는데 그 이유는 표준 방식으로도 Observer Pattern이나 Delegate Pattern을 사용해서 구현할 수 있다는 것이었다. 실상은 Signals & Slots와 같이 범용적으로 쓰기에는 제약사항이 많다.

여기서 Delegate는 Callback 함수와 동일한 개념이다. 표준 방식으로 std::function을 이용해서 구현할 수도 있는데 memory allocation이 사용되는 경우 느려질 수 있고, Signals & Slots와 같이 event callback에 사용하기에는 어려운 점이 많다. 참고로 Qt는 meta object compiler(moc)를 사용해서 Signals & Slot을 구현했다. Qt framework을 사용할 수 없는 경우에 표준 c++로 Signals & Slots를 구현하기란 쉽지 않은 일인 것이다.

아무튼, FastDelegate를 이용하면 Signals & Slots를 한결 수월하게 구현할 수 있을 듯 하다. 참고로, FastDelegate은 일반 함수(static function)와 class member 함수의 Callback을 모두 지원한다. 다만, 당시에는 lambda나 variadic template이 표준 c++이 아니었는데 variadic template 지원 부분은 추가 되었고, lamda의 경우엔 capture가 없으면 사용할 수 있다. 가령, 아래의 예와 같이 사용할 수 있다.

// lambda example : no captures only
  Delegate<double(int, double)> d1([](int i, double x) -> double { return x + i; });
  Delegate<double(int, double)> d2, d3;
  auto lambda = [](int i, double x) -> double { return 2*x + i; };
  d2 = lambda;
  d3 = [](int i, double x) -> double { return x*x + i; };

  std::cout << d1(10, 3) << " " << d2(5, 6) << " " << d3(1, 1) << "\n";
// Rewrite FastDelegate.h by Don Clugston for g++ only(unportable).
// Original FastDelegate is portable to almost all the compilers.
// See http://www.codeproject.com/cpp/FastDelegate.asp for more information.

namespace HIDDEN
{

class TGeneric;
using TGenericP = TGeneric*;
using TGenericPvMF = void(TGeneric::*)();

class Memento
{
public:
  Memento() = default;
  Memento(const Memento& rhs) : m_object(rhs.m_object), m_pmf(rhs.m_pmf) {}
  Memento& operator=(const Memento& rhs)
  {
    m_object = rhs.m_object;
    m_pmf = rhs.m_pmf;
    return *this;
  }
  
  bool operator!() const { return !m_object && !m_pmf; }
  bool operator==(const Memento &rhs) const
    { return m_object == rhs.m_object && m_pmf == rhs.m_pmf; }
  bool operator<(const Memento &rhs) const
  {
    if(m_object != rhs.m_object) return m_object < rhs.m_object;
    return std::memcmp(&m_pmf, &rhs.m_pmf, sizeof(m_pmf)) < 0;
  }
  bool operator>(const Memento& rhs) const { return rhs.operator<(*this); }
  size_t hash() const
    { return reinterpret_cast<size_t>(m_object) ^ unsafe_cast<size_t>(m_pmf); }

protected:
  TGenericP m_object{nullptr};
  TGenericPvMF m_pmf{nullptr};
  
private:
  template<class TO, class TI>
  static TO unsafe_cast(TI in)
  {
    union { TO out; TI in; } u;
    u.in = in;
    return u.out;
  }
};

template<typename TGPMF, typename TPF>
class Closure : public Memento
{
public:
  TGenericP object() const  { return m_object; }
  TGPMF pmf() const  { return reinterpret_cast<TGPMF>(m_pmf); }
  TPF function() const { return horrible_cast<TPF>(this); }

  template<class TB, class TPMF>
  void bind(const TB* pobj, TPMF pmf)
  {
    static_assert(sizeof(TGenericPvMF) == sizeof(pmf), "Unsupported conversion");
    m_pmf = reinterpret_cast<TGenericPvMF>(pmf);
    m_object = reinterpret_cast<TGenericP>(const_cast<TB*>(pobj));
  }

  template<class TB, class TPMF>
  void bind(TB* pobj, TPMF pmf, TPF pf)
  {
    if(!pf) { m_object = nullptr; m_pmf = nullptr; return; }
    bind(pobj, pmf);
    m_object = horrible_cast<TGenericP>(pf);
  }

private:
  template<class TO, class TI>
  static TO horrible_cast(TI in)
  {
    union { TO out; TI in; } u;
    static_assert(sizeof(TI) == sizeof(u) && sizeof(TI) == sizeof(TO),
      "Unsupported conversion");
    u.in = in;
    return u.out;
  }
};

template<typename TR, typename... TArgs>
class DelegateImpl
{
  using TypePF = TR(*)(TArgs...);
  using TypePMF = TR(TGeneric::*)(TArgs...);

public:
  DelegateImpl() = default;
  DelegateImpl(const DelegateImpl& rhs) : m_closure(rhs.m_closure) {}
  template <typename TB, typename TO>
  DelegateImpl(TO* pobj, TR(TB::*pmf)(TArgs... args)) { bind(pobj, pmf); }
  template <typename TB, typename TO>
  DelegateImpl(const TO* pobj, TR(TB::*pmf)(TArgs... args) const){ bind(pobj, pmf); }
  template<class TPF>
  DelegateImpl(TPF pf) { bind(pf); }

  void operator=(const DelegateImpl& rhs) { m_closure = rhs.m_closure; }  
  template<class TPF>
  DelegateImpl& operator=(TPF pf) { bind(pf); }
  bool operator!() const { return !m_closure; }
  bool operator<(const DelegateImpl& rhs) const { return m_closure < rhs.m_closure; }
  bool operator>(const DelegateImpl& rhs) const { return !operator<(rhs); }
  bool operator==(const DelegateImpl& rhs) const
    { return m_closure == rhs.m_closure; }
  bool operator==(TypePF pf) const { return m_closure == pf; }
  bool operator!=(const DelegateImpl& rhs) const { return !operator==(rhs); }
  bool operator!=(TypePF pf) const { return !operator==(pf); }
  TR operator()(TArgs... args) const
    { return (m_closure.object()->*m_closure.pmf())(args...); }

  template <typename TB, typename TO>
  void bind(TO *pobj, TR(TB::*pmf)(TArgs... args))
    { m_closure.bind(implicit_cast<TB*>(pobj), pmf); }
  template <typename TB, typename TO>
  void bind(const TO* pobj, TR(TB::*pmf)(TArgs... args) const)
    {  m_closure.bind(implicit_cast<const TB*>(pobj), pmf); }
  template<class TPF>
  void bind(TPF pf) { m_closure.bind(this, &DelegateImpl::function, pf); }

private:
  template <class TO, class TI>
  static TO implicit_cast(TI in) { return in; }
  TR function(TArgs... args) const { return (*m_closure.function())(args...); }

  Closure<TypePMF, TypePF> m_closure;
};

} // namespace HIDDEN

template<typename T> class Delegate;
template<typename TR, typename... TArgs>
class Delegate<TR(TArgs...)> : public HIDDEN::DelegateImpl<TR, TArgs...>
{
  using TypeBase = HIDDEN::DelegateImpl<TR, TArgs...>;

public:
  using TypeBase::TypeBase;

  Delegate() = default;
  template <typename TB, typename TO>
  Delegate(TO* pobj, TR(TB::*pmf)(TArgs... args)) : TypeBase(pobj, pmf) {}
  template <typename TB, typename TO>
  Delegate(const TO* pobj, TR(TB::*pmf)(TArgs... args) const) : TypeBase(pobj, pmf) {}
  Delegate(TR(*pf)(TArgs... args)) : TypeBase(pf) {}
};

template <typename TR, typename... TArgs>
Delegate<TR(TArgs...)> makeDelegate(TR(*pf)(TArgs...))
  { return Delegate<TR(TArgs...)>(pf); }
template <typename TR, typename TB, typename TO, typename... TArgs>
Delegate<TR(TArgs...)> makeDelegate(TO* pobj, TR(TB::*pmf)(TArgs...))
  { return Delegate<TR(TArgs...)>(pobj, pmf); }
template <typename TR, typename TB, typename TO, typename... TArgs>
Delegate<TR(TArgs...)> makeDelegate(TO* pobj, TR(TB::*pmf)(TArgs...) const)
  { return Delegate<TR(TArgs...)>(pobj, pmf); }
 

2017/10/22

Ubuntu 17.10 설치 및 사용


(2018/1/10 추가)

우분투 Meltdown/Spectre kernel patch 관련

오늘(현지 시간 1월 9일) 우분투의 대대적인 kernel patch가 있었다. 그런데 언제부터인가 우분투가 자동으로 kernel을 update해 버리고 있었다. 재부팅하니 NVIDIA로 로그인이 안된다. <Alt>+<Ctrl>+<F3>로 console 로그인은 되었는데 잠시 후 먹통이 돼 버렸다. 참고로 NVIDIA 상용 드라이버가 정상 동작할 때는 console 로그인을 해도 black screen인데 오늘 같은 경우에는 정상 로그인이 되기 때문에 NVIDIA 드라이버가 로딩되지 않았음을 알 수 있다.

강제 재부팅해서 이전 커널 버전으로 부팅하고 NVIDIA 홈페이지 갔더니 1월 4일자 384.111 버전이 올라왔더라. 내려 받은 후 새 커널(4.13.0-25-generic #29)의 recovery mode로 부팅해서 NVIDIA 새 드라이버 설치하고 재부팅하니 새 커널에서 NVIDIA 로그인이 잘 된다.

구글링 해 보니 역시나 우분투에서 커널과 NVIDIA 드라이버를 동시에 patch 했는데, 내가 우분투에 기본 탑재된 NVIDIA 드라이버를 사용하지 않았기 때문에 문제가 발생한 것인지는 확실하진 않다. 다만 NVIDIA 사용자들의 아우성이 곳곳에 보이더라... 커널 patch 후 PC 성능 저하 문제가 생길지는 두고 봐야할 듯...

참고로, 우분투 자동 update 막는 법도 구글링 해 보니, /etc/apt/apt.conf.d/10periodic 파일에서, 아래의 "1"을 "0"으로 바꾸면 자동 update를 막을 수 있단다.
APT::Periodic::Update-Package-Lists "1";

기타 참고 사항

우분투 17.10에 기본 탑재된 커널이 Lenovo, Acer 등 일부 노트북 기종의 BIOS 셋팅을 저장할 수 없게 만드는 문제가 발생해서 캐노니컬이 우분투 17.10 설치 iso 이미지를 우분투 사이트에서 내렸었는데, 새로운 17.10 설치 iso 이미지가 공식적으로 1월 11일에 올라올 예정이란다. 아마 이번 kernel patch까지 반영될 듯하다.

--------------------------------------------------------------

(2017/11/10 추가)

우분투 17.10에 fcitx 설치

우분투 17.10에서 Gnome-shell이 데스크탑이 되고 나니까 기본 입력기인 ibus와 다른 입력기들이 충돌하는 문제가 생긴다. fcitx를 설치해서 언어 설정에서 입력기를 ibus에서 fcitx로 바꾸더라도 ibus 프로세스들이 살아 있는 문제와 Gnome 상단 패널(또는 Top Bar)에 ibus 설정 icon이 남아 있는 등의 문제가 생긴다.

간단한 해결책은, 1) ibus를 지우고, 2) fcitx-hangul을 설치하고 나서, 3) 언어 설정에서 입력기를 fcitx로 지정하고 4) 재부팅 하면 된다. 마지막으로, 5) gnome-shell이 fcitx를 자동 실행하도록 해두는게 좋다.

$ sudo apt remove ibus
$ sudo apt install fcitx-hangul

1) 에서 ibus 지우기 겁나면 ibus 프로세스가 아예 안뜨도록 하면 되는데 가장 좋은 방법은 아래와 같이 파일명을 바꿔 버려도 된다.

$ sudo mv /usr/bin/ibus-daemon /usr/bin/ibus-daemon.org

그리고, 아래 글에 자세히 다루지 않았었는데 Gnome Tweak Tool을 설치해 두면 여러모로 편리하다. 그리고 Gnome extensions를 사용할 경우에는 chrome-gnome-shell 패키지도 유용하다. 이는 Firefox나 Chrome에서 Gnome extensions를 설치/Update/삭제할 수 있게 해 준다.

$ sudo apt install gnome-tweak-tool chrome-gnome-shell

fcitx는 gnome-shell에서 logout 하고 재로그인 하면 자동으로 기동되지 않는 문제가 있다. Gnome Tweak Tool 에서 [Startup Applications] Tab > [+] 버튼을 누른 뒤 나타나는 "Fcitx"를 선택해서 [Add] 해 주면 된다.

fcitx 설정 방법은 예전과 똑같다. fcitx도 ibus처럼 Wayland 환경에서도 잘 동작한다. 다만, 모든 입력기 들이 진정으로 Wayland를 지원하고 있다고 보기는 어렵다.

우분투 17.10 사용 소감 추가

앞서 얘기한 바 있지만 시스템 안정성 문제 때문에, Wayland를 사용하고 싶으면 Nvidia 등의 상용 비디오 드라이버를 설치하지 않는 것이 좋고, 상용 비디오 드라이버를 사용하고 싶으면 Wayland 대신 Xorg 서버 환경에서 우분투를 사용하는 것이 좋다.

기본 우분투 17.10 환경에서 Ubuntu dock외에 Hide top bar와 Pixel saver 2개의 Gnome extension만 사용해도 이전의 Unity Desktop과 비슷한 느낌을 가질 수는 있게 됐다. 초기의 자잘한 UI 버그들이 수정되어 그럭저럭 안정감있게 쓸만하다.


--------------------------------------------------------------

지난 주에 우분투 17.10이 공식 배포돼서 주말에 설치해서 사용해 보았다. 바뀌는 게 많아서 18.04 LTS 버전이 나오면 Clean Install 할까 했는데 17.04에서 Wayland를 써 보고 나니 17.10에서 얼마나 잘 돌아가나 궁금해서 오랜만에 하드디스크의 iso 파일을 이용해 Clean Install을 감행했다.

우분투 17.10에서 달라진 점들은 우분투의 Release Notes를 참고할 필요가 있다. 몇 가지만 소개하면,
  •  Unity Desktop이 Gnome shell 3.26 Desktop으로 대체 
    • 단, Original Gnome shell을 사용하고 싶다면 gnome-session 패키지 설치 필요
    • 기본 Ubuntu Desktop은 Dash to Dock(Ubuntu Dock) 등 gnome 일부 기능을 cusomize 시킴
  • Display server가 Xorg에서 Wayland로 대체
  • Display manager가 lightdm에서 gdm3로 대체
  • Desktop 32-bit 설치 iso 이미지 파일은 공식적으로 중단
  • Network 유틸리티 들이 새롭게 대체됨
    • ifupdown 대신 netplan 사용
    • net-tools(ifconfig/netstat 등 포함)가 기본 설치되지 않고 ip 명령 권장
  • Linux kernel 4.13 채택: Graphics/CPU 성능 향상, 파일시스템 개선 등
이번 버전은 크게 바뀐 것이 많아서 상대적으로 배포까지의 시간이 부족했을 가능성이 높다. 그래서인지 설치 iso 이미지는 Xorg 서버로만 동작하므로 실제로 설치하지 않고는 Wayland 서버를 사용해 볼 수 없다. 

Gnome Desktop 환경

Unity Desktop을 사용하던 사람들은 여러가지 불편함을 느낄 수 있다. Unity Launcher 대신 Dash to Dock(Gnome extension)을 수정한 Ubuntu Dock이 비슷하게 보이지만 Qt 앱의 경우 Dock에 고정시키기도 불편하고 실행시 별개의 아이콘이 뜨는 문제라든가 Minimize 기능 등 자잘하게 안되는 것들이 있다. 무엇보다 Unity의 Global menu가 사라져서 VirtualBox 등에서 화면 수직폭이 다시 늘어나는 불편함이 있다.

개인적으로는 어차피 Gnome extension을 사용할 거라면 Dash to Dock 보다는 Dash to Panel에 Hide Top Bar를 결합한 원래의 Gnome Desktop이 더 마음에 든다. 다만, 그것이 Windows UI와 거의 비슷하기 때문에 우분투 입장에서 꺼려지는 측면이 있었을 것이다.

Javascript를 사용하는 Gnome extensions를 사용해야 하는 것도 장단점이 있어서 사용자들의 호불호가 극단적으로 나뉜다. OS의 일부 기능을 인터넷 상의 누군가에게 의존해야 하는 문제 때문이다. 보안 문제가 생길 수도 있고, 일부 extension 들은 버전 업에 따른 유지보수가 제대로 이루어 지지 않거나 중단되는 경우도 많다. 머, 장점은 좀 알면 내 맘대로 데스크탑을 바꿀 수 있다는 것이다.

기본 Wayland 서버 환경 (Wayland + nouveau Driver)

우분투 17.10을 설치하고 나면 로그인시 사용자 id를 click하면 기어모양의 아이콘이 패스워드 창 밑에 보이는데 이걸 click하면 두개의 Session이 보인다. "Ubuntu"와 "Ubuntu on Xorg"이다. 이 중에서 "Ubuntu"는 곧 "Ubuntu on Wayland"이다. 오리지널 Gnome Shell을 설치하고 재 부팅하면 "Gnome"과 "Gnome on Xorg" 두 개의 Session이 추가 되는데 여기서도 "Gnome" = "Gnome on Wayland"이다. 로그인시 그냥 id와 패스워드를 입력하고 로그인하면 "Ubuntu" Session으로 로그인된다. 그리고 별도로 Nvidia와 같은 상용 드라이버를 설치하지 않았다면 기본 nouveau 드라이버 환경이 된다. 

오랫동안 써 보진 못했지만 Wayland + nouveau Driver 환경은 이전 버전에 비해 상대적으로 매우 빠르다는 느낌을 준다. 그리고, Wayland + Nvidia Driver 환경에 비해 좀 느릴 수도 있지만 훨씬 안정적으로 동작한다.

  • GUI 앱을 터미널에서 root 권한으로 실행할 수 없음
    • 필요한 기능만 PolicyKit로 root 권한을 주는 앱은 실행 가능
    •  아래와 같이 우회적인 방법으로 gparted, synaptic 등 실행 가능
      • $ sudo xhost +SI:localuser:root
  • 잘 알려진 X11 앱들(xkill, xrandr, sdotool, xsel 등)이 Wayland session이나 앱에서 동작하지 않음
  • Game과 같은 특정 앱이 모니터 해상도를 변경할 수 없음
    • 단, 해상도를 먼저 바꾸면 Game 실행 가능할 수도...
  • 보안 문제를 차단하기 위해 일반 앱에서 화면 capture/casting 불가
    • System에서 허용한 앱만 가능
  • 느린 시스템에서 Mouse pointer가 느리게 반응할 수 있음(버그)
  • Keyboard events가 가끔 빠르게 반복될 수 있음(버그)
  • 모든 key 입력이 원격 데스크탑이나 가상 머신에 전달되지 않을 수 있음(버그) 
그리고, Wayland에 관심이 있다면 위의 Fedora 링크를 자세히 숙지해야 할 것이다. 실제로 일반 사용자들은 자신이 로그인한 데스크탑 환경이 Wayland 환경인지 Xorg  환경인지 헛갈릴 수 있고, Wayland 환경 내에서도 실행시킨 GUI 앱이 Wayland 앱인지 Xorg 앱인지 구분하기 어렵다.

Wayland + Nvidia 상용 드라이버 환경

실제로 우분투 17.10에 탑재된 Nvidia 상용 드라이버를 설치하고 재부팅해서 무심코 로그인하면 기존에 Wayland + nouveau 환경을 사용했던 일반 사용자들은 당연히 Wayland + Nvidia 환경이 됐을 거라 착각하기 쉽다. 하지만 logout 한 후에 기어를 click해 보면 "Ubuntu" 세션이 사라지고 "Ubuntu on Xorg" 세션만 남아 있음을 알게 된다. 기본적으로 Nvidia 상용 드라이버 환경에서 별다른 설정을 하지 않으면 Wayland를 사용할 수 없다. 이 부분은 Nvidia 상용 드라이버의 한계라고 볼 수 있기도 하다. 아직 상용 드라이버들이 Wayland 를 제대로 지원하지는 않고 있다는 증거이다.

Nvidia 상용 드라이버 환경에서 Wayland를 사용하려면 앞서 글에서 다룬 바와 같이 nvidia-drm 모듈에 대한 option을 주고 부팅해야 한다. 앞서 글에서 다룬 Nvidia 드라이버 설치 방법을 숙지하는 것이 좋다. 참고로, 우분투 17.10에 탑재된 Nvidia 상용 드라이버는 Nvidia Site에서 제공하는 최신 상용 드라이버와 버전은 같지만 내 PC에서는 option을 주어도 Wayland를 사용할 수 없었다.

Wayland + Nvidia 환경은 일반 사용자들에게 권하고 싶지 않다. 기본적으로는 안정성 문제가 심각하고 Grub Recovery 모드로 부팅시 화면이 깨져서 아무것도 할 수 없는 문제가 생긴다.

안정성 문제는, 가령 절전 모드 진입시에 Gnome Shell이 먹통이 되어 버리는데 ssh로 원격 접속하는 것 말고는 할 수 있는 게 없다. 기본적으로 콘솔은 black screen이기 때문이다. 더구나 Wayland 환경에서는 gdm 서비스를 종료시켜서 콘솔 화면으로 빠져 나올 수 있는 방법도 없다.

그러니까, Nvidia 상용 드라이버 환경에서 Wayland 를 사용하고 싶다면 반드시 사전에 openssh-server를 설치해 두어야 한다. 물론 ssh로 접속할 수 있는 별개의 PC 한대가 더 있어야겠다. 스마트 폰 앱으로도 가능하긴 하지만 불편하니까...

참고로 ssh로 원격 접속해서 Nvidia 환경에서 Xorg로만 다시 사용하기 위해서는 blacklist 파일에 아래와 같이 option을 변경하고 initramfs를 update 한 후 재부팅하면 된다.

options nvidia-drm modeset=0

또는, 아예 상용 Nvidia 드라이버를 제거하고 nouveau 드라이버 환경으로 복귀하면 최초의 우분투 환경으로 돌아올 수 있다.

Xorg 환경

Xorg 환경은 Wayland에 관심이 없거나 잘 모르는 사용자들이 익숙한 환경이다. Unity Desktop이 Gnome Desktop으로 바뀐 것 빼고는 달라진게 없다. Xorg 환경에서는 nouveau 건 상용 Nvidia 드라이버 건 모두 안정적으로 동작하기 때문에, 역설적으로 기존의 우분투 사용자들이 당분간 선호하게 될 환경이기도 하다.

한글 입력기 문제

Gnome Desktop의 기본 한글 입력기는 ibus이다. 우분투 17.10을 설치하고 나서 한글 언어 팩을 자동 설치하고 나서 재로그인 하면 한글입력이 바로 되면 좋은데 재부팅하고 나서야 제대로 동작하더라. 

참고로, fcitx-hangul은 따로 설치해야 하는데, 설치 후 언어 설정에서 입력기를 fcitx로 지정해도 재로그인 후 fcitx 프로세스가 뜨지 않는 문제가 있다. fcitx 프로세스가 동작하려면 입력기 환경변수 들이 ibus 대신 fcitx로 바뀌어야 하는데 자동으로 바뀌지 않는 버그가 있다. 또한, im-config를 실행해도 환경 변수들이 제대로 설정되지 않는 버그도 있다. 내부적으로 im-config를 사용하고 있다면 동일한 버그일 수도 있다. 이에 더해서, 수동으로 입력기 환경변수를 fcitx로 잡아주면 fcitx 프로세스가 뜨지만 ibus 프로세스들 역시 죽지 않아서 fcitx가 제대로 동작하지 않는다. 

사실, Wayland 테스트 하느라 한글 입력기들은 제대로 테스트하지 못했다. ibus를 사용하면 Qt 앱에서도 한글입력이 잘 된다. 당분간은 ibus에 만족해야 할 듯...

맺음말

우분투 17.10에 Gnome Shell 뿐만 아니라 Wayland 서버가 기본 데스크탑이 되면서 우분투 사용 환경이 다소 복잡해진 것이 사실이다. Gnome은 이제 붙박이가 되었으니 그렇다 쳐도, Wayland냐 Xorg냐의 선택과 nouveau냐 Nvidia 상용 드라이버냐의 선택이 있으므로 최소한 4가지 사용 환경이 파생된다. 여기에 기존의 Unity 사용자들은 Gnome 대신 Unity를 계속 사용할 수도 있으니...

굳이 Nvidia  상용 드라이버를 쓸 필요가 없는 일반 사용자 들은 기본 우분투 환경인 Gnome + Wayland + nouveau도 꽤 쓸만하다고 본다. 하지만, CUDA 등 Nvidia 상용 드라이버를 반드시 사용해야 하는 사용자라면 당분간 Gnome + Xorg + Nvidia 환경을 사용해야 할 것이다.