momo zone

调核人的blog

Monthly Archives: 十一月 2009

ATH W1000 漂亮PP


Advertisements

白白研究半天

才知道RAW_SOCKET不能发送自构造的MAC头部 ,只能在这之后封装其他协议数据包 。要想突破这点还要用libpcap ,winpcap …….

arping代码分析

这两天因特殊需要, 改一下linux的arping 程序,以便能够以任何IP addr 来qurey arp(原版的只能以本地连接已分配ip来发送数据) ,所以顺便将这个程序的源代码注释一遍。

不说废话了,记录的注释都在代码里面。

/*
* arping.c
*
*                This program is free software; you can redistribute it and/or
*                modify it under the terms of the GNU General Public License
*                as published by the Free Software Foundation; either version
*                2 of the License, or (at your option) any later version.
*
* Authors:        Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>;
*/

#include <stdlib.h>;
#include <sys/param.h>;
#include <sys/socket.h>;
#include <linux/sockios.h>;
#include <sys/file.h>;
#include <sys/time.h>;
#include <sys/signal.h>;
#include <sys/ioctl.h>;
#include <linux/if.h>;
#include <linux/if_arp.h>;
#include <sys/uio.h>;

#include <netdb.h>;
#include <unistd.h>;
#include <stdio.h>;
#include <ctype.h>;
#include <errno.h>;
#include <string.h>;
#include <netinet/in.h>;
#include <arpa/inet.h>;

#include "SNAPSHOT.h"

static void usage(void) __attribute__((noreturn));

int quit_on_reply=0;
char *device="eth0";
int ifindex;
char *source;
struct in_addr src, dst;
char *target;
int dad, unsolicited, advert;
int quiet;
int count=-1;
int timeout;
int unicasting;
int s;
int broadcast_only;

struct sockaddr_ll me;
struct sockaddr_ll he;

struct timeval start, last;

int sent, brd_sent;
int received, brd_recv, req_recv;

#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 +
                           ((tv1).tv_usec-(tv2).tv_usec)/1000 )

void usage(void)
{
        fprintf(stderr,
                "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destinationn"
                "  -f : quit on first replyn"
                "  -q : be quietn"
                "  -b : keep broadcasting, don’t go unicastn"
                "  -D : duplicate address detection moden"
                "  -U : Unsolicited ARP mode, update your neighboursn"
                "  -A : ARP answer mode, update your neighboursn"
                "  -V : print version and exitn"
                "  -c count : how many packets to sendn"
                "  -w timeout : how long to wait for a replyn"
                "  -I device : which ethernet device to use (eth0)n"
                "  -s source : source ip addressn"
                "  destination : ask for what ip addressn"
                );
        exit(2);
}

void set_signal(int signo, void (*handler)(void))
{
        struct sigaction sa;

        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = (void (*)(int))handler;
        sa.sa_flags = SA_RESTART;
        sigaction(signo, &sa, NULL);
}

int send_pack(int s, struct in_addr src, struct in_addr dst,
              struct sockaddr_ll *ME, struct sockaddr_ll *HE)
{
        int err;
        struct timeval now;
        unsigned char buf[256];
        struct arphdr *ah = (struct arphdr*)buf;
        unsigned char *p = (unsigned char *)(ah+1);
       
        ah->;ar_hrd = htons(ME->;sll_hatype);        /* 硬件地址类型*/
        if (ah->;ar_hrd == htons(ARPHRD_FDDI))
                ah->;ar_hrd = htons(ARPHRD_ETHER);
        ah->;ar_pro = htons(ETH_P_IP);                /* 协议地址类型   */
        ah->;ar_hln = ME->;sll_halen;                /* 硬件地址长度   */
        ah->;ar_pln = 4;                                /* 协议地址长度 */
        ah->;ar_op  = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);/* 操作类型*/

        memcpy(p, &ME->;sll_addr, ah->;ar_hln);                       /* 发送者硬件地址*/
        p+=ME->;sll_halen;        /*以太网为6*/

        memcpy(p, &src, 4);                /* 发送者IP */
        p+=4;
        /* 目的硬件地址*/
        if (advert)
                memcpy(p, &ME->;sll_addr, ah->;ar_hln);
        else
                memcpy(p, &HE->;sll_addr, ah->;ar_hln);
        p+=ah->;ar_hln;

        memcpy(p, &dst, 4);                /* 目的IP地址*/
        p+=4;

        gettimeofday(&now, NULL);
        err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, sizeof(*HE));
        if (err == p-buf) {
                last = now;
                sent++;
                if (!unicasting)
                        brd_sent++;
        }
        return err;
}

void finish(void)
{
        if (!quiet) {
                printf("Sent %d probes (%d broadcast(s))n", sent, brd_sent);
                printf("Received %d response(s)", received);
                if (brd_recv || req_recv) {
                        printf(" (");
                        if (req_recv)
                                printf("%d request(s)", req_recv);
                        if (brd_recv)
                                printf("%s%d broadcast(s)",
                                       req_recv ? ", " : "",
                                       brd_recv);
                        printf(")");
                }
                printf("n");
                fflush(stdout);
        }
        if (dad)
                exit(!!received);
        if (unsolicited)
                exit(0);
        exit(!received);
}

void catcher(void)
{
        /*这个函数的主要作用是调用send_pack发送一次arp请求*/
        struct timeval tv;

        gettimeofday(&tv, NULL);

        if (start.tv_sec==0)
                start = tv;

        if (count– == 0 || (timeout && MS_TDIFF(tv,start) >; timeout*1000 + 500))
                finish();

        if (last.tv_sec==0 || MS_TDIFF(tv,last) >; 500) {
                send_pack(s, src, dst, &me, &he);
                if (count == 0 && unsolicited)
                        finish();
        }
        alarm(1);        /*每秒放松一个包*/
}

