EPTの設定をbhyveで調べる

KVMよりもEPTの使用前提なbhyveのほうがコード読みやすかったのです。Nested Paging in bhyveというFreeBSDのメモリ管理からbhyveでのEPT周りの実装を解説した論文があったのも理由としては大きいですね。

EPTPの設定

vmx_vminit()という関数でeptpの設定をする関数を呼びます。

  814 static void *
  815 vmx_vminit(struct vm *vm, pmap_t pmap)
  816 {
  817         uint16_t vpid[VM_MAXCPU];
  ~ 略 ~
  827         }
  828         vmx->vm = vm;
  829 
  830         vmx->eptp = eptp(vtophys((vm_offset_t)pmap->pm_pml4));

pmap_tはpmap構造体で、PML4のアドレスを保持している(LinuxだとPGD)。pml4_entry_tはu_int64_tの別名(typedef)。

  309 /*
  310  * The kernel virtual address (KVA) of the level 4 page table page is always
  311  * within the direct map (DMAP) region.
  312  */
  313 struct pmap {
  314         struct mtx              pm_mtx;
  315         pml4_entry_t            *pm_pml4;       /* KVA of level 4 page table */
  316         uint64_t                pm_cr3;
  317         TAILQ_HEAD(,pv_chunk)   pm_pvchunk;     /* list of mappings in pmap */
  318         cpuset_t                pm_active;      /* active on cpus */
  319         enum pmap_type          pm_type;        /* regular or nested tables */
  320         struct pmap_statistics  pm_stats;       /* pmap statistics */
  321         struct vm_radix         pm_root;        /* spare page table pages */
  322         long                    pm_eptgen;      /* EPT pmap generation id */
  323         int                     pm_flags;
  324         struct pmap_pcids       pm_pcids[MAXCPU];
  325 };

eptp()はこのような処理。

  195 uint64_t
  196 eptp(uint64_t pml4)
  197 {
  198         uint64_t eptp_val;
  199 
  200         eptp_val = pml4 | (EPT_PWLEVELS - 1) << 3 | PAT_WRITE_BACK;
  201         if (ept_enable_ad_bits)
  202                 eptp_val |= EPT_ENABLE_AD_BITS;
  203 
  204         return (eptp_val);
  205 }

この関数はExtended-Page-Table Pointer (EPTP)の設定をしています。EPTPはIntel SDM Vol3の24.6.11 Extended-Page-Table Pointer (EPTP)に説明があります。

まず200行目を見ます。EPT_PWLEVELSは4で、PAT_WRITE_BACKは0x6です。そうすると、以下のようなbit列になります。

>>> bin(3 << 3 | 0x6)
'0b11110'

EPTPのbit2:0は0もしくは6を設定する仕様です。ここではPAT_WRITE_BACKで6を設定しています。次にbit5:3がEPT page-walk lengthとなっています。bit5:3は0b11なので3です。 202行目のEPT_ENABLE_AD_BITSは(1 << 6)です。1<<6は2進数で0b1000000なので6bit目を1にしてます。EPTPの6bit目はAccess/Dirty flagを有効にする設定です。EPTPのbit11:7は予約済みで、bit N-1:12はSDMには「Bits N–1:12 of the physical address of the 4-KByte aligned EPT PML4 table 3」とあります。Nは「N is the physical-address width supported by the logical processor.」とのことです。bit63:Nは予約済みです。なので、bit6:0までを設定するのがEPTPの設定ですね。

EPTの設定

vmx_init()からept_init()を呼びます。

ここでvmx_init()までをφ(..)メモメモ

