linux程序開發基礎

 

Chapter 1. C語言基礎

Table of Contents

1.1. 指針與數組

Linux是使用C語言開發的,基於Linux平臺的應用程序開發,C語言是首選的開發語言。本章記錄C語言的基本概念/ 和基礎知識。

1.1. 指針與數組

  • C語言中專門用來存放內存地址的變量叫指針(pointer)變量,簡稱指針。

  • &運算符用來取得變量地址,

  • "*"運算符用來取得指針變量的值。

  • 數組名是地址

Chapter 2. Linux程序開發基礎概念

在設置Linux的系統路徑時,使用冒號分隔每個路徑名。如:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"

在Linux中的程序有兩種,一種是可執行程序,與Windows下的.exe文件類似,一種是腳本,與Windows下的.bat文件類似。

Linux中常用的程序存放路徑有以下幾個:

  • /bin,該路徑存放系統啓動時需要使用的程序。

  • /usr/bin,該路徑存放用戶需使用的標準程序。

  • /usr/local/bin,該路徑存放本地安裝的程序。

  • Linux使用斜槓"/"分隔路徑名,而不是Windows的反斜槓"/"。

  • Linux下的C編譯器使用GCC,由於歷史的原因,在POSIX兼容的操作系統中,C編譯器都叫cc,所以Linux下也有一個cc命令,它是一個到gcc的軟鏈接。

開發工具,多數位於/usr/bin或/usr/local/bin目錄下。

頭文件,位於/usr/include目錄。頭文件包含有常量定義、系統調用和庫函數調用的聲明。這是系統默認的頭文件存放路徑,在編譯程序時,編譯器會自動查找該目錄。gcc編譯器在編譯程序時也可用-I參數指定另外的頭文件路徑。如:

gcc -I/usr/local/myinclude test.c。

庫文件,庫是一組已編譯的函數集合,可方便我們重用代碼。默認存放在/lib和/usr/lib目錄。庫文件可分爲靜態和共享兩類。

  • .a,靜態庫文件。使用靜態庫將會把所有的庫代碼引入程序,佔用更多的磁盤空間和內存空間,所以一般建議使用共享庫。

  • .so,共享庫文件。使用共享庫的程序不包含庫代碼,只在程序運行才調用共享庫中的代碼。

在編譯時可用包含路徑的庫文件名或用-l參數指定使用的庫文件,/usr/lib/libm.a等價於-lm。如:

gcc -o hello hello.c /usr/lib/libm.a  
或用-l參數寫成 
gcc -o hello hello.c -lm

如果我們要使用的庫文件不在默認位置,在編譯程序時可用-L參數指定庫文件的路徑。下面例子使用了/usr/hello/lib目錄下的libhello庫文件:

gcc -o hello -L/usr/hello/lib hello.c -lhello