void print_hex(unsigned char *p, int len)
{
        int i;
        /*打印MAC地址*/
        for (i=0; i<len; i++) {
                printf("%02X", p[i]);
                if (i != len-1)
                        printf(":");
        }
}
/*数据包分析主程序.
把ARP 请求和答复的数据包格式画在这里,希望对理解下面的数据包分析有帮助

                             |—————28 bytes arp request/reply—————————–|       
|——–ethernet header—-|
_____________________________________________________________________________________________________
|ethernet | ethernet| frame|hardware|protocol|hardware|protocol|op|sender  |sender|target  |target|
|dest addr|src addr | type| type   |type    | length |length  |  |eth addr| IP   |eth addr| IP    |
—————————————————————————————————–  
  6 types    6        2      2        2         1        1      2     6       4       6        4
*/
int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
{
        struct timeval tv;
        struct arphdr *ah = (struct arphdr*)buf;
        unsigned char *p = (unsigned char *)(ah+1);
        struct in_addr src_ip, dst_ip;

        gettimeofday(&tv, NULL);

        /* Filter out wild packets */
        if (FROM->;sll_pkttype != PACKET_HOST &&
            FROM->;sll_pkttype != PACKET_BROADCAST &&
            FROM->;sll_pkttype != PACKET_MULTICAST)
                return 0;
        /*到这里pkttype为HOST||BROADCAST||MULTICAST*/
       
        /* Only these types are recognised */
        /*只要ARP request and reply*/
        if (ah->;ar_op != htons(ARPOP_REQUEST) &&
            ah->;ar_op != htons(ARPOP_REPLY))
                return 0;

        /* ARPHRD check and this darned FDDI hack here 😦 */
        if (ah->;ar_hrd != htons(FROM->;sll_hatype) &&
            (FROM->;sll_hatype != ARPHRD_FDDI || ah->;ar_hrd != htons(ARPHRD_ETHER)))
                return 0;

        /* Protocol must be IP. */
        if (ah->;ar_pro != htons(ETH_P_IP))
                return 0;
        if (ah->;ar_pln != 4)
                return 0;
        if (ah->;ar_hln != me.sll_halen)
                return 0;
        if (len < sizeof(*ah) + 2*(4 + ah->;ar_hln))
                return 0;
        /*src_ip:对方的IP
          det_ip:我的IP*/
        memcpy(&src_ip, p+ah->;ar_hln, 4);
        memcpy(&dst_ip, p+ah->;ar_hln+4+ah->;ar_hln, 4);
        if (!dad) {
                if (src_ip.s_addr != dst.s_addr)
                        return 0;
                if (src.s_addr != dst_ip.s_addr)
                        return 0;
                if (memcmp(p+ah->;ar_hln+4, &me.sll_addr, ah->;ar_hln))
                        return 0;
        } else {
        /*一般是执行这里,被理上面那段*/
                /* DAD packet was:
                   src_ip = 0 (or some src)
                   src_hw = ME
                   dst_ip = tested address
                   dst_hw = <unspec>;

                   We fail, if receive request/reply with:
                   src_ip = tested_address
                   src_hw != ME
                   if src_ip in request was not zero, check
                   also that it matches to dst_ip, otherwise
                   dst_ip/dst_hw do not matter.
                 */
                 /*dst.s_addr是我们发送请求是置的对方的IP,当然要等于对方发来的包的src_ip啦*/
                if (src_ip.s_addr != dst.s_addr)
                        return 0;
                if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
                        return 0;
                /*同理,src.s_addr是我们发包是置的自己的IP,要等于对方回复包的目的地址*/
                if (src.s_addr && src.s_addr != dst_ip.s_addr)
                        return 0;
        }
        if (!quiet) {
        /*显示一些答复的信息,不是重点*/
                int s_printed = 0;
                printf("%s ", FROM->;sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast");
                printf("%s from ", ah->;ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
                printf("%s [", inet_ntoa(src_ip));
                print_hex(p, ah->;ar_hln);
                printf("] ");
                if (dst_ip.s_addr != src.s_addr) {
                        printf("for %s ", inet_ntoa(dst_ip));
                        s_printed = 1;
                }
                if (memcmp(p+ah->;ar_hln+4, me.sll_addr, ah->;ar_hln)) {
                        if (!s_printed)
                                printf("for ");
                        printf("[");
                        print_hex(p+ah->;ar_hln+4, ah->;ar_hln);
                        printf("]");
                }
                if (last.tv_sec) {
                        long usecs = (tv.tv_sec-last.tv_sec) * 1000000 +
                                tv.tv_usec-last.tv_usec;
                        long msecs = (usecs+500)/1000;
                        usecs -= msecs*1000 – 500;
                        printf(" %ld.%03ldmsn", msecs, usecs);
                } else {
                        printf(" UNSOLICITED?n");
                }
                fflush(stdout);
        }
        received++;
        if (FROM->;sll_pkttype != PACKET_HOST)
                brd_recv++;
        if (ah->;ar_op == htons(ARPOP_REQUEST))
                req_recv++;
        if (quit_on_reply)
                finish();
        if(!broadcast_only) {
                memcpy(he.sll_addr, p, me.sll_halen);
                unicasting=1;
        }
        return 1;
}

int
main(int argc, char **argv)
{
        int socket_errno;
        int ch;
        uid_t uid = getuid();
        /*取得一个packet socket.
        int packet_sock=socket(PF_PACKET,int sock_type,int protocol);
        其中sock_type有两种:
            1.SOCK_RAW,使用类型的套接子,那么当你向设备写数据时,要提供physical layer
        header.当从设备读数据时,得到的数据是含有physical layer header的
            2.SOCK_DGRAM.这种类型的套接字使用在相对高层.当数据传送给用户之前,physical layer
            header已经去掉了(可能说的不合适,这是一个解封装的过程)*/
        s = socket(PF_PACKET, SOCK_DGRAM, 0);
        socket_errno = errno;
        /*这个setuid没有什么用,因为arping没有setuid(我的系统是Mandrake 10)*/
        setuid(uid);

        while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:V")) != EOF) {
                switch(ch) {
                case ‘b’:
                        broadcast_only=1;
                        break;
                /*duplicate address dection mode对这个参数一直不明白.man page里说see rfc2131.4.4.1
                但rfc2131里好像没有这一节.好在这不是重点.大家有理解的请告诉我吧*/
                case ‘D’:
                        dad++;
                        quit_on_reply=1;
                        break;
                case ‘U’:
                        unsolicited++;
                        break;
                case ‘A’:
                        advert++;
                        unsolicited++;
                        break;
                case ‘q’:
                        quiet++;
                        break;
                case ‘c’:
                        count = atoi(optarg);
                        break;
                case ‘w’:
                        timeout = atoi(optarg);
                        break;
                case ‘I’:
                        device = optarg;
                        break;
                case ‘f’:
                        quit_on_reply=1;
                        break;
                case ‘s’:
                        source = optarg;
                        break;
                case ‘V’:
                        printf("arping utility, iputils-ss%sn", SNAPSHOT);
                        exit(0);
                case ‘h’:
                case ‘?’:
                default:
                        usage();
                }
        }
        argc -= optind;
        argv += optind;

        if (argc != 1)
                usage();

        target = *argv;

        if (device == NULL) {
                fprintf(stderr, "arping: device (option -I) is requiredn");
                usage();
        }

        if (s < 0) {
                errno = socket_errno;
                perror("arping: socket");
                exit(2);
        }

        if (1) {
                struct ifreq ifr;
                memset(&ifr, 0, sizeof(ifr));
                strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
                if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
                        fprintf(stderr, "arping: unknown iface %sn", device);
                        exit(2);
                }
                ifindex = ifr.ifr_ifindex;

                if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) {
                        perror("ioctl(SIOCGIFFLAGS)");
                        exit(2);
                }
                /*设备当然是要up的想要bring up eth0 可以/etc/sysconfig/network-scripts/ifup eth0*/
                if (!(ifr.ifr_flags&IFF_UP)) {
                        if (!quiet)
                                printf("Interface "%s" is downn", device);
                        exit(2);
                }
                if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) {
                        if (!quiet)
                                printf("Interface "%s" is not ARPablen", device);
                        exit(dad?0:2);
                }
        }
        /*下面处理用户输入的目的地址,是11.22.33.44还是www.host.com?*/
        if (inet_aton(target, &dst) != 1) {
                /*看来是www.host.com,那我们得到他的信息byname*/
                struct hostent *hp;
                hp = gethostbyname2(target, AF_INET);
                if (!hp) {
                        fprintf(stderr, "arping: unknown host %sn", target);
                        exit(2);
                }
                /*要的是他的IP地址的binary representation*/
                memcpy(&dst, hp->;h_addr, 4);
        }

        if (source && inet_aton(source, &src) != 1) {
                fprintf(stderr, "arping: invalid source %sn", source);
                exit(2);
        }

        if (!dad && unsolicited && src.s_addr == 0)
                src = dst;
        /*下面这一大段确实有用么?好像唯一有用功是这一句  src = saddr.sin_addr;  */
        if (!dad || src.s_addr)
        {
                struct sockaddr_in saddr;
                int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);

                if (probe_fd < 0)
                {
                        perror("socket");
                        exit(2);
                }
                if (device)
                {
                        /*bind这个socket to
                        "device".操作的结果是只有那个设备收到的数据才传送到这个socket*/
                        if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) ==
                        -1)
                                perror("WARNING: interface is ignored");
                }
                memset(&saddr, 0, sizeof(saddr));
                saddr.sin_family = AF_INET;
                if (src.s_addr)
                {
                        saddr.sin_addr = src;
                        if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1)
                        {
                                perror("bind");
                                exit(2);
                        }
                }
                else if (!dad)
                {
                        int on = 1;
                        int alen = sizeof(saddr);

                        saddr.sin_port = htons(1025);
                        saddr.sin_addr = dst;
                /*SO_DONTROUTE的作用:don’t send via a gateway,only send to directly connected hosts*/
                        if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1)
                                perror("WARNING: setsockopt(SO_DONTROUTE)");
                        if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1)
                        {
                                perror("connect");
                                exit(2);
                        }
                        /*getsockname returns the local IP address and local port number assigned to the
                        connection by
                        the kernel*/
                        if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1)
                        {
                                perror("getsockname");
                                exit(2);
                        }
                        /*这是最终目的,取得本机IP地址*/
                        src = saddr.sin_addr;
                }
                close(probe_fd);
        }

        me.sll_family = AF_PACKET;
        me.sll_ifindex = ifindex;
        me.sll_protocol = htons(ETH_P_ARP);
        /* 只想要由me指定的接口收到的数据包*/
        if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
                perror("bind");
                exit(2);
        }

        if (1) {
                int alen = sizeof(me);
                /*get link layer information   是下面这些.因为sll_family sll_ifindex sll_protocol已知
                  unsigned short  sll_hatype;            Header type
                  unsigned char   sll_pkttype;           Packet type
                  unsigned char   sll_halen;             Length of address
                  unsigned char   sll_addr[8];           Physical layer address */
                if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
                        perror("getsockname");
                        exit(2);
                }
        }
        if (me.sll_halen == 0) {
                if (!quiet)
                        printf("Interface "%s" is not ARPable (no ll address)n", device);
                exit(dad?0:2);
        }

        he = me;
        /*把他的地址设为ff:ff:ff:ff:ff:ff  即广播地址,当然假设是以太网*/
        memset(he.sll_addr, -1, he.sll_halen);

        if (!quiet) {
                printf("ARPING %s ", inet_ntoa(dst));
                printf("from %s %sn",  inet_ntoa(src), device ? : "");
        }

        if (!src.s_addr && !dad) {
                fprintf(stderr, "arping: no source address in not-DAD moden");
                exit(2);
        }

        set_signal(SIGINT, finish);
        set_signal(SIGALRM, catcher);

        catcher();

        while(1) {
                sigset_t sset, osset;
                char packet[4096];
                struct sockaddr_ll from;
                int alen = sizeof(from);
                int cc;
                /*注意s的类型是SOCK_DGRAM,所以收到的数据包里没有link layer info,这些信息被记录在from里*/
                if ((cc = recvfrom(s, packet, sizeof(packet), 0,
                                   (struct sockaddr *)&from, &alen)) < 0) {
                        perror("arping: recvfrom");
                        continue;
                }
                sigemptyset(&sset);
                sigaddset(&sset, SIGALRM);
                sigaddset(&sset, SIGINT);
                sigprocmask(SIG_BLOCK, &sset, &osset);
                recv_pack(packet, cc, &from);
                sigprocmask(SIG_SETMASK, &osset, NULL);
        }
}