初期化の関数はvmm_ops構造体のinit変数にvmx_init()を設定する。

 3423 struct vmm_ops vmm_ops_intel = {
 3424         vmx_init,

呼び出し方はVMM_INITマクロで定義されている。

  169 static struct vmm_ops *ops;
  170 #define VMM_INIT(num)   (ops != NULL ? (*ops->init)(num) : 0)

vmm_init()からvmx_init()を呼び出している。

  321 static int
  322 vmm_init(void)
  323 {
  324         int error;
  325 
  326         vmm_host_state_init();
  327 
  328         vmm_ipinum = lapic_ipi_alloc(&IDTVEC(justreturn));
  329         if (vmm_ipinum < 0)
  330                 vmm_ipinum = IPI_AST;
  331 
  332         error = vmm_mem_init();
  333         if (error)
  334                 return (error);
  335         
  336         if (vmm_is_intel())
  337                 ops = &vmm_ops_intel;
  338         else if (vmm_is_amd())
  339                 ops = &vmm_ops_amd;
  340         else
  341                 return (ENXIO);
  342 
  343         vmm_resume_p = vmm_resume;
  344 
  345         return (VMM_INIT(vmm_ipinum));
  346 }

で、本題に戻ってept_init()

   77 int
   78 ept_init(int ipinum)
   79 {
   80         int use_hw_ad_bits, use_superpages, use_exec_only;
   81         uint64_t cap;
   82 
   83         cap = rdmsr(MSR_VMX_EPT_VPID_CAP);
   84 
   85         /*
   86          * Verify that:
   87          * - page walk length is 4 steps
   88          * - extended page tables can be laid out in write-back memory
   89          * - invvpid instruction with all possible types is supported
   90          * - invept instruction with all possible types is supported
   91          */
   92         if (!EPT_PWL4(cap) ||
   93             !EPT_MEMORY_TYPE_WB(cap) ||
   94             !INVVPID_SUPPORTED(cap) ||
   95             !INVVPID_ALL_TYPES_SUPPORTED(cap) ||
   96             !INVEPT_SUPPORTED(cap) ||
   97             !INVEPT_ALL_TYPES_SUPPORTED(cap))
   98                 return (EINVAL);
   99 
  100         ept_pmap_flags = ipinum & PMAP_NESTED_IPIMASK;
  101 
  102         use_superpages = 1;
  103         TUNABLE_INT_FETCH("hw.vmm.ept.use_superpages", &use_superpages);
  104         if (use_superpages && EPT_PDE_SUPERPAGE(cap))
  105                 ept_pmap_flags |= PMAP_PDE_SUPERPAGE;   /* 2MB superpage */
  106 
  107         use_hw_ad_bits = 1;
  108         TUNABLE_INT_FETCH("hw.vmm.ept.use_hw_ad_bits", &use_hw_ad_bits);
  109         if (use_hw_ad_bits && AD_BITS_SUPPORTED(cap))
  110                 ept_enable_ad_bits = 1;
  111         else
  112                 ept_pmap_flags |= PMAP_EMULATE_AD_BITS;
  113 
  114         use_exec_only = 1;
  115         TUNABLE_INT_FETCH("hw.vmm.ept.use_exec_only", &use_exec_only);
  116         if (use_exec_only && EPT_SUPPORTS_EXEC_ONLY(cap))
  117                 ept_pmap_flags |= PMAP_SUPPORTS_EXEC_ONLY;
  118 
  119         return (0);
  120 }

最初にMSRからEPTをVPIDのケーパビリティを読み出します。これはSDM Vol3のA.10 VPID AND EPT CAPABILITIESに説明があります。そして、必要な機能が使えるかチェックしてます。あとはLinuxで言うところのsysctlで設定されたデータの読み出しと、フラグの設定ですね。

amd64/vmm/intel/ept.cにはもう一つ名前にinitが付く関数があります。それはept_pinit()です。この関数はept_vmspace_alloc()の処理から呼ばれます。

  181 struct vmspace *
  182 ept_vmspace_alloc(vm_offset_t min, vm_offset_t max)
  183 {
  184 
  185         return (vmspace_alloc(min, max, ept_pinit));
  186 }
  187 

vmspace_alloc()はメモリ管理サブシステムの関数です。3番目の引数にept_pinit()を渡すことで、初期化処理の関数としてept_pinit()を呼ぶようにしています。これは論文によるとVMMをサポートするためにこのような形になったようです。

ept_pinit()はこのような関数です。こちらもメモリ管理サブシステムのほうの関数を呼びます。

  174 static int
  175 ept_pinit(pmap_t pmap)
  176 {
  177 
  178         return (pmap_pinit_type(pmap, PT_EPT, ept_pmap_flags));
  179 }

pmap_pinit_type()はbyhveの追加時に新規に作られた関数とのことです。こちらもEPTのためですね。

 2407 /*
 2408  * Initialize a preallocated and zeroed pmap structure,
 2409  * such as one in a vmspace structure.
 2410  */
 2411 int
 2412 pmap_pinit_type(pmap_t pmap, enum pmap_type pm_type, int flags)
 2413 {
 2414         vm_page_t pml4pg;
 2415         vm_paddr_t pml4phys;
 2416         int i;
 2417 
 2418         /*
 2419          * allocate the page directory page
 2420          */
 2421         while ((pml4pg = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL |
 2422             VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL)
 2423                 VM_WAIT;
 2424 
 2425         pml4phys = VM_PAGE_TO_PHYS(pml4pg);
 2426         pmap->pm_pml4 = (pml4_entry_t *)PHYS_TO_DMAP(pml4phys);
 2427         CPU_FOREACH(i) {
 2428                 pmap->pm_pcids[i].pm_pcid = PMAP_PCID_NONE;
 2429                 pmap->pm_pcids[i].pm_gen = 0;
 2430         }
 2431         pmap->pm_cr3 = ~0;      /* initialize to an invalid value */
 2432 
 2433         if ((pml4pg->flags & PG_ZERO) == 0)
 2434                 pagezero(pmap->pm_pml4);
 2435 
 2436         /*
 2437          * Do not install the host kernel mappings in the nested page
 2438          * tables. These mappings are meaningless in the guest physical
 2439          * address space.
 2440          */
 2441         if ((pmap->pm_type = pm_type) == PT_X86) {
 2442                 pmap->pm_cr3 = pml4phys;
 2443                 pmap_pinit_pml4(pml4pg);
 2444         }
 2445 
 2446         pmap->pm_root.rt_root = 0;
 2447         CPU_ZERO(&pmap->pm_active);
 2448         TAILQ_INIT(&pmap->pm_pvchunk);
 2449         bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
 2450         pmap->pm_flags = flags;
 2451         pmap->pm_eptgen = 0;
 2452 
 2453         return (1);
 2454 }

pm_typeとしてPT_EPTを渡しているので2441行目のところは実行されません。ここは通常のページテーブルの設定の場合のみに実行ですね。それ以外はpmap構造体の設定でpml4の物理アドレを設定したりとかしてます。

次に気になるのはept_vmspace_alloc()が何時呼ばれるのか?ですね。

ept_vmspace_alloc()の呼ばれ方

vmx_init()と同様にvmm_ops構造体に関数を設定しています。設定先の変数はvmspace_allocです。この関数も直接は呼び出さないでマクロのVMSPACE_ALLOCマクロから呼ばれます。

  178 #define VMSPACE_ALLOC(min, max) \
  179         (ops != NULL ? (*ops->vmspace_alloc)(min, max) : NULL)

vm_create()がVMSPACE_ALLOCマクロを使っています。

  422 int
  423 vm_create(const char *name, struct vm **retvm)
  424 {
  425         struct vm *vm;
  426         struct vmspace *vmspace;
  427 
  428         /*
  429          * If vmm.ko could not be successfully initialized then don't attempt
  430          * to create the virtual machine.
  431          */
  432         if (!vmm_initialized)
  433                 return (ENXIO);
  434 
  435         if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
  436                 return (EINVAL);
  437 
  438         vmspace = VMSPACE_ALLOC(0, VM_MAXUSER_ADDRESS);
  439         if (vmspace == NULL)
  440                 return (ENOMEM);
  441 
  442         vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO);
  443         strcpy(vm->name, name);
  444         vm->vmspace = vmspace;
  445         mtx_init(&vm->rendezvous_mtx, "vm rendezvous lock", 0, MTX_DEF);
  446 
  447         vm_init(vm, true);
  448 
  449         *retvm = vm;
  450         return (0);
  451 }

vm_create()を呼んでいるのはsysctl_vmm_create()です。なのでVMの作成時に初期化処理の流れで呼ばれる感じですね。

まとめ

EPTPはeptl()で設定します。EPTに使うページテーブルの設定はept_init()でケーパビリティのチェックやフラグの設定をしてからpmap_pinit_type()でpmap構造体の設定を行うことで設定しています。

(´-`).。oO(BSD系のコードは初見でも読みやすい

( ´ー`)フゥー...

はじめてUNIXで仕事をする人が読む本 (アスキードワンゴ)

はじめてUNIXで仕事をする人が読む本 (アスキードワンゴ)

Linuxとかカーネル方面のYoutubeチャンネル

Linuxとかカーネル方面のYoutubeチャンネルをめも

hupstream

Kernel Recipesというタイトルのシリーズがカーネルの濃い発表動画。

www.youtube.com

KVM Forum

KVMQEMUなど。

www.youtube.com

The Linux Foundation

色んなカンファレンス動画がある。

www.youtube.com

USENIX

色んなカンファレンス動画がある。

www.youtube.com

Fedora Project

Flockが開発者・ユーザーがあつまるイベントでディストリビューションの開発に関するネタが多い。

www.youtube.com

DebConf Videos

Debianのカンファレンス。

www.youtube.com

FOSDEM

FOSDEM(Free and Open Source Development European Meeting)。ヨーロッパで行われてるOSS系のイベント。

www.youtube.com

BSDCan

BSD!

www.youtube.com

bsdconferences

BSD系のカンファレンスはこちらが多いか。

www.youtube.com

netdevconf

Linuxのネットワーク系カンファレンス。  NetDev: The Technical Conference On Linux Networking

www.youtube.com

linux: カーネルからioctl(2)可能なfdをユーザ空間に返す

KVMはioctl(2)でVMを作ったりvcpuを作ったりします。この時に使うfdは/dev/kvmファイルに対してioctl(2)を実行した時の戻り値です。ioctl(2)の戻り値がioctl(2)可能なfdとなってます。ユーザーランドに返すfdはどうやって作るのかというのが今回のめもです。

早速ですが、出来たコードはこうなります。

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/anon_inodes.h>
#include <linux/string.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/file.h>

MODULE_DESCRIPTION("ioctl test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

struct fd_test_data {
    unsigned long id;
};

static long fd_test_anon_fd_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
    int r = -1;
    struct fd_test_data *p = filp->private_data;

    pr_info("%s: ioctl is %d\n", __func__, ioctl);

    switch (ioctl) {
    case 0:
        r = p->id;
        break;
    default:
        pr_warn("unkown ioctl %ud\n", ioctl);
    }

    return r;
}

static struct file_operations fd_test_anon_fd_fops = {
    .unlocked_ioctl     = fd_test_anon_fd_ioctl,
    .compat_ioctl       = fd_test_anon_fd_ioctl,
    .llseek     = noop_llseek,
};

static int create_file(unsigned long id)
{
    struct fd_test_data *p = kmalloc(sizeof(*p), GFP_KERNEL);
    int fd;
    char name[256] = { 0x0 };
    struct file *filp;

    if (!p)
        return -ENOMEM;

    p->id = id;

    snprintf(name, sizeof(name), "fd_test-%ld\n", id);


    filp =  anon_inode_getfile(name, &fd_test_anon_fd_fops, p, O_RDWR);
    if (IS_ERR(filp))
        return -1;

    fd = get_unused_fd_flags(O_CLOEXEC);
    if (fd < 0)
        return -1;

    fd_install(fd, filp);

    return fd;
}

static long fd_test_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
    long r = -EINVAL;

    pr_info("%s: ioctl is %d\n", __func__, ioctl);

    switch (ioctl) {
    case 0:
        r = create_file(arg);
        break;
    default:
        pr_warn("unkown ioctl %ud\n", ioctl);
    }

    return r;
}