創建和使用靜態庫。

  • 分別創建兩個函數,函數a的內容如下:

    #include <stdio.h>
    
    void a(char *arg)
    {
            printf("function a,hello world %s/n",arg);
    }
    

    函數b的內容如下:

    #include <stdio.h>
    
    void b(int arg)
    {
            printf("function b,hello world %d/n",arg);
    }
    
  • 接着,生成兩個對象文件。

    debian:~/c# gcc -c a.c b.c
    debian:~/c# ls *.o
    a.o  b.o
    
  • 最後,用ar歸檔命令把生成的對象文件打包成一個靜態庫libhello.a。

    debian:~/c# ar crv libhello.a a.o b.o 
    r - a.o
    r - b.o
    
  • 爲我們的靜態庫定義一個頭文件lib.h,包含這兩個函數的定義。

    /*
     * this is a header file.
     */
    void a(char *arg);
    void b(int arg);
    }}}
      * 創建jims.c程序,內容如下。{{{#!cplusplus
    #include "lib.h"
    
    int main()
    {
            a("jims.yang");
            b(3);
            exit(0);
    }
    
  • 利用靜態鏈接庫編譯程序。

    debian:~/c# gcc -c jims.c
    debian:~/c# gcc -o jims jims.o libhello.a
    debian:~/c# ./jims
    function a,hello world jims.yang
    function b,hello world 3
    debian:~/c#                                       
    
    [Note]
    gcc -o jims jims.o libhello.a也可以寫成gcc -o jims jims.o -L. -lhello。

預處理,在程序開頭以“#”開頭的命令就是預處理命令,它在語法掃描和分析法時被預處理程序處理。預處理有以下幾類:

  • 宏定義,用#define指令定義。如:#define BUFFER 1024。取消宏定義用#undef指令。宏還可帶參數,如:

    #define BUF(x) x*3
    
  • 包含頭文件,用#include指令,可把包含的文件代碼插入當前位置。如:

    <#include <stdio.h>。
    

    包含的文件可以用尖括號,也可用雙引號,如:

    #include "stdio.h"。
    

    不同之處是,使用尖括號表示在系統的包含目錄(/usr/include)下查找該文件,而雙引號表示在當前目錄下查找包含文件。每行只能包含一個包含文件,要包含多個文件要用多個#include指令。

  • 條件編譯,格式如下:

    格式一,如果定義了標識符,則編譯程序段1,否則編譯程序段2:
    #ifdef 標識符
    程序段1
    #else
    程序段2
    #endif
    
    格式二,如果定義了標識符,則編譯程序段2,否則編譯程序段1,與格式一相反:
    #ifndef 標識符
    程序段1
    #else
    程序段2
    #endif 
    
    格式三,常量表達式爲真則編譯程序段1,否則編譯程序段2:
    #if 常量表達式
    程序段1
    #else
    程序段2
    #endif 
    

使用gcc編譯程序時,要經過四個步驟。

  • 預處理(Pre-Processing),用-E參數可以生成預處理後的文件。

    debian:~/c# gcc -E hello.c -o hello.i
    
  • 編譯(Compiling)

  • 彙編(Assembling)

  • 鏈接(Linking)

GCC默認將.i文件看成是預處理後的C語言源代碼,所以我們可以這樣把.i文件編譯成目標文件。

debian:~# gcc -c hello.i -o hello.o}}}

在GCC中使用-pedantic選項能夠幫助程序員發現一些不符合ANSI/ISO C標準的代碼,但不是全部。從程序員的角度看,函數庫實際上就是一些頭文件(.h)和庫文件(.so或者.a)的集合。

Chapter 3. 文件處理

在Linux系統內所有東西都是以文件的形式來表示的,除一般的磁盤文件外,還有設備文件,如硬盤、聲卡、串口、打印機等。設備文件又可分爲字符設備文件(character devices)和塊設備文件(block devices)。使用man hier命令可以查看Linux文件系統的分層結構。文件的處理方法一般有五種,分別是:

  • open,打開一個文件或設備。

  • close,關閉一個打開的文件或設備。

  • read,從一個打開的文件或者設備中讀取信息。

  • write,寫入一個文件或設備。

  • ioctl,把控制信息傳遞給設備驅動程序。

open,close,read,write和ioctl都是低級,沒有緩衝的文件操作函數,在實際程序開發中較少使用,一般我們使用標準I/O函數庫來處理文件操作。如:fopen,fclose,fread,fwrite,fflush等。在使用標準I/O庫時,需用到stdio.h頭文件。

一些常用的文件和目錄維護函數:chmod、chown、unlink、link、symlink、mkdir、rmdir、chdir、getcwd、opendir,closedir、readdir、telldir、seekdir等。

fcntl用於維護文件描述符,mmap用於分享內存。

創建文檔並輸入信息的示例代碼:

#include <stdio.h>

main(void)
        {
        FILE *fp1;
        char c;

        fp1 = fopen("text.txt","w");
        while ((c = getchar())!= '/n')
                putc(c,fp1);
        fclose(fp1);
        }

顯示路徑的示例代碼

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
        char *topdir = ".";
        if (argc >= 2)
                topdir = argv[1];

        printf("Directory scan of %s/n", topdir);
        printdir(topdir,0);
        printf("done./n");

        exit(0);
}

printdir(char *dir, int depth)
{
        DIR *dp;
        struct dirent *entry;
        struct stat statbuf;

        if((dp = opendir(dir)) == NULL)
        {
                fprintf(stderr,"cannot open directory:%s/n",dir);
                return;
        }
        chdir(dir);
        while((entry = readdir(dp)) != NULL)
        {
                lstat(entry->d_name,&statbuf);
                if(S_ISDIR(statbuf.st_mode))
                {
                        if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
                                continue;
                        printf("%*s%s//n",depth,"",entry->d_name);
                        printdir(entry->d_name,depth+4);
                }
                else printf("%*s%s/n",depth,"",entry->d_name);
        }
        chdir("..");
        closedir(dp);
}

Chapter 4. Linux環境

void main()表示程序沒有參數,int main(int argc, char *argv[])表示程序要帶參數,argc保存着參數的個數,argv[]數組保存着參數列表。如:

debian:~# mytest a b c 
argc: 4
argv: ["mytest","a","b","c"]

getopt()函數和getopt_long()用來處理程序選項。getopt_long()函數可以處理以"--"開頭的選項。Gnu官方手冊頁:http://www.gnu.org/software/libc/manual/html_node/Getopt.html

獲取命令行參數的示例代碼:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
   int opt;

   while((opt = getopt(argc,argv,"if:lr")) != -1)       /* 返回“-1”表示已沒選項需要處理。*/
   {
           switch(opt){
           case 'i':
           case 'l':
           case 'r':
                   printf("option: %c/n", opt);
                   break;
           case 'f':
                   printf("filename: %s/n", optarg);           /*如果選項需要一個參數,則參數存放在外部變量optarg中。*/
                   break;
           case ':':
                   printf("option needs a value /n");          /*“:”表示選項需要參數*/
                   break;
           case '?':
                   printf("unknown option: %c/n", optopt);    /*返回“?”表示無效的選項,並把無效的選項存放在外部變量optopt中。*/
                   break;
           }
   }
   for(; optind < argc; optind++)                      /*外部變量optind指向下一個要處理的選項索引值。*/
           printf("argument: %s/n", argv[optind]);
}

在bash shell中使用set命令可以列出Linux系統的環境變量,在C程序中我們也可以用putenv()和getenv()函數來獲取Linux系統的環境變量。這兩個函數的聲明如下:

char *getenv(const char *name);
int putenv(const char *string);

系統有一個environ變量記錄了所有的系統變量。下面的示例代碼可把environ的值顯示同來。

#include <stdlib.h>
#include <stdio.h>

extern char **environ;

int main()
{
        char **env = environ;

        while(*env)
        {
                printf("%s/n",*env);
                env++;
        }
}

linux和其它unix一樣,使用GMT1970年1月1日子夜作爲系統時間的開始,也叫UNIX紀元的開始。現在的時間表示爲UNIX紀元至今經過的秒數。

#include <time.h>
 time_t time(time_t *t);

顯示系統時間的示例代碼:
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
        int i;
        time_t the_time;

        for(i = 1; i <= 10; i++){
                the_time = time((time_t *)0);
                printf("%d  the time is %ld/n", i, the_time);
                sleep(2);
        }
}