使用方法 :./arping.bin -I wlan1 -s 192.168.1.102 192.168.1.1

SuSE /dev/pts/ 和/dev/ptmx

/dev目录下这两个东东比较有个性。每个发行版都不尽相同。其中/dev/pts下的设备文件数量是会变化的。

/dev/pts/0 对应的桌面环境的第一个console ,如konsole . echo hello > /dev/pts/0 可以测试一下
/dev/pts/1 对应一个很奇怪的程序 : KWrite (不是编辑文档的KWrite) ,在“Configure desktop” -> "KDE component" ->"Service " 中可以听掉它。好像是用来实现弹出式消息功能,

linux-mxo6:~ # w
 22:48:33 up  2:57,  5 users,  load average: 1.20, 0.49, 0.33
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
root     tty1      22:04   13:22   0.13s  0.13s -bash
root     :0        19:52   ?xdm?  14:50   0.05s /bin/sh /opt/kde3/bin/startkde
root     pts/0     19:52   15:19  17.66s 17.20s mplayer -ao alsa:device=hw=0.1 -srate 48000 –
root     pts/1     22:44    3:45   0.00s  1.22s kded [kdeinit] –new-startup
root     pts/3     22:34    0.00s  2.42s  0.00s w

从/dev/pts/2 开始就依次对应第二个桌面的console ,第三个 …. 等等。

