• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

                 摘要: 一.IO與文件目錄管理 1.pread=lseek+read   pread讀取以后不改變讀寫位置 2.mmap映射:   /proc/${pid}/mem 無法映射 3.IO的有效用戶與實際用戶   默認情況:實際用戶與有效用戶一致。   &nbs...  閱讀全文

            posted @ 2013-01-02 11:01 鑫龍 閱讀(451) | 評論 (0)編輯 收藏

                 摘要: 一.IO與文件映射 1.IO的共享與效率  read與write其中數據緩沖的大小  讀取數據的緩沖大小:getpagesize。  2.定位與定位讀取(隨機讀取)  read與write在操作的時候,自動移動讀取位置.  lseek改變讀取位置.  pread/pwr...  閱讀全文

            posted @ 2012-12-31 15:30 鑫龍 閱讀(744) | 評論 (0)編輯 收藏

            rzsz 是一款 Linux 下面的文件傳輸工具。實現原理是通過 Xmodem / Ymodem / Zmodem 協議傳輸文件。lrzsz 可以在支持這三種協議的Shell界面的工具下工作,比如XShell。

                lrzsz是一個古老的軟件,最新版本0.12.20是在1998年更新的。

            大部分linux發行版的安裝工具都可以直接安裝 lrzsz,比如apt-get / pacman,當然我們也可以下載安裝包手動安裝。這次由于我所在的服務器沒有管理員權限,我就使用源碼包安裝。

            $ mkdir /home/alswl/app
            $ wget http://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz
            $ tar xzvf lrzsz-0.12.20.tar.gz
            $ cd lrzsz-0.12.20
            $ ./configure --prefix="/home/alswl/app"
            $ make
            $ make install
            $ export PATH="$PATH:/home/alswl/app/bin"

            安裝上面步驟就可以安裝完成了,重點在于 --prefix="/home/alswl/app",這可以指定安裝路徑,安裝到自己可以讀寫的地方去。

            export作用是添加安裝目錄到 $PATH 下面,這樣可以直接使用 lsz / lrz 操作。



            最近學習linux 嘗試使用lrzsz 在這里做記錄以便今后查閱。

            使用前準備

            1.      準備 lrzsz-arm-bin編譯完成的發送及接收工具 本文例子為 lrz 和lsz

            2.      SecureCRT.exe 工具(通過串口連接到終端)。

             

            開始添加工具

            使用其他工具將lrz 和lsz 拷貝到終端 /usr/bin 目錄下( 在系統環境變量路徑下即可)

            修改 lrz 和lsz 的屬性  chmod 755 lrz chmod 755 lsz 。

             

            開始驗證

            1.文件接收

            將PC端文件發送到終端某目錄本例將PC端文件fport.exe發送到終端/xino目錄

            在終端執行命令

            Cd /xino

            Lrz

            SecureCRT.exe彈出如下選擇文件窗口選擇


            選擇 文件并點擊 “添加”添加到發送列表,點擊“確定” 進行發送

             

            傳輸文件

             

            傳輸完成  當前目錄 出現 Fport.exe 文件。

             

            3.      文件發送

            在SecureCRT.exe 上選擇 “選項“à“會話選項”選擇“終端”àX/Y/Zmodem

            設置好 目錄中的 上傳及下載 目錄, 本例為F:\my documents

             

            完成后 在命令行輸入  lsz key.sh (把 key.sh 文件發送到PC端也可以是終端程序執行的 *.log)

             

            輸入命令后回車 上傳文件

            上傳完成后 去目錄F:\my documents

             查看 出現上傳的文件

             

             備注

            常用參數

            -b 以二進制方式,默認為文本方式。(Binary (tell it likeit is) file transfer override.)

            -e 對所有控制字符轉義。(Force sender to escape allcontrol characters; normally XON, XOFF, DLE, CR-@-CR, and Ctrl-X are escaped.)

            如果要保證上傳的文件內容在服務器端保存之后與原始文件一致,最好同時設置這兩個標志,如下所示方式使用:

            rz -be

            此命令執行時,會彈出文件選擇對話框,選擇好需要上傳的文件之后,點確定,就可以開始上傳的過程了。上傳的速度取決于當時網絡的狀況。

            如果執行完畢顯示“0錯誤”,文件上傳就成功了,其他顯示則表示文件上傳出現問題了。

            posted @ 2012-12-30 23:19 鑫龍 閱讀(554) | 評論 (0)編輯 收藏

            一、make與makefile
              1.回顧:
                目標的語法
                目標名:依賴目標
                  @命令
                  @命令
                  
                make的命令行使用
                  make -f make腳本文件  目標名
              2.目標的劃分
                目標依賴

              3.默認規則:
                a.不指定目標,執行第一個目標
                b.不指定make文件,默認文件是makefile Makefile
                  makefile優先
              4.目標的調用規則:(make把目標當成當前文件夾下同名的文件)
                make執行目標:
                  搜索與目標相同的文件
                  如果文件存在,則判定文件是否被修改過。
                  文件未被修改,則停止執行,輸出提示
                  文件修改過,則進行執行。(文件不存在屬于被修改過范疇)

                  比較:當前目標與依賴目標
              5.建議:
                只要有文件輸出,就把任務作為一個目標,并且把輸出的文件作為目標名。
                范例:
                input.o:input.c
                   gcc -c -fpic input.c
                libdemo.so:input.o
                   gcc -shared -olibdemo.so input.o
                demo:libdemo.so demo.c
                   gcc demo.c -ldemo -L. -odemo
              
              6.潛規則(不建議)
                適用于:.c目標與.o目標。
                查找.o目標,目標不存在,就把.o替換成.c
                如果.c存在,實施潛規則:直接調用gcc把.c執為.o
              7.變量
                變量名=值 值
                
                $(變量名)  ${變量}  
              8.偽目標:
                不把目標作為文件處理的目標稱為偽目標
                聲明偽目標
                .PHONY=目標  

            二、環境變量
              1.使用main的參數
              int main(int args,char *argv[],char **arge)
              {
              }
              命令行參數argv與環境行arge都是字符串數組.
              約定:最后一個字符串是NULL/0
              2.在C的標準庫提供:外部變量
               extern char **environ;


            #include <stdio.h>
            #include <unistd.h>
            extern char **environ;
            int main(/*int args,char *argv[],char*arge[]*/)
            {
                /*
                while(*arge)
                {
                    printf("%s\n",*arge);
                    arge++;
                }
                
            */
                /*
                int i=0;
                while(arge[i])
                {
                    printf("%s\n",arge[i]);
                    i++;
                }
                
            */
                while(*environ)
                {
                    printf("%s\n",*environ);
                    environ++;
                }
            }

            3.修改獲取某個環境變量
                getenv/setenv/unsetenv

            #include <stdlib.h>
            #include <stdio.h>
            main()
            {
                char *val;
                val=getenv("PATH");
                printf("%s\n",val);
            }
            三、IO的基礎
              1.認識內核對象
                不允許訪問內核設備和內存,但可以通過內核系統函數去訪問.
                對每個內核對象進行編號ID.
                如果訪問內核對象,只能通過ID.
                編程模型:
                  申請得到一個ID
                  在內核系統函數中使用ID得到對應內核對象數據
              2.怎么訪問文件
                使用函數,傳遞一個文件,系統打開文件,加載文件數據,
                返回一個ID.
                使用函數,傳遞ID,得到數據.
                使用函數傳遞ID,告訴系統釋放文件.
                    
                ID:文件描述符號.file description (fd)
                
                每個程序執行的時候都有一個目錄,存放打開的文件描述符號
              3.每個程序默認打開三個文件設備:
                0:標準輸入
                1:標準輸出
                2:錯誤輸出
              4.操作文件描述符號
                ssize_t write(int fd,
                 void *buf,//要寫入內核對象的數據
                 size_t size);//寫入數據大小
                返回:
                  >0 實際寫入的數據
                  -1 寫入錯誤 
                ssize_t read(int fd,
                  void *buf,//返回數據的空間
                  size_t size);//空間大小
                返回:
                  >0:實際讀取的數據
                  =0:碰到文件結束符號EOF (ctrl+d)
                  -1:讀取錯誤
                
                建議:
                  0:輸入
                  1:輸出
                  2:錯誤輸出
            課堂練習:
              1.使用write向0 1 2 寫數據
              2.使用read從0 1 讀取數據,并判定輸入的情況,然后根據相應的結果輸出提示

            #include <stdlib.h>
            #include <stdio.h>
            main()
            {
                //printf("%d\n",getpid());
                
            //while(1);
                /*
                int r=write(0,"Hello\n",6);
                write(1,"world\n",6);
                write(2,"louis\n",6);
                int a=20;
                
                write(1,&a,4);
                
            */
                char buf[32];
                //memset(buf,0,32);
                bzero(buf,32);
                
                int r=read(0,buf,30);
                printf("實際輸入:%d\n",r);
                if(r>0)
                {
                    buf[r]=0;
                    printf("::%s\n",buf);
                }
                if(r==0)
                {
                    printf("ctrl+d\n");
                }
                if(r==-1)
                {
                    printf("輸入錯誤!\n");
                }
            }

            三.基于文件的描述符號
              1.得到文件描述符號/釋放文件描述符號
               a.文件類型
                目錄文件d
                普通文件f
                字符設備文件c
                塊設備文件b
                軟連接文件l
                管道文件p
                socket文件s   
               b.文件的屬性
                 1.屬性的表達方式:絕對模式(0666類似的八進制數),字符模式(rwx)
                   0    0   0    0
                      擁有者   組   其他用戶
                   0666
                 2.文件的權限屬性:
                   讀
                   寫
                   執行
                   粘附位權限
                   用戶設置位權限
                   組設置位權限
                  0   0         0      0       0
                    特殊權限   Owner  group  其他用戶

                   s:
                   S
                   t
                   T
                  2.1.  s設置位
                      2:組設置位
                      4:用戶設置位
                     s對執行有效
                     無效的設置位使用S表示
                     
                     設置位向其他用戶開放擁有者權限的權限.用戶設置位
                     設置位向其他用戶開放組用戶權限的權限.組用戶設置位
                     設置位只對執行程序有意義(執行權限有意義)
                     
                  2.2.  t設置位
                      1:表示沾附位設置
                      t對寫文件有意義
                     沒有執行權限的沾附位使用T表示.
                     沾附的目的:防止有些權限的用戶刪除文件.
                  
                  程序在執行的時候到底擁有的是執行者用戶的權限
                  還是文件擁有者的權限.(看setUID)
                   程序執行中有兩個用戶:
                      實際用戶:標示進程到底是誰
                      有效用戶:標示進程訪問資源的權限
                   上述一般情況是一樣的,有時候被setUID改變      
               總結:
                 沾附位的作用: 防止其他有寫權限用戶刪除文件
                 設置位的作用: 向其他執行者開發組或者用戶的權限.
            練習:
              1.使用cat創建一個文件
              2.設置沾附位,并觀察屬性
              3.設置用戶設置位, 并觀察屬性
              4.設置組設置位, 并觀察屬性
              5.考慮w權限與沾附位的關系
              6.考慮x權限與設置位的關系.

              2.通過文件描述符號讀寫各種數據.      
                open函數與creat函數

             int open(
                                const char *filename,//文件名
                                int flags,//open的方式[創建/打開]
                                mode_t mode//權限(只有創建的時候有效)
                                )

             返回:
                  >=0:內核文件描述符號.
                  =-1:打開/創建失敗
              
                open的方式:
                  必選方式:O_RDONLY O_WRONLY O_RDWR,必須選擇一個
                  創建/打開:O_CREAT
                  可選方式:
                      對打開可選方式:O_APPEND  O_TRUNC(清空數據)
                      對創建可選方式:O_EXCL
                 組合:
                    創建:
                      O_RDWR|O_CREAT
                      O_RDWR|O_CREAT | O_EXCL 
                    
                    打開:          
                      O_RDWR
                      O_RDWR|O_APPEND
                      O_RDWR|O_TRUNC
                 權限:
                   建議使用8進制數
                關閉 
                void  close(int fd);           

            案例1:
              創建文件
            案例2:
              創建文件并寫入數據
                20  short float
                tom  20   99.99
                bush  70   65.00
                達內  40   100.00
              注意:
                文件的創建的權限受系統的權限屏蔽的影響
                umask    //顯示屏蔽權限.
                umask 0666  //設置權限屏蔽.   
                
                ulimit -a 顯示所有的其他限制. 


            /*創建文件*/
            #include <fcntl.h>
            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            main()
            {
                int fd;
                
                char name[20];
                short age;
                float score;
                char sex;
                
                fd=open("test.dat",
                    O_RDWR|O_CREAT|O_EXCL,
            );
                if(fd==-1) printf("open error:%m\n"),exit(-1);
                
                
                //寫第一條
                memcpy(name,"tom",strlen("tom")+1);
                age=20;
                score=99.99;
                sex='F';
                
                write(fd,name,sizeof(name));
                write(fd,&age,sizeof age);
                write(fd,&score,sizeof(float));
                write(fd,&sex,sizeof(sex));
                
                //寫第二條
                memcpy(name,"Bush",strlen("Bush")+1);
                age=70;
                score=65.00;
                sex='M';
                write(fd,name,sizeof(name));
                write(fd,&age,sizeof age);
                write(fd,&score,sizeof(float));
                write(fd,&sex,sizeof(sex));
                
                //寫第三條
                
                memcpy(name,"達內",strlen("達內")+1);
                age=10;
                score=99.00;
                sex='F';
                write(fd,name,sizeof(name));
                write(fd,&age,sizeof age);
                write(fd,&score,sizeof(float));
                write(fd,&sex,sizeof(sex));
                    
                close(fd);
            }
            案例3:
              打開文件讀取數據
              重點:
                怎么打開讀取
                文件尾的判定
              
              基本類型的數據讀寫.
            #include <fcntl.h>
            #include <stdlib.h>
            #include <unistd.h>
            #include <stdio.h>

            main()
            {
                char name[20];
                short age;
                float score;
                char sex;
                int fd;
                int r;
                fd=open("test.dat",O_RDONLY);
                if(fd==-1) printf("open error:%m\n"),exit(-1);
                
                while(1)
                {
                    r=read(fd,name,sizeof(name));
                    if(r==0) break;
                    r=read(fd,&age,sizeof(short));
                    r=read(fd,&score,sizeof(float));
                    r=read(fd,&sex,sizeof(sex));
                    printf("%s,\t%4hd,\t%.2f,\t%1c\n",
                            name,age,score,sex);
                }
                
                close(fd);
            }
            案例4:
              結構體讀取
              描述:從鍵盤讀取若干條數據,保存到文件
                 數據追加
            View Code 

            #include <stdio.h>
            #include <unistd.h>
            #include <fcntl.h>
            #include <stdlib.h>
            #include <string.h>
            struct stu
            {
                int no;
                char name[20];
                float score;
            };
            /*
            .判定文件是否存在,存在打開,不存在創建
            .輸入記錄
            .保存記錄
            .提示繼續輸入
            .繼續/不繼續
            .關閉文件
            */
            int openfile(const char *filename)
            {
                int fd;
                fd=open(filename,O_RDWR|O_CREAT|O_EXCL,0666);
                if(fd==-1)//表示文件存在,則打開
                {
                    fd=open(filename,O_RDWR|O_APPEND);
                    return fd;
                }
                return fd;
            }
            void input(struct stu *record)
            {
                bzero(record,sizeof(struct stu));
                printf("輸入學號:");
                scanf("%d",&(record->no));
                printf("輸入姓名:");
                scanf("%s",record->name);
                printf("輸入成績:");
                scanf("%f",&(record->score));
            }
            void save(int fd,struct stu *record)
            {
                write(fd,record,sizeof(struct stu));
            }
            int iscontinue()
            {
                char c;
                printf("是否繼續輸入:\n");
                //fflush(stdin);
                
            //fflush(stdout);
                scanf("\n%c",&c);    
                if(c=='Y' || c=='y')
                {
                    return 1;
                }
                return 0;
            }

            int main()
            {
                int fd;
                int r;
                struct stu s={0};
                fd=openfile("stu.dat");
                if(fd==-1) printf("openfile:%m\n"),exit(-1);
                
                while(1)
                {
                    input(&s);
                    save(fd,&s);
                    r=iscontinue();
                    if(r==0) break;
                    system("clear");
                }
                close(fd);
                printf("輸入完畢!\n");    
            }
            3.文件描述符號與重定向
                 1.判定文件描述符號與終端的邦定關系
                 int isatty(int fd)
                 返回非0:fd輸出終端
                    0:fd輸出被重定向
                 2.防止重定向
                   /dev/tty

            #include <stdio.h>
            #include <unistd.h>
            #include <fcntl.h>
            #include <stdlib.h>
            #include <string.h>

            int main()
            {
                int fd;
                printf("Hello\n");
                write(1,"World\n",6);
                fd=open("/dev/tty",O_WRONLY);
                if(isatty(1))
                {
                    write(1,"notredir\n",9);
                }
                else
                {
                    write(1,"redir\n",6);
                }
                write(fd,"Killer\n",7);
            }

            總結:
              1.make的多目標依賴規則以及偽目標
              2.文件的創建與打開(了解設置位的作用)
              3.文件的讀寫(字符串/基本類型/結構體)
              4.了解描述符號與重定向

            作業:
              1.完成上課的練習.
              2.寫一個程序使用結構體讀取1種的數據,
                 并全部打印數據,
                 并打印平均成績
              3.寫一個程序:
                查詢1種的數據.比如:輸入姓名,查詢成績
              4.寫一個程序,錄入保存如下數據:
                書名  出版社  價格  存儲量  作者  
              5.寫一個程序負責文件拷貝
                main 存在的文件  新的文件名
                要求:
                  文件存在就拷貝,不存在提示錯誤.

             

            posted @ 2012-12-30 16:39 鑫龍 閱讀(469) | 評論 (0)編輯 收藏

            一、引言

            通常情況下,對函數庫的鏈接是放在編譯時期(compile time)完成的。所有相關的對象文件(object file)與牽涉到的函數庫(library)被鏈接合成一個可執行文件(executable file)。程序運行時,與函數庫再無瓜葛,因為所有需要的函數已拷貝到自己門下。所以這些函數庫被成為靜態庫(static libaray),通常文件名為“libxxx.a”的形式。

            其實,我們也可以把對一些庫函數的鏈接載入推遲到程序運行的時期(runtime)。這就是如雷貫耳的動態鏈接庫(dynamic link library)技術。

            二、動態鏈接庫的特點與優勢

            首先讓我們來看一下,把庫函數推遲到程序運行時期載入的好處:

            1. 可以實現進程之間的資源共享。

            什么概念呢?就是說,某個程序的在運行中要調用某個動態鏈接庫函數的時候,操作系統首先會查看所有正在運行的程序,看在內存里是否已有此庫函數的拷貝了。如果有,則讓其共享那一個拷貝;只有沒有才鏈接載入。這樣的模式雖然會帶來一些“動態鏈接”額外的開銷,卻大大的節省了系統內存資源。C的標準庫就是動態鏈接庫,也就是說系統中所有運行的程序共享著同一個C標準庫的代碼段。

            2. 將一些程序升級變得簡單。用戶只需要升級動態鏈接庫,而無需重新編譯鏈接其他原有的代碼就可以完成整個程序的升級。Windows 就是一個很好的例子。

            3. 甚至可以真正坐到鏈接載入完全由程序員在程序代碼中控制。

            程序員在編寫程序的時候,可以明確的指明什么時候或者什么情況下,鏈接載入哪個動態鏈接庫函數。你可以有一個相當大的軟件,但每次運行的時候,由于不同的操作需求,只有一小部分程序被載入內存。所有的函數本著“有需求才調入”的原則,于是大大節省了系統資源。比如現在的軟件通常都能打開若干種不同類型的文件,這些讀寫操作通常都用動態鏈接庫來實現。在一次運行當中,一般只有一種類型的文件將會被打開。所以直到程序知道文件的類型以后再載入相應的讀寫函數,而不是一開始就將所有的讀寫函數都載入,然后才發覺在整個程序中根本沒有用到它們。

            三、動態鏈接庫的創建

            由于動態鏈接庫函數的共享特性,它們不會被拷貝到可執行文件中。在編譯的時候,編譯器只會做一些函數名之類的檢查。在程序運行的時候,被調用的動態鏈接庫 函數被安置在內存的某個地方,所有調用它的程序將指向這個代碼段。因此,這些代碼必須實用相對地址,而不是絕對地址。在編譯的時候,我們需要告訴編譯器, 這些對象文件是用來做動態鏈接庫的,所以要用地址不無關代碼(Position Independent Code (PIC))。

            對gcc編譯器,只需添加上 -fPIC 標簽,如:

            gcc -fPIC -c file1.c
            gcc -fPIC -c file2.c
            gcc -shared libxxx.so file1.o file2.o

            注意到最后一行,-shared 標簽告訴編譯器這是要建立動態鏈接庫。這與靜態鏈接庫的建立很不一樣,后者用的是 ar 命令。也注意到,動態鏈接庫的名字形式為 “libxxx.so” 后綴名為 “.so”

            四、動態鏈接庫的使用

            使用動態鏈接庫,首先需要在編譯期間讓編譯器檢查一些語法與定義。

            這與靜態庫的實用基本一樣,用的是 -Lpath 和 -lxxx 標簽。如:

            gcc file1.o file2.o -Lpath -lxxx -o program.exe

            編譯器會先在path文件夾下搜索libxxx.so文件,如果沒有找到,繼續搜索libxxx.a(靜態庫)。

            在程序運行期間,也需要告訴系統去哪里找你的動態鏈接庫文件。在UNIX下是通過定義名為 LD_LIBRARY_PATH 的環境變量來實現的。只需將path賦值給此變量即可。csh 命令為:

            setenv LD_LIBRARY_PATH your/full/path/to/dll

            一切安排妥當后,你可以用 ldd 命令檢查是否連接正常。

            ldd program.exe


            動態鏈接庫*.so的編譯與使用- -


            動態庫*.so在linux下用c和c++編程時經常會碰到,最近在網站找了幾篇文章介紹動態庫的編譯和鏈接,總算搞懂了這個之前一直不太了解得東東,這里做個筆記,也為其它正為動態庫鏈接庫而苦惱的兄弟們提供一點幫助。
            1、動態庫的編譯

            下面通過一個例子來介紹如何生成一個動態庫。這里有一個頭文件:so_test.h,三個.c文件:test_a.c、test_b.c、test_c.c,我們將這幾個文件編譯成一個動態庫:libtest.so。

            so_test.h:

            #include 
            #include 

            void test_a();
            void test_b();
            void test_c();


            test_a.c:

            #include "so_test.h"
            void test_a()
            {
            printf("this is in test_a...\n");
            }


            test_b.c:
            #include "so_test.h"
            void test_b()
            {
            printf("this is in test_b...\n");
            }

            test_a.c:

            #include "so_test.h"
            void test_c()
            {
            printf("this is in test_c...\n");
            }

            將這幾個文件編譯成一個動態庫:libtest.so
            $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

            2、動態庫的鏈接

            在1、中,我們已經成功生成了一個自己的動態鏈接庫libtest.so,下面我們通過一個程序來調用這個庫里的函數。程序的源文件為:test.c。

            test.c:

            #include "so_test.h"
            int main()
            {
            test_a();
            test_b();
            test_c();
            return 0;

            }

            l 將test.c與動態庫libtest.so鏈接生成執行文件test:

            $ gcc test.c -L. -ltest -o test

            l 測試是否動態連接,如果列出libtest.so,那么應該是連接正常了

            $ ldd test

            l 執行test,可以看到它是如何調用動態庫中的函數的。
            3、編譯參數解析
            最主要的是GCC命令行的一個選項:
            -shared 該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當于一個可執行文件

            l -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

            l -L.:表示要連接的庫在當前目錄中

            l -ltest:編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱

            l LD_LIBRARY_PATH:這個環境變量指示動態連接器可以裝載動態庫的路徑。

            l 當然如果有root權限的話,可以修改/etc/ld.so.conf文件,然后調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
            4、注意

            調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數引導,并指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。

            posted @ 2012-12-30 15:16 鑫龍 閱讀(441) | 評論 (0)編輯 收藏

              檢查是否已經安裝以下的輔助軟件包
                      [root@localhost ~]# rpm -q ncurses
                      ncurses-5.5-24.
                      [root@localhost ~]#  rpm -q ncurses-devel
                      package ncurses-devel is not installed
                提示ncurses-devel沒有安裝,用yum安裝:
                      [root@localhost ~]#  yum install ncurses-devel
                      Setting up Install Process
                      Total download size: 1.6 M
                      Is this ok [y/N]: y
                      Downloading Packages:
                      Installed:  ncurses-devel.i386 0:5.5-24.
                      Complete! 

            鏈接要加上ncurses庫, cc -l ncurses xxxx.c

            轉自:http://blog.csdn.net/xumaojun/article/details/6229789

            posted @ 2012-12-29 10:56 鑫龍 閱讀(375) | 評論 (0)編輯 收藏

            在寫hadoop程序編譯時,往往需要HADOOP_CLASSPATH路徑,可通過以下方式進行在編譯腳本中設置:
            for f in $HADOOP_HOME/hadoop-*.jar; do
            CLASSPATH=${CLASSPATH}:$f
            done

            for f in $HADOOP_HOME/lib/*.jar; do
            CLASSPATH=${CLASSPATH}:$f
            done

            for f in $HIVE_HOME/lib/*.jar; do
            CLASSPATH=${CLASSPATH}:$f
            done
             
            轉自:http://blog.sina.com.cn/s/blog_62a9902f01017x7j.html

            posted @ 2012-12-28 20:44 鑫龍 閱讀(4804) | 評論 (1)編輯 收藏

            brk/sbrk
            維護一個位置。 brk/sbrk改變這個位置。
            brk改變絕對位置
            sbrk改變相對位置

            昨天的補充:
            永遠記住:C的基本類型就那幾種。
            所有全新類型都是使用typedef重新定義的。
            類型重定義的好處:
            1. 維護方便
            2. 便于移植(每個系統中都用同一個名,不用修改)
            3. 容易理解

            一、 映射虛擬內存
            沒有任何額外維護數據的內存分配 mmap/munmap
            1. 函數說明:

            void *mmap(
            void *start, //指定映射的虛擬地址,如果為0,則由系統指定開始位置
            size_t length,//指定映射空間的大小。 pagesize的倍數
            int prot, //映射的權限 PROT_NONE PROT_READ PROT_WRITE PROT_WRITE PROT_EXEC
            int flags, //映射的方式
            int fd, //文件描述符號
            offset_t off //文件中的映射開始位置(必須是0或pagesezi的倍數)
            );

            關于映射的方式flags:
            內存映射:又叫匿名映射,最后兩個參數無效
            文件映射:映射到某個文件
            只有文件映射,最后兩個參數才有效
            MAP_ANONYMOUS:內存映射
            MAP_SHAREDMAP_PRIVATE:二選一,文件映射

            2. 案例:

            #include <unistd.h>
            #include <sys/mman.h>
            #include <stdlib.h>
            #include <stdio.h>

            int main()
            {
            int *p = mmap(
            NULL,
            getpagesize(),
            PROT_READ|PROT_WRITE,
            MAP_ANONYMOUS|MAP_SHARED,
            0,
            0);
            *P = 20;
            *(p+1) = 30;
            *(p+2) = 40;
            printf("%d\n", p[2]); //打印出40
            munmap(p, 4096);
            }
            3. 總結:
            選擇什么樣的內存管理方法?
            STL
            new

            malloc小而多的數據
            brk/sbrk同類型的大塊數據,動態移動指針
            mmap/munmap 控制內存的訪問/使用文件映射/控制內存共享

            二、編程工具與動態庫
            1. gcc
            2. make
            3. gdb
            4. 其他工具
            5. 動態庫(共享庫)

            1. gcc
                -o 輸出文件名
                -O-O1-O2-O3//編譯優化
                -g-g1-g2-g3//產生調試信息 
                -Wallerror//-Wall 顯示所有警告-Werror 將警告當成錯誤提示
                -w//關閉所有警告
                -c//只編譯不連接,產生 .o文件(目標文件)
                -E//預編譯
                -S//匯編。 產生 .s文件(匯編文件)

            編譯4過程是 -E(產生.i) -c(產生.o) -S(產生.s) 自動調用連接器ld

                -D//在命令行定義宏 (宏可以在代碼中定義,也可以在命令行上定義)
                -x//指定編譯的語言類型 C, C++, .S(匯編), none(自動判定)
                -std=c89  使用標準C89
                -std=c99  使用標準C99

            三、 靜態庫的編譯
            1. 編譯過程 (*.a) a是achieve的縮寫
              1.1 編譯成目標文件
                -static 可選
                gcc -c -static 代碼文件.c  //生產可用于歸檔的目標代碼:代碼文件.0
              1.2 歸檔成靜態庫
                ar工具 (常用-r -t選項)
                ar -r 靜態庫 被歸檔的文件名(上一步代碼文件.o) 
                ar -r add.a add.o
                nm工具(查看庫中所蘊含的函數列表)
                nm 靜態庫或動態庫或目標文件或執行文件
              1.3 使用靜態庫
                gcc 代碼文件 靜態庫
            小例子:
            使用靜態庫完成如下程序
            輸入一個菱形半徑,打印菱形
            輸入整型封裝成IOTool
            菱形打印封裝成Graphic
            計劃:
            1. 實現輸入
            2. 實現菱形
            3. 編譯靜態庫
            4. 調用靜態庫

            //iotool.c
            #include <stdio.h>
            int inputInt(const char *info)
            {
            int r; //返回值
            printf("%s:", info);
            scanf("%d", &r);
            return r;
            }
            //graphic.c
            #include <stdio.h>
            void diamond(int r)
            {
            int x, y;
            for(y=0; y<=2*r; y++)
            {
            for(x=0; x<=2*r; x++)
            {
            if(y == x+r || y == x-r ||y == -x+r || y == -x+3*r)
            {
            printf("*");
            }
            else
            {
            printf(" ");
            }
            }
            printf("\n");

            }
            }

            編譯: gcc -c -static iotool.c

            gcc -c -static graphic.c
            ar -r demo1.a iotool.o graphic.o
            ar -t demo1.a //相當于nm demo1.a

            //main.c
            main()
            {
            int r = inputInt("輸入菱形半徑:");
            diamond(r);
            }

            編譯: gcc main.c demo1.a -o main

            執行:./main
            把靜態庫作為代碼的一部分來編譯

            總結:
            1. 什么是庫?
            函數等代碼封裝的二進制已經編譯的歸檔文件
            2. ar歸檔工具
            3. 采用庫的方式管理代碼優點:
            容易組織代碼
            復用
            保護代碼版權
            4. 靜態庫的“靜態”的含義:
            編譯好的程序運行的時候不依賴庫
            庫作為程序的一部分編譯連接
            5. 靜態庫的本質
            就是目標文件的集合(歸檔)
            6. -static可選

            2. 庫的規范與約定
            庫命名規則:
            lib庫名.a.主版本號.副版本號.批號
            一般就寫“lib庫名.a”就行了。
            ar -r libdemo2.a iotool.o graphic.o
            庫的使用規則
            -l庫名
            -L庫所在的目錄
            gcc main.c -o main -l demo2 -L.

            四、 動態庫的編譯
            1. 什么是動態庫(共享庫)
            動態庫是可以執行的,靜態庫不能執行
            但動態庫沒有main,不能獨立執行
            動態庫不會連接成程序的一部分
            程序執行時,必須需要動態庫文件
            2. 工具
            ldd查看程序需要調用的動態庫 ,ldd只能查看可執行文件(共享庫文件或elf文件)
            nm (查看庫中的函數符號)
            3. 動態庫的編譯
            3.1編譯
            -c -f pic(可選) (-f 指定文件格式 pic 位置無關代碼)
            3.2 連接
            -shared

            編譯:gcc -c -fpic iotool.c
            gcc -c -fpic graphic.c
            (非標準)gcc -shared -odemo3.so iotool.o graphic.o
            (標準)gcc -shared -olibdemo4.so iotool.o graphic.o
            4. 使用動態庫
            gcc 代碼文件名 動態庫文件名
            gcc 代碼文件名 -l庫名 -L動態庫所在的路徑
            gcc main.c -ldemo4 -L. -o main

            標準命名規則:
            lib庫名.so
            lib庫名.a

            問題:
            4.1 執行程序怎么加載動態庫?
            4.2 動態庫沒有作為執行程序的一部分,為什么連接需要制定動態庫及目錄?
            因為連接器需要確認函數在動態庫中的位置
            動態庫的加載:
            1. 找到動態庫
            2. 加載動態庫到內存(系統實現)
            3. 映射到用戶的內存空間(系統實現)
            動態庫查找規則:
            /lib
            /user/lib
            LD_LIBRARY_PATH環境變量指定的路徑中找
            設置當前路徑為環境變量:(自己定義的庫最好設置好目錄,或者放到上述公共目錄)
            export LD_LIBRARY_PATH=.:~:..:~Walle
            緩沖機制:
            系統把lib:/user/lib:LD_LIBRARY_PATH里的文件加載到緩沖
            /sbin/ldconfig -v 刷新緩沖so中的搜索庫的路徑
            小練習:
            輸入兩個數,計算兩個數的和。
            要求:輸入與計算兩個數的和封裝成動態庫調用

            五、 使用libdl.so庫
            動態庫加載原理
            動態庫中函數的查找已經封裝成哭libdl.so
            libdl.so里面有4個函數:
            dlopen//打開一個動態庫
            dlsym//在打開的動態庫里找一個函數
            dlclose//關閉動態庫
            dlerror//返回錯誤


            //dldemo.c
            #include <dlfcn.h>
            main()
            {
            void *handle = dlopen("./libdemo4.so", RTLD_LAZY);
            void (*fun)(int) = dlsym(handle, "diamond");
            fun(5);
            dlclose(handle);
            }
            gcc dldemo.c -o main -ldl

            ldd main
            ./main

            總結:
            1. 編譯連接動態庫
            2. 使用動態庫
            3. 怎么配置讓程序調用動態庫
            4. 掌握某些工具的使用 nm ldd lddconfig objdump strit(去掉多余的信息)

            六、 工具make的使用與makefile腳本
            背景:
            make編譯腳本解釋
            編譯腳本makefile
            make -f 腳本文件 目標
            腳本文件:
            1. 文本文件 (例如 demo.mk)
            2. 基本構成語法
            基本單位目標target
            目標名:依賴目標
            \t目標指令
            \t目標指令

            //demo.mk
            demo:iotool.c graphic.c main.c
            gcc iotool.c -c
            gcc graphic.c -c
            gcc iotool.o graphic.o -shared -o libdemo.so
            gcc main.c -ldemo -L. -o main
            make -f demo.mk demo 會生產main可執行文件

            posted @ 2012-12-27 13:27 鑫龍 閱讀(433) | 評論 (0)編輯 收藏

            brk() , sbrk() 的聲明如下:
            #include <unistd.h>
            int brk(void *addr);
            void *sbrk(intptr_t increment);
            這兩個函數都用來改變 "program break" (程序間斷點)的位置,這個位置可參考下圖:

            如 man 里說的:

            引用
            brk()  and  sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment).  
            brk() 和 sbrk() 改變 "program brek" 的位置,這個位置定義了進程數據段的終止處(也就是說,program break 是在未初始化數據段終止處后的第一個位置)。
            如此翻譯過來,似乎會讓人認為這個 program break 是和上圖中矛盾的,上圖中的 program break 是在堆的增長方向的第一個位置處(堆和棧的增長方向是相對的),而按照說明手冊來理解,似乎是在 bss segment 結束那里(因為未初始化數據段一般認為是 bss segment)。


            首先說明一點,一個程序一旦編譯好后,text segment ,data segment 和 bss segment 是確定下來的,這也可以通過 objdump 觀察到。下面通過一個程序來測試這個 program break 是不是在 bss segment 結束那里:

            #include <stdio.h>
            #include <unistd.h>
            #include <stdlib.h>
            #include <sys/time.h>
            #include <sys/resource.h>
             
             
            int bssvar;    //聲明一個味定義的變量,它會放在 bss segment 中
             
             
            int main(void)
            {
                char *pmem;
                long heap_gap_bss;
             
             
                printf ("end of bss section:%p\n", (long)&bssvar + 4);
             
             
                pmem = (char *)malloc(32);          //從堆中分配一塊內存區,一般從堆的開始處獲取
                if (pmem == NULL) {
                    perror("malloc");
                    exit (EXIT_FAILURE);
                }
             
             
                printf ("pmem:%p\n", pmem);
             
             
            //計算堆的開始地址和 bss segment 結束處得空隙大小,注意每次加載程序時這個空隙都是變化的,但是在同一次加載中它不會改變
                heap_gap_bss = (long)pmem - (long)&bssvar - 4;          
                printf ("1-gap between heap and bss:%lu\n", heap_gap_bss);
             
             
                free (pmem);   //釋放內存,歸還給堆
                 
                sbrk(32);        //調整 program break 位置(假設現在不知道這個位置在堆頭還是堆尾)
                 pmem = (char *)malloc(32);   //再一次獲取內存區
                    if (pmem == NULL) {
                            perror("malloc");
                            exit (EXIT_FAILURE);
                    }
             
             
                    printf ("pmem:%p\n", pmem);   //檢查和第一次獲取的內存區的起始地址是否一樣
                heap_gap_bss = (long)pmem - (long)&bssvar - 4;  //計算調整 program break 后的空隙
                printf ("2-gap between heap and bss:%lu\n", heap_gap_bss);
             
             
                free(pmem);   //釋放
                return 0;
            }
            下面,我們分別運行兩次程序,并查看其輸出:


            引用
            [beyes@localhost C]$ ./sbrk 
            end of bss section:0x8049938
            pmem:0x82ec008
            1-gap between heap and bss:2762448
            pmem:0x82ec008
            2-gap between heap and bss:2762448
            [beyes@localhost C]$ ./sbrk 
            end of bss section:0x8049938
            pmem:0x8dbc008
            1-gap between heap and bss:14100176
            pmem:0x8dbc008
            2-gap between heap and bss:14100176
            從上面的輸出中,可以發現幾點:
            1. bss 段一旦在在程序編譯好后,它的地址就已經規定下來。
            2. 一般及簡單的情況下,使用 malloc() 申請的內存,釋放后,仍然歸還回原處,再次申請同樣大小的內存區時,還是從第 1 次那里獲得。
            3. bss segment 結束處和堆的開始處的空隙大小,并不因為 sbrk() 的調整而改變,也就是說明了 program break 不是調整堆頭部。

            所以,man 手冊里所說的  “program break 是在未初始化數據段終止處后的第一個位置” ,不能將這個位置理解為堆頭部。這時,可以猜想應該是在堆尾部,也就是堆增長方向的最前方。下面用程序進行檢驗:

            當 sbrk() 中的參數為 0 時,我們可以找到 program break 的位置。那么根據這一點,檢查一下每次在程序加載時,系統給堆的分配是不是等同大小的:
            #include <stdio.h>
            #include <unistd.h>
            #include <stdlib.h>
            #include <sys/time.h>
            #include <sys/resource.h>
             
             
            int main(void)
            {
                    void *tret;
                    char *pmem;
             
             
             
                    pmem = (char *)malloc(32);
                    if (pmem == NULL) {
                            perror("malloc");
                            exit (EXIT_FAILURE);
                    }
             
             
                    printf ("pmem:%p\n", pmem);
             
                    tret = sbrk(0);
                    if (tret != (void *)-1)
                            printf ("heap size on each load: %lu\n", (long)tret - (long)pmem);
             
             
                return 0;
            }
            運行上面的程序 3 次:

            引用
            [beyes@localhost C]$ ./sbrk 
            pmem:0x80c9008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x9682008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x9a7d008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x8d92008
            heap size on each load: 135160
            [beyes@localhost C]$ vi sbrk.c
            從輸出可以看到,雖然堆的頭部地址在每次程序加載后都不一樣,但是每次加載后,堆的大小默認分配是一致的。但是這不是不能改的,可以使用 sysctl 命令修改一下內核參數:
            引用
            #sysctl -w kernel/randomize_va_space=0
            這么做之后,再運行 3 次這個程序看看:
            引用
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            從輸出看到,每次加載后,堆頭部的其實地址都一樣了。但我們不需要這么做,每次堆都一樣,容易帶來緩沖區溢出攻擊(以前老的 linux 內核就是特定地址加載的),所以還是需要保持 randomize_va_space 這個內核變量值為 1 。

            下面就來驗證 sbrk() 改變的 program break 位置在堆的增長方向處:
            #include <stdio.h>
            #include <unistd.h>
            #include <stdlib.h>
            #include <sys/time.h>
            #include <sys/resource.h>
             
             
            int main(void)
            {
                    void *tret;
                    char *pmem;
                    int i;
                    long sbrkret;
             
                   pmem = (char *)malloc(32);
                    if (pmem == NULL) {
                            perror("malloc");
                            exit (EXIT_FAILURE);
                    }
             
             
                    printf ("pmem:%p\n", pmem);
             
                     for (i = 0; i < 65; i++) {
                            sbrk(1);
                            printf ("%d\n", sbrk(0) - (long)pmem - 0x20ff8);   //0x20ff8 就是堆預分配的固定大小;改變后要用 sbrk(0) 再次獲取更新后的program break位置
                    }
                   free(pmem);
             
                    
                   return 0;
            }
            運行輸出:

            引用
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            1
            2
            3
            4
            5

            ... ...
            61
            62
            63
            64

            從輸出看到,sbrk(1) 每次讓堆往棧的方向增加 1 個字節的大小空間。

            而 brk() 這個函數的參數是一個地址,假如你已經知道了堆的起始地址,還有堆的大小,那么你就可以據此修改 brk() 中的地址參數已達到調整堆的目的。

            實際上,在應用程序中,基本不直接使用這兩個函數,取而代之的是 malloc() 一類函數,這一類庫函數的執行效率會更高。 還需要注意一點,當使用 malloc() 分配過大的空間,比如超出 0x20ff8 這個常數(在我的系統(Fedora15)上是這樣,別的系統可能會有變)時,malloc 不再從堆中分配空間,而是使用 mmap() 這個系統調用從映射區尋找可用的內存空間。


            brk/sbrk我覺得:一個程序可尋址的邏輯地址范圍是3G(本來是4G,有1G是內核空間不能用),但是這只是邏輯地址,并不可能全部給你用,所以就把預定約定可以給你用的那部分空間(code\data\bss\heap\stack)映射成了物理地址。但是預先約定的總有不夠用的時候,此時可以通過brk/sbrk來增加邏輯地址到物理地址映射的范圍,即擴大該程序堆空間的大小。

            posted @ 2012-12-27 12:15 鑫龍 閱讀(815) | 評論 (0)編輯 收藏

            (1) _stdcall調用
              _stdcall 是Standard Call的縮寫,是C++的標準調用方式,也是Pascal程序的缺省調用方式,參數采用從右到左的壓棧方式,如果是調用類成員的話,最后一個入棧的是this指針。這些堆棧中的參數由被調函數自身在返回前清空,
            使用的指令是 retn X,X表示參數占用的字節數,CPU在ret之后自動彈出X個字節的堆棧空間。稱為自動清棧。
              WIN32 Api都采用_stdcall調用方式,這樣的宏定義說明了問題:
              #define WINAPI _stdcall
              按C編譯方式,_stdcall調用約定在輸出函數名前面加下劃線,后面加“@”符號和參數的字節數,形如_functionname@number。
            (2) _cdecl調用
              _cdecl是C Declaration[聲明]的縮寫,表示C/C++語言默認的函數調用方法,也是C/C++的缺省調用方式,所有參數從右到左依次入棧,這些參數由調用者清除,稱為手動清棧。_cedcl約定的函數只能被C/C++調用,每一個調用它的
            函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用_stdcall函數的大。被調用函數無需要求調用者傳遞多少參數,調用者傳遞過多或者過少的參數,甚至完全不同的參數都不會產生編譯階段的錯誤。
              由于_cdecl調用方式的參數內存棧由調用者維護,所以變長參數的函數能(也只能)使用這種調用約定。
              由于Visual C++默認采用_cdecl 調用方式,所以VC中中調用DLL時,用戶應使用_stdcall調用約定。
              按C編譯方式,_cdecl調用約定僅在輸出函數名前面加下劃線,形如_functionname。
            (3) _fastcall調用
              _fastcall 是編譯器指定的快速調用方式。由于大多數的函數參數個數很少,使用堆棧傳遞比較費時。因此_fastcall通常規定將前兩個(或若干個)參數由寄存器傳遞,其余參數還是通過堆棧傳遞。不同編譯器編譯的程序規定的寄
            存器不同。返回方式和_stdcall相當。
              _fastcall調用較快,它通過CPU內部寄存器傳遞參數。
             
            (4)PASCAL調用
            PASCAL 是Pascal語言的函數調用方式,也可以在C/C++中使用,參數壓棧順序與_cdecl和_stdcall相反。
             
            歸納如下:
            調用約定            入棧參數清理         參數入棧順序
            -----------        --------------         ----------------
            cdecl               調用者處理            右->左
            stdcall             函數自己處理        右->左
            fastcall            函數自己處理        依賴于編譯器
            pascal             函數自己處理        左->右

            posted @ 2012-12-27 11:41 鑫龍 閱讀(348) | 評論 (0)編輯 收藏

            僅列出標題
            共20頁: First 6 7 8 9 10 11 12 13 14 Last 
            国产精品久久久久久影院 | 亚洲午夜久久久久久久久久| 日日狠狠久久偷偷色综合0| 91久久国产视频| 99久久免费国产精品特黄| 伊人久久大香线蕉av不变影院| 久久这里只精品99re66| 久久精品国产免费观看三人同眠| 97久久精品无码一区二区天美| 国产A级毛片久久久精品毛片| 91久久国产视频| 久久久久av无码免费网| 日本精品久久久久影院日本| 色婷婷狠狠久久综合五月| 久久成人18免费网站| 久久天天躁狠狠躁夜夜网站| 色诱久久av| 免费一级做a爰片久久毛片潮| 青青青国产成人久久111网站| 久久se精品一区二区影院| 亚洲国产成人久久综合碰碰动漫3d | 99久久人妻无码精品系列| 久久影视国产亚洲| 色偷偷偷久久伊人大杳蕉| 品成人欧美大片久久国产欧美...| 亚洲国产视频久久| 国产精品内射久久久久欢欢| 久久精品国产99久久无毒不卡 | 亚洲中文久久精品无码| 久久精品国产99国产电影网 | 成人国内精品久久久久影院| 996久久国产精品线观看| 国内精品久久久人妻中文字幕| 成人国内精品久久久久一区| 色99久久久久高潮综合影院| 久久亚洲日韩看片无码| 久久亚洲精品无码AV红樱桃| 狠狠色丁香久久婷婷综合图片 | 久久久噜噜噜www成人网| 日韩精品久久久久久免费| 青青草国产精品久久久久|