用ctime()函數以友好方式返回當前時間,它的函數聲明格式:

#include <time.h>
char *ctime(const time_t *timeval);

示例:
#include <time.h>
#include <stdio.h>

int main(void)
{
        time_t time1;

        (void)time(&time1);
        printf("The date is: %s/n",ctime(&time1));
}

用mkstemp()函數創建臨時文件。聲明:

#include<stdlib.h>
int mkstemp(char * template);

示例:
#include <stdio.h>

int main(void)
{
        char template[] = "template-XXXXXX";
        int fp;
        fp = mkstemp(template);
        printf("template = %s/n", template);
        close(fp);
}

獲取用戶信息。

聲明:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);   /* 根據uid返回用戶信息 */
struct passwd *getpwnam(const char *name);   /* 根據用戶名返回用戶信息 */

passwd結構體說明:
passwd Member Description
char *pw_name          The user's login name
uid_t pw_uid             The UID number
gid_t pw_gid             The GID number
char *pw_dir            The user's home directory
char *pw_gecos        The user's full name
char *pw_shell         The user's default shell

示例代碼:
#include <stdio.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>

int main(void)
{
        uid_t uid;
        gid_t gid;
        struct passwd *pw;

        uid = getuid();
        gid = getgid();
        pw = getpwuid(uid);

        printf("User is %s/n", getlogin());
        printf("The uid is:%d/n", uid);
        printf("The gid is:%d/n",gid);
        printf("The pw struct:/n name=%s, uid=%d, gid=%d, home=%s,shell=%s/n", pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell);
}

用gethostname()函數獲取主機名。

函數聲明:
#include <unistd.h>
int gethostname(char *name, size_t namelen);    /* 主機名返回給name變量 */

示例代碼:
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        char computer[100];
        int status;

        status = gethostname(computer, 100);
        printf("The status is %d/n", status);
        printf("The hostname is: %s/n", computer);
}

用uname()函數獲取主機詳細信息,就像shell的uname命令返回的信息一樣。

函數聲明:
#include <sys/utsname.h>
int uname(struct utsname *name);

utsname結構體說明:
utsname Member                  Description
char sysname[]                  The operating system name
char nodename[]                 The host name
char release[]                  The release level of the system
char version[]                  The version number of the system
char machine[]                  The hardware type

示例代碼:
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h>

int main(void)
{
        char computer[100];
        int status;
        struct utsname uts;

        status = gethostname(computer,100);
        printf("The computer's size is %d/n",sizeof(computer));
        printf("The status is %d/n", status);
        printf("The hostname is: %s/n", computer);

        uname(&uts);
        printf("The uname's information./n uts.sysname=%s/n uts.machine=%s/n uts.nodename=%s/n uts.release=%s/n uts.version=%s/n", uts.sysname,uts.machine,uts.nodename,uts.release,uts.version);
}

使用syslog()函數處理日誌信息。

函數聲明:
#include <syslog.h>
void syslog(int priority, const char *message, arguments...);

priority參數的格式(severity level|facility code)
示例: 
LOG_ERR|LOG_USER