另外注意 Ctrl+F1 的console 是对应的/dev/tty1 以此类推。 但同样其他的是不知道/dev/tty0 对应的是谁 . 还有就是/dev/tty 对应是“当前”的终端 (无论真实还是虚拟,console还是konsole)

浅析terminal创建时ptmx和pts关系

我们打开一个terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件,
该pts字符文件节点直接由/dev/ptmx节点的驱动函数ptmx_open()
调用devpts_pty_new(tty->link)
[tty对应ptmx,tty->link对应/dev/pts/xxx,那么tty->link->link又对应回ptmx
同样ptm_driver->other等于pts_driver,pts_driver->other等于ptm_driver]主动创建,
而非通过netlink的udev或者hotplug配合创建[luther.gliethttp]

1.首先我们打开3个新的terminal终端
使用who am i查询当前终端对应的pts号
luther@gliethttp:~$ who am i
luther   pts/3        2009-07-03 09:05 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/4        2009-07-03 09:08 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/5        2009-07-03 09:08 (:0.0)
他们分别对应pts 3,4和5.
2.在pts/4终端中执行如下命令
luther@gliethttp:~$ cat /dev/pts/3
llllllllls
3.在pts/3终端中输入平常的命令ls
你会发现输入的数据并不能被完全显示,而2步骤中运行的cat  /dev/pts/3
命令确出现了不完整命令,这是怎么回事呢,接下来我们讲一讲该现象背后的故事[luther.gliethttp].
luther@gliethttp:~$ l
4.讲讲现象背后的故事
当ubuntu系统创建一个新的terminal时(比如上面的pts/3)
4.1 首先执行ptm = open(‘/dev/ptmx’,…)操作
4.2 接下来fork(),然后child将打开’/dev/pts/3′,dup2到0,1和2句柄上,随后执行execl启动一个shell.
pts = open(‘/dev/pts/3’,…);
dup2(pts, 0); // 对应lib库中stdin
dup2(pts, 1); // 对应lib库中stdout
dup2(pts, 2); // 对应lib库中stderr
close(pts);
execl("/system/bin/sh", "/system/bin/sh", NULL);
// 这样sh输入数据将全部来自pts,
// sh的输出数据也都全部输送到pts,也就直接送到了打开ptmx的新terminal中.
4.3 新terminal将启动GUI,捕获按键数据,然后写入ptm,这样pts将收到数据,进而sh将从stdin中获得数据,
于是sh将作进一步运算,将结果送给stdout或stderr,进而送给pts,于是ptm获得数据,然后terminal的GUI
将数据显示出来.

具体流程图如下[luther.gliethttp]:
terminal捕获到key按键值 <–> ptm <–> pts/3 <–> stdin <–> shell读到数据
shell数据结果 <–> stdout <–> pts/3 <–> ptm <–> terminal显示
4.4 好了,正常的启动流程图已经有了,来看看,我们试验时数据出现显示异常的现象背后到底是怎么发生的.
与上面正常流程不同的时,我们在另外一个地方执行了cat.

这种情况下的流程图为[luther.gliethttp]:
terminal <–> ptm <–> pts/3 <——> shell
|
<——> 运行在pts/4上的 cat /dev/pts/3
很明显terminal发送数据到pts/3之后,
因为有2个独立的进程都在等待pts/3的数据,所以他们之间就发生了对pts/3数据抢夺现象,
因为linux内核调度器根据当时情况随时都会将他们中的一个调出或者调入,因此数据
就出现了一部分被送到了pts/4的cat命令,另一部分被送到了shell,
因为shell具有回显能力,shell将它接收到的所有字符串一一回显给terminal,所以terminal显示
到的数据就是shell与cat命令争抢数据时,shell自己抢到的数据,
而pts/4上显示的数据就是cat命令抢到的数据[luther.gliethttp]

比如我们仍然在pts/4上执行cat命令,然后我们在pts/5上执行echo命令
luther@gliethttp:~$ who am i
luther   pts/5        2009-07-03 09:08 (:0.0)
luther@gliethttp:~$ echo ‘luther.gliethttp’ >/dev/pts/3
这时pts/3对应的terminal将完全显示’luther.gliethttp’字符串,因为没有人和ptm争抢数据[luther.gliethttp].
4.5 在pts/3自己所在terminal中执行cat回是什么现象呢,我么继续看看
luther@gliethttp:~$ cat /dev/pts/3
ls
ls
pwd
pwd
可以看到,输入ls回车之后,显示了2个ls,其中1个ls数据是cat命令自己回显出来的,
另外一个ls就来自/dev/pts/3文件,那这是怎么回事呢,原因是这样的,
cat和terminal都能获得键盘数据,cat将键盘数据直接回显到terminal上,
而terminal捕获的数据将通过ptm发送到pts/3,而cat自己又在等待pts/3的数据,所以
cat将从pts/3上再次读取到ptm发送过来的数据,再一次显示到terminal上,
那同样是cat pts/3,为什么就不一样呢,通过strace发现,如果在
terminal中直接调用库函数execve()执行另外一个shell命令,那么sh自身将停止对stdin进行数据读取,
只是等待shell命令退出,数据读取操作权完全交给被执行的shell命令(cat),
所以cat这时0,1,2都是对应pts/3,因为cat /dev/pts/3命令需要打开文件,
所以fd = open(‘/dev/pts/3’,…)之后,fd数值将等于3,这样cat /dev/pts/3他的
0,1,2和3这4个句柄都对应pts/3节点[0为stdin,1为stdout,2为stderr]
所以读取pts/3的进程只有了一个,没有人和他争数据了,当然cat能够完全获得数据了,呵呵[luther.gliethttp]