static struct file_operations fd_test_chardev_fops = {
    .unlocked_ioctl     = fd_test_ioctl,
    .compat_ioctl       = fd_test_ioctl,
    .llseek         = noop_llseek,
};

static struct miscdevice fd_test_chardev = {
    .minor      = 243,
    .name       = "fd_test",
    .fops       = &fd_test_chardev_fops,
};

static int fd_test_init(void)
{
    int fd;

    fd = misc_register(&fd_test_chardev);
    if (fd < 0) {
        pr_warn("failed to create miscdevice\n");
        return fd;
    }

    pr_info("device file created\n");
    return fd;
}

static void fd_test_exit(void)
{
    misc_deregister(&fd_test_chardev);
    pr_info("finish %s\n", __func__);

}

module_init(fd_test_init);
module_exit(fd_test_exit);

まずはmiscデバイスを作っていて、最初にこのファイルを開いてioctl(2)を実行します。そうするとioctl(2)を実行できるfdが返るという感じです。 このfdの作り方は以下の3ステップでできます。

  1. fdを新たに割り当て
  2. file構造体を作成
  3. fdとファイル構造体を関連付ける

あ、上記のコードだと1と2は逆ですが。

1のfdの割り当てはget_unused_fd_flags()で行います。 2はanon_inode_getfile()です。この関数の3番目の引数はfile構造体のprivate_data変数に設定するデータです。上記のコードだとfd_test_data構造体を設定してます。 最後の3はfd_install()で、見ての通りfdとfile構造体を渡してます。 そして、fdをユーザーランドに返せば完了です。