severity level:
Priority Level               Description
LOG_EMERG                    An emergency situation
LOG_ALERT                    High-priority problem, such as database corruption
LOG_CRIT                     Critical error, such as hardware failure
LOG_ERR                      Errors
LOG_WARNING                  Warning
LOG_NOTICE                   Special conditions requiring attention
LOG_INFO                     Informational messages
LOG_DEBUG                    Debug messages

facility value(轉自syslog.h頭文件):
/* facility codes */
#define LOG_KERN        (0<<3)  /* kernel messages */
#define LOG_USER        (1<<3)  /* random user-level messages */
#define LOG_MAIL        (2<<3)  /* mail system */
#define LOG_DAEMON      (3<<3)  /* system daemons */
#define LOG_AUTH        (4<<3)  /* security/authorization messages */
#define LOG_SYSLOG      (5<<3)  /* messages generated internally by syslogd */
#define LOG_LPR         (6<<3)  /* line printer subsystem */
#define LOG_NEWS        (7<<3)  /* network news subsystem */
#define LOG_UUCP        (8<<3)  /* UUCP subsystem */
#define LOG_CRON        (9<<3)  /* clock daemon */
#define LOG_AUTHPRIV    (10<<3) /* security/authorization messages (private) */
#define LOG_FTP         (11<<3) /* ftp daemon */

示例代碼:
#include <syslog.h>
#include <stdio.h>

int main(void)
{
        FILE *f;

        f = fopen("abc","r");
        if(!f)                                    
                syslog(LOG_ERR|LOG_USER,"test - %m/n");       
}

上面的日誌信息由系統自動給出,我們也可過濾日誌信息。用到以下函數:

#include <syslog.h>
void closelog(void);
void openlog(const char *ident, int logopt, int facility);
int setlogmask(int maskpri);

logopt參數的選項:
logopt Parameter    Description
LOG_PID             Includes the process identifier, a unique number allocated to each process by the system, in the messages.
LOG_CONS            Sends messages to the console if they can’t be logged.
LOG_ODELAY          Opens the log facility at first call to .
LOG_NDELAY          Opens the log facility immediately, rather than at first log.

示例代碼:
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        int logmask;

        openlog("logmask", LOG_PID|LOG_CONS, LOG_USER); /*日誌信息會包含進程id。*/
        syslog(LOG_INFO, "informative message, pid=%d", getpid()); 
        syslog(LOG_DEBUG,"debug message, should appear");   /*記錄該日誌信息。*/
        logmask = setlogmask(LOG_UPTO(LOG_NOTICE));     /*設置屏蔽低於NOTICE級別的日誌信息。*/
        syslog(LOG_DEBUG, "debug message, should not appear");  /*該日誌信息被屏蔽,不記錄。*/
}

不同安全級別的日誌信息存放在/var/log目錄下的哪個文件中是由/etc/syslog.conf文件控制的,下面是我係統中syslog.conf文件的內容:

#  /etc/syslog.conf     Configuration file for syslogd.
#
#                       For more information see syslog.conf(5)
#                       manpage.

#
# First some standard logfiles.  Log by facility.
#

auth,authpriv.*                 /var/log/auth.log
*.*;auth,authpriv.none          -/var/log/syslog
#cron.*                         /var/log/cron.log
daemon.*                        -/var/log/daemon.log
kern.*                          -/var/log/kern.log
lpr.*                           -/var/log/lpr.log
mail.*                          -/var/log/mail.log
user.*                          -/var/log/user.log
uucp.*                          /var/log/uucp.log

#
# Logging for the mail system.  Split it up so that
# it is easy to write scripts to parse these files.
#
mail.info                       -/var/log/mail.info
mail.warn                       -/var/log/mail.warn
mail.err                        /var/log/mail.err

# Logging for INN news system
#
news.crit                       /var/log/news/news.crit
news.err                        /var/log/news/news.err
news.notice                     -/var/log/news/news.notice

#
# Some `catch-all' logfiles.
#
*.=debug;/
        auth,authpriv.none;/
        news.none;mail.none     -/var/log/debug
*.=info;*.=notice;*.=warn;/
        auth,authpriv.none;/
        cron,daemon.none;/
        mail,news.none          -/var/log/messages

#
# Emergencies are sent to everybody logged in.
#
*.emerg                         *

#
# I like to have messages displayed on the console, but only on a virtual
# console I usually leave idle.
#
#daemon,mail.*;/
#       news.=crit;news.=err;news.=notice;/
#       *.=debug;*.=info;/
#       *.=notice;*.=warn       /dev/tty8

# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,
# you must invoke `xconsole' with the `-file' option:
#
#    $ xconsole -file /dev/xconsole [...]
#
# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
#      busy site..
#
daemon.*;mail.*;/
        news.crit;news.err;news.notice;/
        *.=debug;*.=info;/
        *.=notice;*.=warn       |/dev/xconsole
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章