清理reiser的垃圾文件

今天开始清理磁盘,垃圾东东一律清除出去~

du -h 先看一下各磁盘分区占用情况:

linux-mxo6:~ # df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda8              31G   13G   18G  42% /
udev                 1008M  156K 1008M   1% /dev
/dev/sda7             510M   49M  436M  11% /boot
/dev/sda12             11G  9.1G  1.1G  90% /mnt/TEMP
/dev/sda3              46G   44G  1.6G  97% /media/0-2-0-FILES
/dev/sdb3             9.7G  2.8G  6.4G  31% /mnt/sdb3
/dev/sda11             31G   18G   13G  57% /media/0-1-6-GALLERY
/dev/sdc1             1.9G  770M  1.2G  40% /media/disk

还好,根目录占用量在预期之内。

然后开始在/ 中一级一级查找占用地盘较多的目录:

du -h / –max-depth=1 相当强大的命令…

du -h / –max-depth=1 –exclude=/mnt –exclude=/media –exclude=/proc –exclude=/vod_cache_data –exclude=/dev –exclude=/sys –exclude=/home
8.3M    /bin
156K    /dev
85M     /etc
621M    /lib
414M    /opt
64K     /srv
5.4M    /tmp
0       /sys
288M    /var
6.7G    /usr
32M     /boot
0       /home
9.6M    /sbin
4.6G    /root
13G     /

看来/root 要清理了。把火狐的编译文件删掉后多出300MB, 然后删图片缓存和解压缓存一共多出1100MB . 无奈对wine影响不大。毕竟wine里都是编译好的dll ,清理目标代码没有什么用。

du -h /root/wine-1.1.23/ –max-depth=1 –exclude=/mnt –exclude=/media –exclude=/proc –exclude=/vod_cache_data
88M     /root/wine-1.1.23/dlls
6.0M    /root/wine-1.1.23/libs
1.5M    /root/wine-1.1.23/fonts
3.2M    /root/wine-1.1.23/tools
8.8M    /root/wine-1.1.23/documentation
112K    /root/wine-1.1.23/loader
1.6M    /root/wine-1.1.23/server
8.9M    /root/wine-1.1.23/programs
9.3M    /root/wine-1.1.23/include
130M    /root/wine-1.1.23/

搜索SAP menu的text

需要根据tcode 来查sap menu 路径是件很麻烦的事,如果一级一级的去展,熟悉的还好说,不熟悉的就累死了 x_x
可以用tcode : search sap menu 来查哦.

xorg和linux input system

在 /proc内有进程打开的文件信息, 比如简单看看Xorg打开了什么文件, 可以这样:

$ps -A | grep Xorg
 6245 tty7     00:56:59 Xorg

 sudo ls /proc/6245/fd -l
[sudo] password for yhe:
total 0
l-wx—— 1 root root 64 2009-04-14 14:10 0 -> /var/log/Xorg.0.log
lrwx—— 1 root root 64 2009-04-14 14:10 1 -> socket:[16061]
lrwx—— 1 root root 64 2009-04-14 14:10 10 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 11 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 12 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 13 -> socket:[16387]
lrwx—— 1 root root 64 2009-04-14 14:10 14 -> /dev/nvidiactl
lrwx—— 1 root root 64 2009-04-14 14:10 15 -> /dev/nvidia0
lr-x—— 1 root root 64 2009-04-14 14:10 16 -> /proc/acpi/video/VID1/LCD/state
lr-x—— 1 root root 64 2009-04-14 14:10 17 -> /proc/acpi/video/VID1/LCD/info
lr-x—— 1 root root 64 2009-04-14 14:10 18 -> /proc/acpi/video/VID1/CRT/state
lr-x—— 1 root root 64 2009-04-14 14:10 19 -> /proc/acpi/video/VID1/CRT/info
l-wx—— 1 root root 64 2009-04-14 14:10 2 -> /var/log/gdm/:0.log
lr-x—— 1 root root 64 2009-04-14 14:10 20 -> /proc/acpi/video/VID1/TV/state
lr-x—— 1 root root 64 2009-04-14 14:10 21 -> /proc/acpi/video/VID1/TV/info
lr-x—— 1 root root 64 2009-04-14 14:10 22 -> /proc/acpi/video/VID/DVI/state
lr-x—— 1 root root 64 2009-04-14 14:10 23 -> /proc/acpi/video/VID/DVI/info
lr-x—— 1 root root 64 2009-04-14 14:10 24 -> /proc/acpi/video/VID/LCD/state
lr-x—— 1 root root 64 2009-04-14 14:10 25 -> /proc/acpi/video/VID/LCD/info
lr-x—— 1 root root 64 2009-04-14 14:10 26 -> /proc/acpi/video/VID/CRT/state
lr-x—— 1 root root 64 2009-04-14 14:10 27 -> /proc/acpi/video/VID/CRT/info
lr-x—— 1 root root 64 2009-04-14 14:10 28 -> /proc/acpi/video/VID/TV/state
lr-x—— 1 root root 64 2009-04-14 14:10 29 -> /proc/acpi/video/VID/TV/info
lrwx—— 1 root root 64 2009-04-14 14:10 3 -> socket:[16082]
lrwx—— 1 root root 64 2009-04-14 14:10 30 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 31 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 32 -> /dev/psaux
lrwx—— 1 root root 64 2009-04-14 14:10 33 -> /dev/nvidiactl
lrwx—— 1 root root 64 2009-04-14 14:10 34 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 35 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 36 -> socket:[16545]
lrwx—— 1 root root 64 2009-04-14 14:10 37 -> socket:[16547]
lrwx—— 1 root root 64 2009-04-14 14:10 38 -> socket:[17674]
lrwx—— 1 root root 64 2009-04-14 14:10 39 -> socket:[17679]
lrwx—— 1 root root 64 2009-04-14 14:10 4 -> /dev/tty7
…..
lrwx—— 1 root root 64 2009-04-14 14:10 5 -> /proc/bus/pci/01/00.0
….
lrwx—— 1 root root 64 2009-04-14 14:10 6 -> /dev/nvidiactl