テストコードはこんな感じです。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
    int fd;
    int anon_fd;
    int r;

    unsigned long id = argc == 2 ? strtoul(argv[1], NULL, 10) : 1234UL;

    fd = open("/dev/fd_test", O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(-1);
    }

    anon_fd = ioctl(fd, 0, id);
    if (anon_fd < 0) {
        perror("ioctl");
        exit(-1);
    }


    r = ioctl(anon_fd, 0, 0UL);

    printf("id is %d\n", r);
    close(fd);
}

これをコンパイルして動かすとこんなふうになります。

masami@kerntest:~/fd_test$ sudo ./a.out 1234
id is 1234
masami@kerntest:~/fd_test$ sudo ./a.out 12345678
id is 12345678
masami@kerntest:~/fd_test$ 

( ´ー`)フゥー...

dockerでNignxのリバースプロキシとwebアプリをlinkしたローカル開発環境を作る

前に仕事で作ったdockerを使ったwebアプリの開発環境の覚書です。

実環境はAWSで動いていて、ALBがhttpsでリクエストを受けてALBとwebアプリはhttpで通信します。このような構成をdockerで作りたかったわけです。 簡単な概要はこんな図になります。

f:id:masami256:20180724230335p:plain

ALBのところをNginxのリバースプロキシに置き換えて作りました。

Nginxのリバースプロキシはこんな設定です。

server {
       listen         443;
       server_name    www.webapp-test.local;
       ssl on;
       ssl_certificate      /etc/nginx/server.crt;
       ssl_certificate_key  /etc/nginx/server.key;

       location / {
             proxy_set_header Host $host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header X-Forwarded-Proto https;
             proxy_set_header X-Forwarded-Host $host;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

             # name is docker's link alias.
             proxy_pass http://local.webapp-test.local:3000/;
             proxy_redirect http:// https://;
       }
}

アプリの方でX-Forwarded-*を参照するのでセットしたり、hostヘッダはそのまま流したりしてます。 server_nameは実際にはちゃんとしたサーバ名ですがここではこんな名前にしてます。この名前はdocker-compose.ymlファイルのlinkで使う名前と合わせています。 www.webapp-test.localがブラウザでアクセスするURLになって、proxy_passで記述しているのがアプリのURLですね。どっちもlinkする時に使います。

Dockerfileはこの程度です。ベースのイメージは軽めなやつがよかったのでalpineを使ってます。

FROM alpine:latest

RUN apk update && \
apk add nginx && \
mkdir -p /run/nginx && \
rm -rf /var/cache/apk/*

COPY nginx/nginx.conf /etc/nginx/nginx.conf
COPY nginx/server.conf /etc/nginx/conf.d/server.conf

COPY nginx/server.crt /etc/nginx/server.crt
COPY nginx/server.key /etc/nginx/server.key

CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

webアプリの方はDockerfileとかは特に変わったことはしてません。webaアプリのほうにあるdocker-compose.ymlでlinkしてあげるだけです。

version: '2'

services:
  webapp:
    build: .
    image: webapp
    hostname: local
    domainname: local.webapp-test.local
  proxy:
    image: local_dev_proxy
    ports:
      - 443:443
    links:
      - webapp:local.webapp-test.local
    hostname: www
    domainname: www.webapp-test.local

webappがwebアプリ、proxyがリバースプロキシです。hostとdomainnameはNginxの設定と合わせています。proxyのほうは443ポートを公開してます。ホストからアクセスする時は普通にhttpsの443番ポートを使います。で、proxyからwebappにアクセスを流したいのでlinkを使ってます。 あ、リバースプロキシもdocker-compose.yml使ってビルドしていて、名前はlocal_dev_proxyとしてます。

docker-compose upでwebアプリとリバースプロキシを起動しておいてcurlでアクセスを確認するとこうなります。

f:id:masami256:20180724232128p:plain

とまあ、ローカルの開発環境でALBの代わりにNginxでリバースプロキシを作ったというめもでした。

Docker実践ガイド impress top gearシリーズ

Docker実践ガイド impress top gearシリーズ

Linux:パフォーマンスに影響あるデバッグオプションがどれだけ影響あるか試してみた

なんとなくやってみた系ですね。

今回はCONFIG_DEBUG_OBJECTSのところです。

f:id:masami256:20180706233206p:plain

CONFIG_DEBUG_OBJECTS_FREEがkmalloc/kfreeをヘヴィにつかうような場合にパフォーマンス劣化するよーなんていってます。

f:id:masami256:20180706233254p:plain

まずはCONFIG_DEBUG_OBJECTSの項目を全部無効にした場合。

この時点でもロック周りに時間取ってますね。

f:id:masami256:20180706233504p:plain

hackbenchを引数 10 process 20000 で実行してる時の様子がこんな感じです。

f:id:masami256:20180706233612p:plain

次にCONFIG_DEBUG_OBJECTSの項目を全部無効にした場合。

起動した時点でかなりの時間をロックで使ってます。

f:id:masami256:20180706233435p:plain

そして、hackbenchを同じく実行するとこうこうなります/(^o^)\

f:id:masami256:20180706233745p:plain

このときはこんな感じになっていてもうどうにもならない感がありますね。

f:id:masami256:20180706233833p:plain

説明に書いてあるとおり、ほんと遅くなりますね。

( ´ー`)フゥー...