非常值得注意的是, /dev/nvidia0, /dev/nvidiactl是NV 官方驱动引入的两个设备文件, 而/dev/tty7的含义也很明显, 为什么ctrl+alt+f7 可以切换到X系统就很明显拉.  同时Xorg这个进程还打开了acpi的CRT以及LCD相关文件, 可以获取电源管理的一些信息. /dev/psaux 则是一个虚拟设备, 由mousedev.c创建, 他获取系统的鼠标设备事件, 以PS2协议提供设备/dev/psaux给use space使用, 这个mousedev可以产生三种PS2协议: PS/2, ImPS2(Microsoft ItelliMouse, 支持滚轮), ExplorerPS/2( 支持多个鼠标button).  mousedev和linux新的input系统共存, 是一个临时的hack 行为。

/proc/bus/pci/01/00.0, 对应pci bus 1, 设备00.0,  从可以看到这设备正是nvida显卡.
$ls /sys/bus/pci/devices/0000:01:00.0/ -l
total 0
-rw-r–r– 1 root root      4096 2009-04-15 09:58 broken_parity_status
-r–r–r– 1 root root      4096 2009-04-15 09:58 class
-rw-r–r– 1 root root       256 2009-04-15 09:58 config
-r–r–r– 1 root root      4096 2009-04-15 09:58 device
lrwxrwxrwx 1 root root         0 2009-04-15 09:58 driver -> ../../../../bus/pci/drivers/nvidia
-rw——- 1 root root      4096 2009-04-15 09:58 enable
drwxr-xr-x 5 root root         0 2009-04-15 09:58 i2c-adapter
-r–r–r– 1 root root      4096 2009-04-15 09:58 irq
-r–r–r– 1 root root      4096 2009-04-15 09:58 local_cpus
-r–r–r– 1 root root      4096 2009-04-15 09:58 modalias
-rw-r–r– 1 root root      4096 2009-04-15 09:58 msi_bus
drwxr-xr-x 2 root root         0 2009-04-15 09:58 power
-r–r–r– 1 root root      4096 2009-04-15 09:58 resource
-rw——- 1 root root  16777216 2009-04-15 09:58 resource0
-rw——- 1 root root 268435456 2009-04-15 09:58 resource1
-rw——- 1 root root  33554432 2009-04-15 09:58 resource3
-rw——- 1 root root       128 2009-04-15 09:58 resource5
-r——– 1 root root    131072 2009-04-15 09:58 rom
lrwxrwxrwx 1 root root         0 2009-04-15 09:58 subsystem -> ../../../../bus/pci
-r–r–r– 1 root root      4096 2009-04-15 09:58 subsystem_device
-r–r–r– 1 root root      4096 2009-04-15 09:58 subsystem_vendor
-rw-r–r– 1 root root      4096 2009-04-15 09:58 uevent
-r–r–r– 1 root root      4096 2009-04-15 09:58 vendor

然后可以获得verndor 和device 的名字.
$cat /usr/share/misc/pci.ids | grep  10de
10de  nVidia Corporation

$cat /sys/bus/pci/devices/0000:01:00.0/device
0x042b
$ cat /usr/share/misc/pci.ids | grep  042b
    042b  Quadro NVS 135M

但是这些过程不用这么麻烦, 有现成的工具, 做得更好.

$ ps -A | grep Xorg
 6134 tty7     00:05:02 Xorg
$ sudo lsof -p 6134

然后查看硬件信息如nvidia, 可以
lspci -d 10de: -vvv -xxx -b
01:00.0 VGA compatible controller: nVidia Corporation Quadro NVS 135M (rev a1) (prog-if 00 [VGA controller])
    Subsystem: Dell Unknown device 01f9
    Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B-
    Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR-
    Latency: 0
    Interrupt: pin A routed to IRQ 5
    Region 0: Memory at f5000000 (32-bit, non-prefetchable)
    Region 1: Memory at 00000000e0000000 (64-bit, prefetchable)
    Region 3: Memory at 00000000f2000000 (64-bit, non-prefetchable)
    Region 5: I/O ports at ef00
    Capabilities: <access denied>
00: de 10 2b 04 07 00 10 00 a1 00 00 03 00 00 00 00
10: 00 00 00 f5 0c 00 00 e0 00 00 00 00 04 00 00 f2
20: 00 00 00 00 01 ef 00 00 00 00 00 00 28 10 f9 01
30: 00 00 00 00 60 00 00 00 00 00 00 00 05 01 00 00


稍微深入看Xorg如何处理鼠标键盘初始化,需要看看xorg的代码。从本文分析的具体环境如下, 从/var/log/Xorg.0.log获取:

X.Org X Server 1.4.0.90
Release Date: 5 September 2007
X Protocol Version 11, Revision 0
Build Operating System: Linux Ubuntu (xorg-server 2:1.4.1~git20080131-1ubuntu9.2)
Current Operating System: Linux yhe-laptop 2.6.24-23-generic #1 SMP Thu Feb 5 15:00:25 UTC 2009 i686

首先安装source code:sudo apt-get  source xorg-server,就能得到源代码xorg-server-1.4.1~git20080131. 不深入看xorg代码,从config 解析看看鼠标键盘的初始化吧.先看看log记录:

Markers: (–) probed, (**) from config file, (==) default setting,
        (++) from command line, (!!) notice, (II) informational,
        (WW) warning, (EE) error, (NI) not implemented, (??) unknown.