日経Linux 2018年 7 月号

日経Linux 2018年 7 月号

qemu: pyhtonでゲストとシリアル通信

ゲスト側のLinuxカーネルでシリアルコンソールを有効にしているときに、ホストからpythonでシリアルコンソールにアクセスするときメモです。

ゲスト側はカーネルコマンドラインでシリアルの設定をするのと、qemuのオプションで-serial ptyとしてptyを使うようにする。

$ qemu-kvm -initrd ./initramfs-4.17.2-200.fc28.x86_64.img -kernel ./vmlinuz-4.17.2-200.fc28.x86_64 -m 512 -append "console=ttyS0,115200" -serial pty -nographic
QEMU 2.11.1 monitor - type 'help' for more information
(qemu) qemu-system-x86_64: -serial pty: char device redirected to /dev/pts/2 (label serial0)

pythonのコードは特にパッケージとかはインストールしていなくて、qemu起動時に表示された /dev/pts/Xをオープンしてデータの読み書きを行います。

#!/usr/bin/env python3
#  qemu-system-x86_64 -initrd ./initramfs-4.17.2-200.fc28.x86_64.img  -kernel ./vmlinuz-4.17.2-200.fc28.x86_64 -m 512 -append "console=ttyS0,115200" -serial pty -nographic

import io, os
pty = io.TextIOWrapper(
    io.FileIO(
        os.open(
            "/dev/pts/2", os.O_NOCTTY | os.O_RDWR
        ),
        "w+"
    )
)
pty.write("uname -a\n");
for line in iter(pty.readline, None):
    print(line.strip())

こんな感じで使えます。

f:id:masami256:20180701003342p:plain

TwitterのPC版のUIからだと連携解除できないappの連携解除方法メモ

iPhoneとかで連携したappの一部はPCのWeb版からだとrevokeできなかったのでやり方のメモです。

PCのWeb版だと連携しているアプリを見るとLearn how to revoke an iOS appとリンクがあるだけでRevoke Accessボタンがないappがあります。Twitter for iPhoneとかを使ってるアプリがこうなってるようです。

revokeしたい場合はスマホのアプリもしくは、モバイル版のtwitterにアクセスしてrevokeします。 ただ、それでも個別にrevokeということは出来ないのでiPhoneを使っているならTwittre for iPhoneをrevokeします。そうするとこのrevokeに連動してTwitter for iPhoneと連携しているappが一気にrevokeされます。 もし、許可したいappがある場合は再度許可してあげる必要があります。