(==) Log file: "/var/log/Xorg.0.log", Time: Mon May  4 10:40:15 2009
(==) Using config file: "/etc/X11/xorg.conf"  —> xf86HandleConfigFile

(==) ServerLayout "Layout0"  —–>xf86parseLayoutSection
(**) |–>Screen "Screen0" (0)
(**) |   |–>Monitor "Monitor0"
(**) |   |–>Device "Device0"
(**) |–>Input Device "Keyboard0"
(**) |–>Input Device "Mouse0"
(==) Automatically adding devices
(==) Automatically enabling devices

对应代码是hw/xfree86/parser/scan.c , xf86openConfigFile, 通过两个宏来搜索配置文件:
#define DEFAULT_CONF_PATH    "/etc/X11/%S,"
                                                     …………………
#define XCONFIGFILE    "xorg.conf"

继续深入前,看看这个环境的xorg.conf 对应输入部分的内容:

Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0"
    InputDevice    "Keyboard0" "CoreKeyboard"
    InputDevice    "Mouse0" "CorePointer"
EndSection

Section "InputDevice"
    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"
    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection

从上面提到的函数可以追踪到键盘配置的解析过程xf86parseInputSection,将鼠标键盘的配置记录下来,接着看log:
(II) Module ABI versions:
        X.Org ANSI C Emulation: 0.3
        X.Org Video Driver: 2.0
        X.Org XInput driver : 2.0
        X.Org Server Extension : 0.3
        X.Org Font Renderer : 0.5
(II) Loader running on linux  —>xfree86/common/xf86init.c->InitOutput->LoaderInit
….
(++) using VT number 7  —>   
xf86OpenConsole();

(II) LoadModule: "kbd"
(II) Loading /usr/lib/xorg/modules/input//kbd_drv.so
(II) Module kbd: vendor="X.Org Foundation"
        compiled for 1.4.0, module version = 1.2.2
        Module class: X.Org XInput Driver
        ABI class: X.Org XInput driver, version 2.0
(II) LoadModule: "mouse"
(II) Loading /usr/lib/xorg/modules/input//mouse_drv.so
(II) Module mouse: vendor="X.Org Foundation"
        compiled for 1.4.0, module version = 1.2.3
        Module class: X.Org XInput Driver
        ABI class: X.Org XInput driver, version 2.0
……
(II) Initializing built-in extension XInputExtension
(II) Initializing built-in extension XTEST

从log可以看到, kbd是加载的kbd_drv.so, 这个driver在另外一个包中, 需要单独获取源代码:
sudo apt-get source xserver-xorg-input-kbd. 编译以下就知道是哪几个C文件构成这个xserver 的drv了:
gcc -shared  .libs/kbd.o .libs/lnx_KbdMap.o .libs/lnx_kbd.o .libs/at_scancode.o   -Wl,-soname -Wl,kbd_drv.so -o .libs/kbd_drv.so
也可以参考 man 4 kbd. 我们的配置文件如下, 选择的就是这个驱动, 在man的信息里提到,默认(没有指定device参数的)情况下, 使用tty7来作为输入设备.
Section "InputDevice"
    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection
打开设备的代码如下, 可以看到在么有指定device的情况下,就用tty7作为键盘输入源.

static Bool
OpenKeyboard(InputInfoPtr pInfo)
{
    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
    int i;
    KbdProtocolId prot = PROT_UNKNOWN_KBD;
    char *s;

    s = xf86SetStrOption(pInfo->options, "Protocol", NULL);
    for (i = 0; protocols[i].name; i++) {
        if (xf86NameCmp(s, protocols[i].name) == 0) {
           prot = protocols[i].id;
           break;
        }
    }

    switch (prot) {
        case PROT_STD:
           pInfo->read_input = stdReadInput;
           break;
        default:
           xf86Msg(X_ERROR,""%s" is not a valid keyboard protocol namen", s);
           xfree(s);
           return FALSE;
    }

    xf86Msg(X_CONFIG, "%s: Protocol: %sn", pInfo->name, s);
    xfree(s);

    s = xf86SetStrOption(pInfo->options, "Device", NULL);
    if (s == NULL) {
       pInfo->fd = xf86Info.consoleFd;

       pKbd->isConsole = TRUE;

    } else {
       pInfo->fd = open(s, O_RDONLY | O_NONBLOCK | O_EXCL);
       if (pInfo->fd == -1) {
           xf86Msg(X_ERROR, "%s: cannot open "%s"n", pInfo->name, s);
           xfree(s);
           return FALSE;
       }
       pKbd->isConsole = FALSE;
       xfree(s);
    }

    if (pKbd->isConsole)
         pKbd->vtSwitchSupported = TRUE;

    return TRUE;
}

……….
(**) Option "CoreKeyboard"
(**) Keyboard0: always reports core events
(**) Option "Protocol" "standard" 
(**) Keyboard0: Protocol: standard  xserver-xorg-input-kbd/src/lnx_kbd.c ->OpenKeyboard

(**) Option "AutoRepeat" "500 30"
(**) Option "XkbRules" "xorg"
(**) Keyboard0: XkbRules: "xorg"
(**) Option "XkbModel" "pc105"
(**) Keyboard0: XkbModel: "pc105"
(**) Option "XkbLayout" "us"
(**) Keyboard0: XkbLayout: "us"
(**) Option "CustomKeycodes" "off"
(**) Keyboard0: CustomKeycodes disabled

鼠标类似,也是单独下载驱动的源码:sudo apt-get source xserver-xorg-input-mouse, make 然后知道其组成:
gcc -shared  .libs/mouse.o .libs/pnp.o   -Wl,-soname -Wl,mouse_drv.so -o .libs/mouse_drv.so

Section "InputDevice"
    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection


(**) Option "Protocol" "auto"
(**) Mouse0: Device: "/dev/psaux"  xserver-xorg-input-mouse/src/mouse.c:MousePreInit

(**) Mouse0: Protocol: "auto"
(**) Option "CorePointer"
(**) Mouse0: always reports core events
(**) Option "Device" "/dev/psaux"
(**) Option "Emulate3Buttons" "no"
(**) Option "ZAxisMapping" "4 5"
(**) Mouse0: ZAxisMapping: buttons 4 and 5
(**) Mouse0: Buttons: 9
(**) Mouse0: Sensitivity: 1
(II) evaluating device (Mouse0)
(II) XINPUT: Adding extended input device "Mouse0" (type: MOUSE)
(II) XINPUT: Adding extended input device "Mouse0" (type: MOUSE)
(II) evaluating device (Keyboard0)
(II) XINPUT: Adding extended input device "Keyboard0" (type: KEYBOARD)
(–) Mouse0: PnP-detected protocol: "ExplorerPS/2"
(II) Mouse0: ps2EnableDataReporting: succeeded


Xorg 键盘鼠标输入处理流程

先总结后分析: xorg 处理两种输入, 一种是鼠标键盘这种设备的输入, 另一种是client的socket传入的请求. socket当然采用的是select, 来进行异步处理. 本环境下鼠标键盘的x驱动是通过tty的fd进行select. 有的系统, 鼠标输入采用异步IO, 采用的信号是SIGIO, 比如BSD. 采用SIGIO的还有些DRI设备(Direct Rendering Infrastructure 参考:http://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure )

这里采用的xorg环境上面已经有所提及, 这里提及一点历史:  xfree86 原来专用于x86,后来发展成极为通用的X系统, 但是XFree86从2004年发布的版本4.4起不再遵从GPL许可证发行, 同年Xorg发布了基于4.4版本的Xorg X系统.

所以你看到xorg代码中有多个相同定义时, 对我们的环境, 它是xfree86所定义的那个函数. 此外预先介绍下xorg Xserver的几个相关组成部分(google即得):
*xserver/dix:   Device Independent
                 1) 资源管理(字体,位图,光标之类    2. dispatch, 处理client请求(events.c)   3.处理input事件
* xserver/mi … machine independent X. Mouse cursor rendering stuff.
* xserver/hw/xfree86/common … some additional stuff, especially for X Input.
* xserver/Xi … X Input Extension protocol stuff.
文章推荐:
http://wearables.unisa.edu.au/mpx/?q=eventprocessing   MP X server 项目文档 此文档有个如何处理鼠标输入的图, 非常直观, 先奉上. 这个例子里鼠标采用的是在SIGIO信号内处理鼠标事件,和这里讨论的具体环境有差异,仅供参考. (单击看大图了)

先看对client的服务是如何分发的, 前面提到是select模式, 非常有意义的一个变量是定义在 xserver/os/connection.c中,
fd_set AllSockets;        /* select on this */
新建立的链接会吧自己的fd加入这个set, 而Dispatch会直接select on 这个set. 在同一个文件还有另外一个重要的变量:
fd_set EnabledDevices;        /* mask for input devices that are on */
用于记录打开的输入设备, 比如我们所讨论的鼠标和键盘.

还是先从main开始吧, 在server/dix/main.c中

int main(int argc, char *argv[], char *envp[])
{
……..
 while(1)
    {
    OsInit();

        config_init();
    if(serverGeneration == 1)
    {
        CreateWellKnownSockets(); /*打开server端口并加入AllSockets*/
             …..
       }
……….
    InitOutput(&screenInfo, argc, argv); /*读取配置文件, 打开console等,载入input设备驱动和各种模块
                                           (xfree86/common/xf86init.c), 注册
xf86Wakeup 到wakup handlers*/
#ifdef XPRINT
    PrinterInitOutput(&screenInfo, argc, argv);
#endif

    if (screenInfo.numScreens < 1)
        FatalError("no screens found");
    if (screenInfo.numVideoScreens < 0)
        screenInfo.numVideoScreens = screenInfo.numScreens;
    InitExtensions(argc, argv);
    if (!InitClientPrivates(serverClient))
        FatalError("failed to allocate serverClient devprivates");
    for (i = 0; i < screenInfo.numScreens; i++)
    {
        ScreenPtr pScreen = screenInfo.screens[i];
        if (!CreateScratchPixmapsForScreen(i))
        FatalError("failed to create scratch pixmaps");
        if (pScreen->CreateScreenResources &&
        !(*pScreen->CreateScreenResources)(pScreen))
        FatalError("failed to create screen resources");
        if (!CreateGCperDepth(i))
        FatalError("failed to create scratch GCs");
        if (!CreateDefaultStipple(i))
        FatalError("failed to create default stipple");
        if (!CreateRootWindow(pScreen))
        FatalError("failed to create root window");
    }
    InitCoreDevices();
    InitInput(argc, argv); /*调用每个输入设备的PreInit函数, mieqInit*/
    if (InitAndStartDevices() != Success) /*向鼠标键盘发送DEVICE_ON命令, 注册到全局输入处理表:handlers*/
        FatalError("failed to initialize core devices");

    InitFonts();

    Dispatch(); /*WaitForSomething select on AllSockets, 有键盘鼠标输入也会被信号唤醒,并处理*/

    /* Now free up whatever must be freed */
……….
}

键盘鼠标的输入函数注册
InitAndStartDevices -> KbdProc(xserver-xorg-input-kbd-1.22/src/kbc.c)(鼠标的亦同)  -> AddEnabledDevice(pInfo->fd)  : 把fd同时加入EnabledDevices, 和AllSockets 全局fdset中, 并在全局

读取数据:
InitOutput -> RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, xf86Wakeup,NULL);
    注册xf86Wakup到全局表handlers 中,在任务被唤醒后得到调用.
os/WaitFor.c :WaitForSomething -> WakeupHandler ->(*handlers[i].WakeupHandler) (handlers[i].blockData,..) -> xf86Wakeup
     根据select结果, 在全局输入设备表xf86InputDevs 中找到此设备调用其read_input函数.

唤醒:
内核根据select 结果,唤醒Xorg.

xclient传递的消息在Dispatch函数中处理.  至于鼠标键盘的read_input读取键盘/鼠标输入然后分发给xclient的过程就不做研究了.

将WEB页变为可编辑状态

比较实用的东西,做截图的时候很有用哦
 
其实就是把document的可编辑属性打开
javascript:document.body.contentEditable=’true’; document.designMode=’on’; void 0