HICK 者,乡巴佬也

æ•°æ®ç®¡ç†2(Beginning Linux Programming 笔记11)

Posted in: Linux 编程 — Hick @ 2009/06/15 22:18:53 Comments (5)

p297 文件é”定

有两ç§ç±»åž‹çš„æ–‡ä»¶é”定,最常è§çš„æ˜¯éžå†…核实现的åŠå‘Šå¼é”定(advisory locking),其方法是所有进程éµå¾ªä¸€ä¸ªçº¦å®šï¼Œä¹Ÿå°±æ˜¯ä¸‹é¢è¯´çš„æ–‡ä»¶é”。å¦å¤–ä¸€ç§æ˜¯å¼ºåˆ¶é”(mandatory locking),它是由内核强制实施的,当一个é”è¢«åˆ›å»ºæ—¶ï¼Œå…¶ä»–è¿›ç¨‹å¯¹è¢«é”æ–‡ä»¶è¿›è¡Œè¯»å†™æ“作时会被挂起,椅å­åŽåˆ°é”定释放,因为é”定在 read å’Œ write 上,会é™ä½Žç³»ç»Ÿè°ƒç”¨çš„æ€§èƒ½ã€‚

文件é”定对多用户多任务æ“作系统是éžå¸¸é‡è¦çš„,进程之间ç»å¸¸éœ€è¦é€šè¿‡æ–‡ä»¶æ¥å…±äº«æ•°æ®ã€‚linux 下的文件é”ä¸ä»…ä»…å¯ä»¥é€šè¿‡åˆ›å»ºé”定文件(lock file)çš„åŽŸå­æ“作æ¥ä¿è¯æ“作唯一性,还能针对文件的æŸä¸€éƒ¨åˆ†åšæŽ’ä»–æ€§è®¿é—®çš„é”æ“作。有一ç§è¯´æ³•,分别称这俩ç§é”为文件é”和记录é”(record lock)。

如果程åºéœ€è¦æŽ’他性的访问æŸä¸ªèµ„æºï¼Œå¯ä»¥é€šè¿‡å…ˆåˆ›å»ºä¸€ä¸ªçº¦å®šçš„æ–‡ä»¶é”的方å¼ï¼Œçº¦å®šå…¶ä»–访问该资æºçš„程åºéƒ½å…ˆæ£€æŸ¥è¯¥æ–‡ä»¶æ˜¯å¦å­˜åœ¨ã€‚处ç†å®ŒæŸä¸ªèµ„æºåŽï¼ŒæŠŠè¯¥é”文件 unlink ,以便其他进程访问该资æºã€‚linux 系统一般把文件é”创建在 /var/spool 下。使用 open åˆ›å»ºæ–‡ä»¶é”æ—¶ï¼Œéœ€è¦ä½¿ç”¨ O_CREAT å’Œ O_EXCL 标识,这样æ‰èƒ½ä»¥åŽŸå­æ“作的方å¼ä¿è¯åˆ›å»ºæœ‰æ•ˆé”,下é¢çš„程åºä¸­ï¼Œåªåˆ›å»ºäº†é”,没有释放é”:

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

int main()
{
    int file_desc;
    int save_errno;

    file_desc = open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
    if (file_desc == -1) {
        save_errno = errno;
        printf("Open failed with error %d\n", save_errno);
    }
    else {
        printf("Open succeeded\n");
    }
    exit(EXIT_SUCCESS);
}

linux 下第二次执行上é¢çš„程åºï¼Œä¼šè¿”回错误ç ä¸º 17 ï¼Œæ˜¯å› ä¸ºæ–‡ä»¶å­˜åœ¨çš„é”™è¯¯ç  EEXIST 的值被定义为 17 ,这也正是由于指定了 O_CREAT | O_EXCL 。一ç§å¸¸è§åˆ›å»ºæ–‡ä»¶é”的方法是把å‘èµ·é”的进程的 id 写到文件中,这样å¯ä»¥é€šè¿‡æ£€æŸ¥è¿›ç¨‹ id 是å¦å­˜åœ¨ï¼Œé˜²æ­¢é”被创建以åŽï¼Œåœ¨åˆ é™¤æ–‡ä»¶é”之å‰ç¨‹åºæ„外终止导致文件ç一直存在。

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

const char *lock_file = "/tmp/LCK.test2";

int main() {
    int file_desc;
    int tries = 10;

    while (tries--) {
        file_desc = open(lock_file, O_RDWR | O_CREAT | O_EXCL, 0444);
        if (file_desc == -1) {
            printf("%d - Lock already present\n", getpid());
            sleep(3);
        }
        else {
                /* critical region */
            printf("%d - I have exclusive access\n", getpid());
            sleep(1);
            (void)close(file_desc);
            (void)unlink(lock_file);
                /* non-critical region */
            sleep(2);
        }
    } /* while */
    exit(EXIT_SUCCESS);
}

p301 局部é”(locking regions)

PS: 这里很多笔记都æ¥è‡ª<>一书第二版。

就是别的书上说的记录é”。需è¦è®¿é—®ä¸€ä¸ªå¤§çš„共享文件时,文件é”就显得局é™äº†ã€‚linux å…许é”定文件的æŸäº›éƒ¨åˆ†(regions),其他进程å¯ä»¥è®¿é—®æ–‡ä»¶çš„其他没有é”定的部分。记录é”å¯ä»¥é”定文件的任æ„部分,比如进程 A å¯ä»¥é”定一个文件从 50 字节 200 字节的部分,å¦ä¸€ä¸ªè¿›ç¨‹ B 则å¯ä»¥é”定 300 字节到 350 字节的部分,这两个é”之间并ä¸å†²çªã€‚记录é”çš„å¦å¤–一个优点是被内核ä¿å­˜ï¼Œè€Œä¸æ˜¯æ–‡ä»¶ç³»ç»Ÿå®žçŽ°çš„ã€‚å½“ä¸€ä¸ªè¿›ç¨‹ç»ˆæ­¢åŽï¼Œå®ƒæ‰€ä¿å­˜çš„æ‰€æœ‰é”都会被自动释放。这ç§é”也是åŠå‘Šå¼çš„。linux 有æè®°å½•é”的强制å¼å˜ä½“ï¼Œä½†æ˜¯ä½¿ç”¨ä¸æ˜¯å¾ˆæ–¹ä¾¿ã€‚

è®°å½•é”æœ‰ä¸¤ç§ç±»åž‹ï¼Œè¯»é”和写é”,读é”实际上是共享é”,因为多个进程å¯ä»¥åŒæ—¶å¯¹ä¸€ä¸ªåŒºåŸŸä¿æŒè¯»é”。但是对一个记录åªèƒ½æœ‰ä¸€ä¸ªè¿›ç¨‹ä¿æŒå†™é”,写é”必须的胡斥的。进程在需è¦å†™æ–‡ä»¶æ—¶ï¼Œå¿…须得到一个写é”,写é”存在时,这个记录应该也应该没有读é”存在。ä¿è¯è¯»å†™ä¸å†²çªã€‚

è‡³å°‘æœ‰ä¸¤ç§æ–¹æ³•能实现局部é”: fcntl å’Œ lockf ,下é¢ä¸»è¦çœ‹ç”¨ fcntl æ¥å®žçŽ°çš„æ–¹å¼ï¼Œæ³¨æ„两者虽然比较相似,但是ä¸èƒ½åŒæ—¶å¥æ•ˆï¼Œå¯¹åŒä¸€ä¸ªæ–‡ä»¶ï¼Œä¸èƒ½æ··åˆä½¿ç”¨ä¸¤ç§é”定方法。

#include <fcntl.h>
int fcntl(int fildes, int command,  struct flock *flock_structure);

ç¬¬ä¸€ä¸ªå‚æ•°ä¸ºéœ€è¦æ“作的文件 fd , ç¬¬äºŒä¸ªå‚æ•°ä¸ºéœ€è¦æ‰§è¡Œä»»åŠ¡æˆ–è€…è¯´å‘½ä»¤ï¼š F_GETLK, F_SETLK, F_SETLKW åŽé¢åˆ†åˆ«è¯¦ç»†ä»‹ç»ã€‚ç¬¬ä¸‰ä¸ªå‚æ•°ä¸º 一个文件结构体 flock ,包å«ä»¥ä¸‹æˆå‘˜:

- l_type é”的类型,å¯ä»¥æ˜¯ F_RDLCK 设置为读é”(共享é”)ï¼›F_WRLCK 设置为写é”(互斥é”)ï¼›F_UNLCK 删除é”
- l_start l_whence é…åˆä½¿ç”¨æŒ‡å®šé”定区域, å¯ä»¥æ˜¯ SEEK_SET, SEEK_CUR, SEEK_END ,具体 lseek 函数
- l_len 指定é”定长度
- l_pid åªæœ‰å½“查询é”的时候æ‰ä¼šä½¿ç”¨ï¼Œå®ƒè¢«è®¾ç½®ä¸ºæ‹¥æœ‰è¢«æŸ¥è¯¢é”的进程的 pid

command å–值:

- F_SETLK 设置 flock 结构æè¿°çš„é”,如果因为与å¦å¤–一个进程的é”冲çªè€Œä½¿å¾—锿— æ³•创建,则返回 EAGAIN 。l_type 为 F_UNLCK 则删除é”
- F_SETLKW å’Œ F_SETLK 类似,但是一直阻塞é”直到é”被创建创建æˆåŠŸï¼Œå¦‚æžœåœ¨è¿›ç¨‹è¢«é˜»å¡žæ—¶æœ‰ä¿¡å·å‘生, fcntl 调用返回 EAGAIN
- F_GETLK 检查所æè¿°çš„锿˜¯å¦è¢«åˆ›å»ºæˆåŠŸï¼Œå¦‚æžœå·²ç»åˆ›å»ºï¼Œflock 结构除了 l_type 以外ä¸ä¼šæ”¹å˜: l_type è¢«æ”¹æˆ F_UNLCK (还ä¸å¤§æ˜Žç™½);如果创建é”失败,l_pid è¢«è®¾ç½®ä¸ºä¿æŒè¿™ä¸ªé”的进程 pid 。

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


const char *test_file = "/tmp/test_lock";
#define SIZE_TO_TRY 5

void show_lock_info(struct flock *to_show);


int main() {
    int file_desc;
    int res;
    struct flock region_to_test;
    int start_byte;
    
        /* open a file descriptor */
    file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
    if (!file_desc) {
        fprintf(stderr, "Unable to open %s for read/write", test_file);
        exit(EXIT_FAILURE);
    }

    for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
            /* set up the region we wish to test */
        region_to_test.l_type = F_WRLCK;
        region_to_test.l_whence = SEEK_SET;
        region_to_test.l_start = start_byte;
        region_to_test.l_len = SIZE_TO_TRY;
        region_to_test.l_pid = -1;     

        printf("Testing F_WRLCK on region from %d to %d\n",
               start_byte, start_byte + SIZE_TO_TRY);
        
            /* now test the lock on the file */
        res = fcntl(file_desc, F_GETLK, &region_to_test);
        if (res == -1) {
            fprintf(stderr, "F_GETLK failed\n");
            exit(EXIT_FAILURE);
        }
        if (region_to_test.l_pid != -1) {
            printf("Lock would fail. F_GETLK returned:\n");
            show_lock_info(&region_to_test);
        }
        else {
            printf("F_WRLCK - Lock would succeed\n");
        }

            /* now repeat the test with a shared (read) lock */
        
            /* set up the region we wish to test */
        region_to_test.l_type = F_RDLCK;
        region_to_test.l_whence = SEEK_SET;
        region_to_test.l_start = start_byte;
        region_to_test.l_len = SIZE_TO_TRY;
        region_to_test.l_pid = -1;     

        printf("Testing F_RDLCK on region from %d to %d\n",
               start_byte, start_byte + SIZE_TO_TRY);
        
            /* now test the lock on the file */
        res = fcntl(file_desc, F_GETLK, &region_to_test);
        if (res == -1) {
            fprintf(stderr, "F_GETLK failed\n");
            exit(EXIT_FAILURE);
        }
        if (region_to_test.l_pid != -1) {
            printf("Lock would fail. F_GETLK returned:\n");
            show_lock_info(&region_to_test);           
        }
        else {
            printf("F_RDLCK - Lock would succeed\n");
        }

    } /* for */
    
    close(file_desc);
    exit(EXIT_SUCCESS);
}

void show_lock_info(struct flock *to_show) {
    printf("\tl_type %d, ", to_show->l_type);
    printf("l_whence %d, ", to_show->l_whence);
    printf("l_start %d, ", (int)to_show->l_start);       
    printf("l_len %d, ", (int)to_show->l_len);
    printf("l_pid %d\n", to_show->l_pid);
}

书中还讲述了 竞争é”以åŠå¦å¤–一ç§è®°å½•é”实现 lockf ,暂ä¸ç»†çœ‹ã€‚

p314 æ•°æ®åº“

大多数 unix 系的æ“作系统都支æŒä¸€ç§é«˜æ•ˆçš„æ•°æ®åº“管ç†ç³»ç»Ÿ dbm 。其实有些人怀疑它是å¦å¯ä»¥ä½œä¸ºä¸€ç§æ•°æ®åº“,因为它åªå…·å¤‡å¾ˆç®€å•的数æ®åº“功能,å¯ä»¥è¯´å°±æ˜¯ä¸€ä¸ªæ–‡ä»¶ç´¢å¼•存储系统。RPM 打包系统就使用 dbm ä¿å­˜å·²ç»å®‰è£…包的信æ¯çš„存储介质。开æºçš„ Open LDAP ä»¥åŠ apache , sendmail 也使用 dbm ä¿å­˜æ•°æ®ã€‚相比 mysql 等数æ®åº“系统, dbm éžå¸¸è½»é‡ï¼Œä¸éœ€è¦å®‰è£…独立的数æ®åº“ server 。dbm éžå¸¸é€‚åˆè¯»å¾ˆè¯»å¤šä½†æ˜¯å†™å¾ˆå°‘的数æ®ã€‚linux 系统一般带 dbm çš„ GNU 版,也就是 gdbm ã€‚ä¸‹é¢æ˜¯ä¸ªæ“作实例:

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

#include <ndbm.h>
/* On some systems you need to replace the above with
#include <gdbm-ndbm.h>
*/


#include <string.h>

#define TEST_DB_FILE "/tmp/dbm1_test"
#define ITEMS_USED 3

/* A struct to use to test dbm */
struct test_data {
    char misc_chars[15];
    int  any_integer;
    char more_chars[21];
};

int main() {

    struct test_data items_to_store[ITEMS_USED];
    struct test_data item_retrieved;

    char key_to_use[20];
    int i, result;

    datum key_datum;
    datum data_datum;
    
    DBM *dbm_ptr;

    dbm_ptr = dbm_open(TEST_DB_FILE, O_RDWR | O_CREAT, 0666);
    if (!dbm_ptr) {
        fprintf(stderr, "Failed to open database\n");
        exit(EXIT_FAILURE);
    }

        /* put some data in the structures */
    memset(items_to_store, '\0', sizeof(items_to_store));
    strcpy(items_to_store[0].misc_chars, "First!");
    items_to_store[0].any_integer = 47;
    strcpy(items_to_store[0].more_chars, "foo");
    strcpy(items_to_store[1].misc_chars, "bar");
    items_to_store[1].any_integer = 13;
    strcpy(items_to_store[1].more_chars, "unlucky?");
    strcpy(items_to_store[2].misc_chars, "Third");
    items_to_store[2].any_integer = 3;
    strcpy(items_to_store[2].more_chars, "baz");

    for (i = 0; i < ITEMS_USED; i++) {
            /* build a key to use */
        sprintf(key_to_use, "%c%c%d",
            items_to_store[i].misc_chars[0],
            items_to_store[i].more_chars[0],
            items_to_store[i].any_integer);

            /* build the key datum strcture */
        key_datum.dptr = (void *)key_to_use;
        key_datum.dsize = strlen(key_to_use);
        data_datum.dptr = (void *)&items_to_store[i];
        data_datum.dsize = sizeof(struct test_data);

        result = dbm_store(dbm_ptr, key_datum, data_datum, DBM_REPLACE);
        if (result != 0) {
            fprintf(stderr, "dbm_store failed on key %s\n", key_to_use);
            exit(2);
        }
    } /* for */

        /* now try and retrieve some data */
    sprintf(key_to_use, "bu%d", 13); /* this is the key for the second item */
    key_datum.dptr = key_to_use;
    key_datum.dsize = strlen(key_to_use);

    data_datum = dbm_fetch(dbm_ptr, key_datum);
    if (data_datum.dptr) {
        printf("Data retrieved\n");
        memcpy(&item_retrieved, data_datum.dptr, data_datum.dsize);
        printf("Retrieved item - %s %d %s\n",
               item_retrieved.misc_chars,
               item_retrieved.any_integer,
               item_retrieved.more_chars);
    }
    else {
        printf("No data found for key %s\n", key_to_use);
    }

    dbm_close(dbm_ptr);

    exit(EXIT_SUCCESS);
}

dbm ä»¥åŠ mysql æ›´å…·ä½“è¯¦ç»†ä»‹ç»æš‚ä¸çœ‹ã€‚åŽé¢å‡ ç« ä¹Ÿä¸æ˜¯æˆ‘关注的点,暂时忽略ä¸çœ‹ã€‚直接跳到 第å一章 进程和信å·ã€‚

æ•°æ®ç®¡ç†1(Beginning Linux Programming 笔记10)

Posted in: Linux 编程 — Hick @ 2009/06/14 13:14:18 Comments (0)

本章主è¦è®² linux 的动æ€å†…存管ç†ã€æ–‡ä»¶é”å®šä»¥åŠ dbm æ•°æ®åº“.

p289 linux 中最基本的内存æ“作为 malloc 函数:

#include <stdlib.h>
void *malloc(size_t size);

#example:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define A_MEGABYTE (1024 * 1024)

int main() {
    char *some_memory;
    int  megabyte = A_MEGABYTE;
    int exit_code = EXIT_FAILURE;

    some_memory = (char *)malloc(megabyte);
    if (some_memory != NULL) {
        sprintf(some_memory, "Hello World\n");
        printf("%s", some_memory);
        exit_code = EXIT_SUCCESS;
    }
    exit(exit_code);
}

虽然定义 malloc 返回的是 void * 指针,但是å¯ä»¥ç”¨ char * 类型å˜é‡æ¥ä¿å­˜ï¼Œæ³¨æ„è¿™ä¸»è¦æ˜¯å› ä¸ºå½“å‰ä½¿ç”¨çš„系统大都是 32 ä½ï¼Œ 32 使Œ‡é’ˆå¯ä»¥ä¿å­˜åˆ°ä¸€ä¸ª char * 类型中,64 ä½ä¸‹ä¸å¯ä»¥ã€‚这里把字符串ä¿å­˜åˆ°å†…å­˜(å˜é‡æ˜¯ç”¨çš„ sprintf 函数)。

p291 ä»¥æ¯æ¬¡ 1M 的方å¼ç”³è¯·å†…å­˜ï¼Œå¹¶åœ¨ç”³è¯·åˆ°çš„å†…å­˜ä¸­å†™å…¥ä¸€æŽ‰æ®µå­—ç¬¦ä¸²ï¼Œä¸æ–­åˆ†é…直到达到两å€ç‰©ç†å†…存的é‡(作者给出的为 2G),最终æˆåŠŸæ‰§è¡Œã€‚æŽ¥ä¸‹æ¥æ¯æ¬¡åªåˆ†é… 1k 内存,程åºä¸æ–­åˆ†é…下去,执行的时间比较长,ç£ç›˜çš„读写的æ“作也会很明显,在分é…到 1526M 以åŽï¼Œæç¤º Out of memory, Killed process 2365 。

p292 这里内存的管ç†(读ã€å†™)是由 linux å†…æ ¸è´Ÿè´£çš„ï¼Œæ¯æ¬¡å†…å­˜æ“作请求,内核会觉得如何å“应。一开始进程申请使用内存时,会分é…物ç†å†…存给进程使用;当物ç†å†…存耗尽,就会开始使用交æ¢åˆ†åŒº(swap space, 把ç£ç›˜å½“内存用);比如无法å†åˆ†é…内存时,结æŸç”³è¯·å†…存的进程。内核根æ®ç®—法在物ç†å†…存和交æ¢åˆ†åŒºä¹‹é—´ç§»åŠ¨æ•°æ®ï¼Œä½†æ˜¯ç¨‹åºä¸­è°ƒç”¨èµ·æ¥æ˜¯æ²¡æœ‰å·®åˆ«çš„,当时候到交æ¢åˆ†åŒºçš„æ—¶å€™ï¼Œç”±äºŽç£ç›˜æ“作会比较慢,所以会影å“到程åºå’Œç³»ç»Ÿæ€§èƒ½ã€‚

对进程æ¥è¯´ï¼Œå®ƒä½¿ç”¨çš„内存是虚拟的。Linux 把内存以页为å•元进行管ç†ï¼Œä¸€èˆ¬æ¥è¯´ä¸€é¡µ 4k (分é…了 1M 内存ä¸ä¸€å®šå°±ä½¿ç”¨äº†è¿™ä¹ˆå¤šå†…å­˜)。当进程进行内存æ“作时,先需è¦åšä¸€ä¸ªè™šæ‹Ÿåœ°å€-物ç†åœ°å€çš„æ˜ å°„。

p293 内存滥用 ç”³è¯·å†…å­˜çš„æ—¶å€™éœ€è¦æŒ‡å®šå¤§å°ï¼ŒæŽ¥ç€å¾€ç”³è¯·çš„内存中写入数æ®çš„æ—¶å€™ï¼Œæœ‰å¯èƒ½ç”³è¯·çš„内存ä¸å¤Ÿç”¨ï¼š

#include <unistd.h>
#include <stdlib.h>

#define ONE_K (1024)

int main() {
    char *some_memory;
    char *scan_ptr;
    
    some_memory = (char *)malloc(ONE_K);
    if (some_memory == NULL) exit(EXIT_FAILURE);

    scan_ptr = some_memory;
    while(1) {
        *scan_ptr = '\0';
        scan_ptr++;
    }
    
    exit(EXIT_SUCCESS);
}

上é¢çš„程åºä¸­ï¼Œç”³è¯·äº† 1k 的内存,但是以一个死循环的方å¼ï¼Œä¸æ–­çš„往内存中ä¿å­˜ä¸€ä¸ªä¸€ä¸ªå­—符。编译都ä¸ä¼šå‡ºé”™ï¼Œæ‰§è¡Œç¨‹åºæ—¶ï¼Œä¼šæŠ¥å‘Š: Segmentation fault 。

p294 空指针

linux ç³»ç»Ÿå¯¹å†…å­˜æœ‰ä¸€äº›ä¿æŠ¤æ€§æŽªæ–½ï¼Œå¯ä»¥è¯»ä¸€ä¸ªç©ºæŒ‡é’ˆ(返回 null),而写空指针时,会出现 Sagmentation fault 的错误:

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

int main() {
    char *some_memory = (char *)0;

    printf("A read from null %s\n", some_memory);
    sprintf(some_memory, "A write to null\n");
  
    exit(EXIT_SUCCESS);
}

p295 释放内存

è™½ç„¶å½“ä¸€ä¸ªè¿›ç¨‹ç»“æŸæ—¶ï¼Œå…¶ç”³è¯·ä½¿ç”¨çš„内存会被系统自动释放,但是很多场景下,我们需è¦ä¸»åŠ¨çš„é‡Šæ”¾å†…å­˜ï¼š 使用 free 函数。当一个进程的内存 page 被释放,ä¸å†ä½¿ç”¨æ—¶ï¼Œlinux 的内存管ç†å™¨ä¼šæŠŠå®ƒä»Žç‰©ç†å†…存移动到交æ¢åˆ†åŒºã€‚如果一个进程å°è¯•æ“作被移到交æ¢åˆ†åŒºçš„内存 page 的数æ®ï¼Œç³»ç»Ÿä¼šå…ˆæŒ‚èµ·è¿›ç¨‹ï¼Œç„¶åŽæŠŠç›¸å…³ page 从交æ¢åˆ†åŒºç§»åŠ¨åˆ°ç‰©ç†å†…存,然åŽè®©è¿›ç¨‹ç»§ç»­æ‰§è¡Œã€‚æ˜¯å¯¹ç¨‹åºæ¥è¯´ï¼Œç›¸å…³å†…å­˜å—å°±åƒæ˜¯ä¸€ç›´åœ¨ç‰©ç†å†…存中一样。

#include <stdlib.h>
void free(void *ptr_to memory);

free 函数åªèƒ½é‡Šæ”¾ malloc, calloc, realloc 申请的内存。

p297 其他内存分é…函数: calloc()å‡½æ•°æœ‰ä¸¤ä¸ªå‚æ•°,分别为元素的数目和æ¯ä¸ªå…ƒç´ çš„大å°,è¿™ä¸¤ä¸ªå‚æ•°çš„乘积就是è¦åˆ†é…的内存空间的大å°ï¼Œå¦‚果调用æˆåŠŸï¼Œè¿”å›žæ‰€åˆ†é…的内存空间的首地å€ã€‚且会将所分é…的内存空间中的æ¯ä¸€ä½éƒ½åˆå§‹åŒ–为零,也就是说,如果你是为字符类型或整数类型的元素分é…内存,那麽这些元素将ä¿è¯ä¼šè¢«åˆå§‹åŒ–为0;如果你是为指 针类型的元素分é…内存,那麽这些元素通常会被åˆå§‹åŒ–为空指针;如果你为实型数æ®åˆ†é…内存,则这些元素会被åˆå§‹åŒ–为浮点型的零。calloc()函数是malloc的简å•包装。它的主è¦ä¼˜ç‚¹æ˜¯æŠŠåЍæ€åˆ†é…的内存清零。用ç»éªŒçš„程åºå‘˜æ›´å–œæ¬¢ä½¿ç”¨calloc()ï¼Œå› ä¸ºè¿™æ ·çš„è¯æ–°åˆ†é…内存的内容就ä¸ä¼šæœ‰ä»€ä¹ˆé—®é¢˜ï¼Œè°ƒç”¨calloc()肯定会清0,并且å¯ä»¥é¿å…调用memset().(部分摘自网文)

realloc 函数用于改å˜åˆ†é…已申请的内存的大å°ï¼š void *realloc(void *existing_memory, size_t new_size);

Linux 环境2(Beginning Linux Programming 笔记9)

Posted in: Linux 编程 — Hick @ 2009/06/13 14:33:59 Comments (0)

p181 时间和日期

linux 下时间一般用 long int 型的类型 time_t ,其定义在 time.h 中,我们å¯ä»¥é€šè¿‡ time 函数获得 UNIX 时间戳值,原型以åŠå®žä¾‹å¦‚下:

// 原型
#include <time.h>
time_t time(time_t *tloc);
// 实例
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int i;
    time_t the_time;

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

è¯¥å‡½æ•°è¿”å›žæ—¶é—´æˆ³ï¼ŒåŒæ—¶æŠŠæ—¶é—´æˆ³ä¿å­˜å­— tlog ä¸­ã€‚æ³¨æ„ (time_t *)0 为 null 指针的写法。

p183 difftime 函数用æ¥è®¡ç®—时间差。gmtime åˆ™ç”¨æ¥æŠŠæ—¶é—´æˆ³è¾“å‡ºæˆåŒ…å«å¯è¯»æ—¶é—´çš„ tm 结构体:

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

int main()
{
    struct tm *tm_ptr;
    time_t the_time;

    (void) time(&the_time);
    tm_ptr = gmtime(&the_time);

    printf("Raw time is %ld\n", the_time);
    printf("gmtime gives:\n");
    printf("date: %02d/%02d/%02d\n",
        tm_ptr->tm_year, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
    printf("time: %02d:%02d:%02d\n",
        tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);
    exit(0);
}

p184 gmtime 得到的是 GMT æ ‡å‡†æ—¶é—´ï¼Œè¦æƒ³å¾—到本地(本时区)时间,需è¦ç”¨ localtime 函数,它跟 gmtime 的区别åªåœ¨è¾“出ä¸åŒæ—¶åŒºçš„æ—¶é—´ã€‚

tm 结构体å¯ä»¥ç”± mkdir 函数转æ¢å›žæ—¶é—´æˆ³ï¼Œè½¬æ¢å¤±è´¥å‡½æ•°è¿”回 -1 :

#include <time.h>
time_t mktime(struct tm *timeptr);

å¦å¤– asctime 函数å¯ä»¥æŠŠ tm ç»“æž„è½¬æ¢æˆ”Sun Jun 9 12:34:56 2007\n\0″这样的字符串。 ctime 则相当于下é¢çš„第四行代ç ï¼š

#include <time.h>
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timeval);
asctime(localtime(timeval));

p186 strftime ç”¨æ¥æ ¼å¼åŒ–输出自定义的å¯è¯»çš„æ—¶é—´æ ¼å¼ã€‚

#include <time.h>
size_t strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr);

把 timeptr 表示的 tm ç»“æž„ä½“æ—¶é—´è½¬åŒ–æˆ format 指定格å¼çš„字符串,date 命令对应的输出为: “%a %b %d %H:%M:%S %Yâ€ã€‚ å¦å¤– strptime 功能类似:

#include <time.h>
char *strptime(const char *buf, const char *format, struct tm *timeptr);

使用例å­ï¼š

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

int main()
{
    struct tm *tm_ptr, timestruct;
    time_t the_time;
    char buf[256];
    char *result;

    (void) time(&the_time);
    tm_ptr = localtime(&the_time);
    strftime(buf, 256, "%A %d %B, %I:%S %p", tm_ptr);

    printf("strftime gives: %s\n", buf);

    strcpy(buf,"Thu 26 July 2007, 17:53 will do fine");

    printf("calling strptime with: %s\n", buf);
    tm_ptr = &timestruct;

    result = strptime(buf,"%a %d %b %Y, %R", tm_ptr);
    printf("strptime consumed up to: %s\n", result);

    printf("strptime gives:\n");
    printf("date: %02d/%02d/%02d\n",
        tm_ptr->tm_year % 100, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
    printf("time: %02d:%02d\n",
        tm_ptr->tm_hour, tm_ptr->tm_min);
    exit(0);
}

p189 临时文件

tmpnam 函数å¯ä»¥ç”¨æ¥ç”Ÿæˆå”¯ä¸€æ–‡ä»¶å(文件系统中ä¸å­˜åœ¨çš„æ–‡ä»¶),åŒä¸€ä¸ªè¿›ç¨‹ä¸­å®ƒå¯ä»¥è¢«è°ƒç”¨ TMP_MAX æ¬¡ã€‚å‚æ•°éž null 时,生æˆçš„æ–‡ä»¶å会赋予给该å˜é‡ï¼š

#include <stdio.h>
char *tmpnam(char *s);

å¦‚æžœç”Ÿæˆæ–‡ä»¶å以åŽé©¬ä¸Šå°±éœ€è¦åˆ›å»ºæ–‡ä»¶ï¼Œå¯ä»¥ç›´æŽ¥ä½¿ç”¨ tmpfile å‡½æ•°ï¼Œè¿™æ ·èƒ½é˜²æ­¢åœ¨ç”Ÿæˆæ–‡ä»¶å到创建文件的过程中,å¦å¤–ä¸€ä¸ªè¿›ç¨‹åˆ›å»ºäº†åŒæ ·çš„æ–‡ä»¶ã€‚

#include <stdio.h>
FILE *tmpfile(void);

该函数返回指å‘ä¸´æ—¶æ–‡ä»¶çš„æ–‡ä»¶æµæŒ‡é’ˆï¼Œå¹¶ä¸”ä»¥è¯»å†™æ–¹å¼æ‰“开文件,当所有指å‘该文件的引用被关闭时,临时文件会被自动删除。注æ„因为有上é¢è¯´åˆ°çš„风险,所以一般创建临时文件都用 tmpfile æ¥ï¼Œå®žé™…上 GNU C 编译器会对 tmpname çš„ä½¿ç”¨äº§ç”Ÿä¸€æ¡ warning 。å¦å¤–,æŸäº› UNIX 版本中æä¾›å¦å¤–ä¸€ç§æ–¹å¼:

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

p191 用户信æ¯

linux 中,除了 init 进程以外,所有的进程都是由用户或者其他进程å¯åŠ¨çš„ã€‚è¿›ç¨‹å¯åЍ以åŽï¼Œéƒ½ä¼šæœ‰ä¸€ä¸ªä¸Žä¹‹æƒ³å…³è”的用户,程åºä¸­ä»¥ç”¨æˆ·çš„唯一标识 UID æ¥è¡¨ç¤ºã€‚(su 命令å¯ä»¥æ”¹å˜è¿›ç¨‹çš„å¯åŠ¨ç”¨æˆ·) 。 UID 的结构体为 sys/types.h 中定义的 uid_t ,一般是个整型,éžç³»ç»Ÿè¿›ç¨‹ä¹Ÿå°±æ˜¯ç”¨æˆ·è¿›ç¨‹çš„ UID 一般大于 100 , init 进程为系统åˆå§‹è¿›ç¨‹ï¼Œ UID 为 0 。å¯ä»¥é€šè¿‡ä¸‹é¢çš„函数获得进程的 UID 或者用户å:

#include <sys/types.h>
#include <unistd.h>
uid_t getuid(void);
char *getlogin(void);

系统文件 /etc/passwd 中ä¿å­˜ç€ç³»ç»Ÿç”¨æˆ·çš„ä¿¡æ¯ï¼Œæ¯è¡Œä¸ºä¸€ä¸ªç”¨æˆ·çš„ä¿¡æ¯: 用户å:加密密ç :UID:GID/å…¨å:HOME目录:默认shell ,比如:

neil:zBqxfqedfpk:500:100:Neil Matthew:/home/neil:/bin/bash

现在的大多数系统中已ç»ä¸å†æŠŠå¯†ç åР坆䏲ä¿å­˜åœ¨è¿™é‡Œï¼Œè€Œæ˜¯ä½¿ç”¨”å½±å­å¯†ç (shadow password)”文件,一般在 /etc/shadow ,通过é™åˆ¶å¯¹å®ƒçš„æ“ä½œï¼Œæ¥åŠ å¼ºç³»ç»Ÿçš„å®‰å…¨æ€§ã€‚

我们å¯ä»¥é€šè¿‡ä¸‹é¢çš„函数获得用户的信æ¯:

#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);

getpwuid æ ¹æ® UID 返回 passwd 结构, getpwnam 则是通过用户å返回 passwd 结构,跟 /etc/passwd 文件对应,该结构包å«ä»¥ä¸‹æˆå‘˜ï¼š

. char *pw_name 用户å
. uid_t pw_uid UID
. gid_t pw_gid GID - group id
. char *pw_dir HOME 目录
. char *pw_gecos 用户全å
. char *pw_shell 用户默认 shell

注æ„获å–用户全å在ä¸åŒç³»ç»Ÿä¸‹å¯èƒ½å®šä¹‰ä¸ä¸€æ ·ï¼Œä¸Šé¢æ˜¯ linux 下的定义,其他系统å¯èƒ½æ˜¯ pw_comment ã€‚ä¸‹é¢æ˜¯ä¸€ä¸ªä½¿ç”¨ç¤ºèŒƒ:

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

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

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

    printf("User is %s\n", getlogin());

    printf("User IDs: uid=%d, gid=%d\n", uid, gid);

    pw = getpwuid(uid);
    printf("UID passwd entry:\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);

    pw = getpwnam("root");
    printf("root passwd entry:\n");
    printf("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);
    exit(0);
}

如果需è¦é历所有用户信æ¯ï¼Œå¯ä»¥é€šè¿‡ getpwent 函数æ¥å®žçŽ°ï¼Œä¸è¯¦è¿°ã€‚

p194 获得主机信æ¯

如果一个 linux 机器在网络环境下,å¯ä»¥ä½¿ç”¨ gethostname 函数获得主机å,而用 uname å¯ä»¥èŽ·å¾—æ›´å¤šçš„ä¸»æœºä¿¡æ¯ï¼ŒåŒ…括æ“作系统以åŠéƒ¨åˆ†ç¡¬ä»¶ä¿¡æ¯ã€‚

#include <unistd.h>
int gethostname(char *name, size_t namelen);

#include <sys/utsname.h>
int uname(struct utsname *name);

p196 系统日志

æ ¹æ®ä¸åŒçš„目的,linux 下会有ä¸åŒçš„目录ä¿å­˜ç³»ç»Ÿæ—¥å¿—ä¿¡æ¯ã€‚常è§çš„的比如 /usr/adm , /var/log 下。/var/log/message åŒ…å«æ‰€æœ‰çš„系统消æ¯ï¼Œ /var/log/mail 为邮件系统的日志,/var/log/debug 为调试性信æ¯ï¼Œå¯ä»¥åœ¨ /etc/syslog.conf 或者 /etc/syslog-ng/syslog-ng.conf 看到相关目录和文件的é…置。syslog 函数用æ¥è®°å½•系统日志:

#include <syslog.h>
void syslog(int priority, const char *message, arguments...);

日志级别定义: LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG 。很多系统默认设置会把 LOG_EMERG 级的信æ¯å¹¿æ’­ç»™æ‰€æœ‰ç”¨æˆ·ï¼ŒLOG_ALERT 则是 mail 给系统管ç†å‘˜ï¼ŒLOG_DEBUG å¯èƒ½è¢«å¿½ç•¥ã€‚ 使用 syslog 的时候,å¯ä»¥å¦‚下通过 %m æ¥æ’入当å‰é”™è¯¯çš„ä¿¡æ¯(errno 对应的错误信æ¯): syslog(LOG_ERR|LOG_USER, “oops - %m\n”) 。

å¯ä»¥é€šè¿‡ setlogmask 定义需è¦è®°å½•的日志级别。

p201 资æºé™åˆ¶

出于硬件é™åˆ¶(比如内存)或者系统策略等的需è¦ï¼Œlinux 会丢一些资æºçš„使用åšé™åˆ¶ï¼Œæ¯”如å•è¿›ç¨‹åŒæ—¶æ‰“开的文件数。limits.h 中定义了一些常é‡(列了部分):

- NAME_MAX æ–‡ä»¶åæœ€å¤§å­—符数
- CHAR_BIT 字符的字节数
- CHAR_MAX 字符最大值
- INT_MAX int 型最大值

对系统资æºçš„é™åˆ¶å®šä¹‰åœ¨ sys/resource.h ä¸­ã€‚ä¸‹é¢æ˜¯èŽ·å¾—æˆ–è€…è®¾ç½®ç³»ç»Ÿèµ„æºé™åˆ¶çš„常用函数:

#include <sys/resource.h>
int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int priority);
int getrlimit(int resource, struct rlimit *r_limit);
int setrlimit(int resource, const struct rlimit *r_limit);
int getrusage(int who, struct rusage *r_usage);

上é¢ç»“构中的 rusage 是表示 CPU 时间的,至少包括两个æˆå‘˜ï¼š struct timeval ru_utime 用户时间; struct timeval ru_stime 系统时间。 timeval 是 sys/time.h 中定义的包括表示秒和微秒两个æˆå‘˜çš„结构。简å•的说,系统时间就是进程使用系统调用所耗费的时间,用户时间则是进程进行系统调用之外的è¿ç®—耗费的时间。getruage 函数还å¯ä»¥ç”±å‚数指定获å–包括当å‰è¿›ç¨‹çš„å­è¿›ç¨‹çš„ä¿¡æ¯ã€‚

系统默认的进程优先级是 0 ,å¯ç”¨å€¼èŒƒå›´ä¸º -20 到 +20 。所以 getpriority 函数获得进程优先级时,返回 -1 å¯èƒ½æ˜¯å‡½æ•°æ‰§è¡Œå‡ºé”™ä¹Ÿå¯èƒ½æ˜¯ä¼˜å…ˆçº§ä¸º -1 ï¼Œè¿™æ—¶å€™å°±éœ€è¦æ£€æŸ¥ç³»ç»Ÿçš„ errno ä¸­æ˜¯å¦æœ‰å€¼äº†ã€‚

最大打开文件数等资æºé™åˆ¶å¯ä»¥ç”± getrlimit å’Œ setrlimit 获得和设置。 rlimit 结构æè¿°ä¸€ä¸ªé™åˆ¶ï¼Œä¸¤ä¸ªæˆå‘˜åˆ†åˆ«ä»£è¡¨è½¯é™åˆ¶(当å‰è¿›ç¨‹çš„é™åˆ¶)以åŠç¡¬é™åˆ¶(系统的é™åˆ¶)。定义在 sys/resource.h 的常è§é™åˆ¶:

- RLIMIT_CORE 字节表示的 cordump 文件最大值
- RLIMIT_CPU 秒为å•元的 CPU 最大使用时间
- RLIMIT_DATA ç¨‹åºæ•°æ®æ®µä½¿ç”¨çš„字节数é™åˆ¶
- RLIMIT_FSIZE 字节为å•ä½çš„æ–‡ä»¶å¤§å°é™åˆ¶
- RLIMIT_NOFILE åŒæ—¶æ‰“开最大文件数
- RLIMIT_STACK ç¨‹åºæ ˆå¤§å°(字节)
- RLIMIT_AS 地å€ç©ºé—´(栈和数æ®)é™åˆ¶

接下æ¥çš„俩章: 终端 å’Œ 基于文本和光标的å±å¹•å¤„ç†æ²¡æœ‰ä»€ä¹ˆå®žé™…价值,ä¸ç»†çœ‹ã€‚

Linux 环境1(Beginning Linux Programming 笔记8)

Posted in: Linux 编程 — Hick @ 2009/06/07 8:32:56 Comments (0)

p170 程åºå‚æ•°

先明确两个概念:选项ã€é€‰é¡¹å‚数。比如 gcc -g -o test test.c ,这里 g å’Œ o 是选项,其中 test 是 o çš„é€‰é¡¹å‚æ•°ï¼Œä¸€èˆ¬ä¹Ÿç›´æŽ¥ç§°ä¸ºå‚数。

c 语言里最常è§çš„ main 函数原型:

int main(int argc, char *argv[])

其中 argc ä¸ºé€‰é¡¹å’Œå‚æ•°çš„个数, argv ä¸ºé€‰é¡¹å’Œå‚æ•°å­—符串数组。

p171 在 main 函数中å¯ä»¥ç›´æŽ¥è¯»å–上é¢çš„两个å˜é‡è޷得傿•°ä¿¡æ¯ï¼š

$ myprog left right ‘and center’

rgc: 4
argv: {“myprogâ€, “leftâ€, “rightâ€, “and centerâ€}

由于一些历å²åŽŸå› ï¼Œlinux/unix ä¸‹çš„å‘½ä»¤çš„é€‰é¡¹å’Œå‚æ•°æ ¼å¼å¹¶æ²¡æœ‰å¾—到很好的统一规和范。比如解压缩命令 tar zxvf httpd-2.0.59.tar.gz ,查看当å‰ç›®å½•文件列表命令 ls -lt 或者 ls -l -t 。一般建议使用横æ åŠ å•个字符或者数字的方å¼ï¼Œæ²¡æœ‰é™„åŠ å‚æ•°çš„选项一般跟上é¢çš„ ls 命令一样,å¯ä»¥åˆå¹¶åˆ°æ¨ªæ ä»¥åŽã€‚有些命令会有相对应的å呿“作命令,比如 set -o xtrace ç”¨æ¥æ‰“å¼€ shell 脚本执行跟踪,而å呿“作为 set +o xtrace æ¥å…³é—­è¯¥åŠŸèƒ½ã€‚

p172 下é¢çš„ç¨‹åºæ¼”示获得一个程åºå‚数的方å¼ï¼š

for(arg = 0; arg < argc; arg++) {
    if(argv[arg][0] == '-')
        printf("option: %s\n", argv[arg]+1);
    else
        printf("argument %d: %s\n", arg, argv[arg]);
}

注æ„上é¢ç¬¬ä¸‰è¡Œ argv[arg]+1 实际上是指针æ“作,输出以åŽå¾—到字符传的一部分。

p173 针对上é¢çš„建议规范,Linux æä¾› getopt æ¥è§£æžé€‰é¡¹å’Œå‚æ•°(注æ„åŒºåˆ†é€‰é¡¹å’Œå‚æ•°)。函数原型:

#include <unistd.h>
int getopt(int argc, char *const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;

getopt 扫æ argv ä¸­çš„é€‰é¡¹å’Œå‚æ•°ï¼Œè¿”回 optstring 中定义过的选项å。optstring 中定义的选项ååŽåР冒å·è¡¨ç¤ºè¯¥é€‰é¡¹éœ€è¦å‚数,getopt 在扫æåˆ°è¿™æ ·çš„选项åŽï¼Œä¼šæŠŠå‚数值ä¿å­˜åœ¨å…¨å±€å˜é‡ optarg 中。(###待定) optopt 中ä¿å­˜æœ€åŽä¸€ä¸ªç”± getopt() 返回的已知的选项;扫æåˆ°æœ€åŽä¸€é¡¹ç›®è¿”回 -1 ï¼›é‡åˆ°æ— æ•ˆé€‰é¡¹è¿”回问å·ï¼Œoptopt 包å«äº†æ— æ•ˆé€‰é¡¹å­—符。当 getopt 检测到错误,比如 optstring å®šä¹‰æœ‰å‚æ•°çš„é€‰é¡¹æ²¡æœ‰å‚æ•°æ—¶ï¼Œå¯èƒ½ä¼šæŠ›å‡ºé”™è¯¯ï¼Œé€šè¿‡åœ¨è°ƒç”¨ getopt ä¹‹å‰æŠŠ opterr 设置为 0 或者 optstring 的第一个字符为冒å·å¯ä»¥è®© getopt ä¸æŠ›å‡ºé”™è¯¯ï¼›æŠ‘åˆ¶äº†é”™è¯¯åŽï¼Œéœ€è¦æŒ‡å®šå‚数的选项没有选项时,getopt 在返回该选项的地方返回冒å·ï¼Œæ²¡æœ‰æŠ‘制错误时返回问å·ã€‚实例:

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

    while ((ch = getopt(argc, argv, "abcd:")) != -1)
    {
        switch (ch) {
        case 'a':
            printf("option: -a\n");
            break;
        case 'b':
            printf("option: -b\n");
            break;
        case 'c':
            printf("HAVE option: -c\n");
            break;
        case 'd':
            printf("option: -c\n");
            printf("The argument of -b is %s\n", optarg);
            break;
        case '?':
            printf("Unknown option: %c\n",(char)optopt);
            break;
        }
        printf("optind: %d\n", optind);
    }
}

先看下比较正常的执行方å¼:

$ ./a -a -b -c -d hick1 hick2
option: -a
optind: 2
option: -b
optind: 3
HAVE option: -c
optind: 4
option: -c
The argument of -b is hick1
optind: 6

ä¸Šé¢æœ‰ä¸€ä¸ª”䏿­£å¸¸”的地方,就是最åŽä¸€ä¸ªé€‰é¡¹å¤šäº†ä¸€ä¸ªå‚数,getopt å‡½æ•°ä¼šå¿½ç•¥è¿™ä¸ªå‚æ•°ã€‚在扫æåˆ° -a 选项的时候,optind 中ä¿å­˜çš„æ˜¯ä¸‹ä¸€ä¸ªé€‰é¡¹ä¹Ÿå°±æ˜¯ -b 在 agrv 数组中的索引; -d 在 argv 中的索引为 4 ,按照程åºè®¾è®¡åŽé¢éœ€è¦è·Ÿä¸€ä¸ªå‚数,所以 optind 中ä¿å­˜çš„值应该是 6 — å°½ç®¡è¯¥ç´¢å¼•å€¼å¹¶ä¸æ˜¯çœŸæ­£çš„选项。总结æ¥è¯´ï¼Œ optind ä¿å­˜çš„æ˜¯æ ¹æ® optstring 的规则, getopt å³å°†åˆ¤æ–­çš„ä¸‹ä¸€ä¸ªæ˜¯å¦æ˜¯é€‰é¡¹çš„项在 argv 中的索引值。把 -a æ”¾åˆ°æœ€åŽæ‰§è¡Œä¸€ä¸‹å°±å¯ä»¥éªŒè¯ã€‚

å˜é‡ opterr å’Œ optind 都被åˆå§‹åŒ–为1。如果想è¦ç•¥åŽ»å‘½ä»¤è¡Œçš„å‰å‡ ä¸ªå‚数,å¯ä»¥åœ¨è°ƒç”¨ getopt() å‰å°† optind 设æˆå…¶ä»–值。

p175 getopt_long 用æ¥è§£æžå¦å¤–一ç§é£Žæ ¼çš„傿•°ï¼Œæ¯”如 –help 。例å­è™½ç„¶æ˜¯çœ‹æ‡‚了,但是还是有ä¸å°‘疑问,考虑到这å—应用é¢ä¸å¹¿ï¼Œéœ€è¦çš„æ—¶å€™å†è¯¦ç»†æ·±å…¥ã€‚

p177 环境å˜é‡ 我们å¯ä»¥é€šè¿‡ getenv 函数和 putenv ä¸¤ä¸ªå‡½æ•°æ¥æ“作环境å˜é‡ã€‚

#include <stdlib.h>
char *getenv(const char *name);
int putenv(const char *string);

注æ„环境å˜é‡æœ‰è¿™æ ·çš„å­—ç¬¦ä¸²ç»„æˆ name=value 。getenv 会返回 value 的值,找ä¸ç€æ—¶ä¼šè¿”回 null 。如果由于å¯ç”¨å†…å­˜ä¸è¶³å¯¼è‡´ä¿®æ”¹å¤±è´¥(应该很难å‘生å§),会返回 -1 ,这时候全局å˜é‡ errno ä¼šç™¾è®¾ç½®æˆ ENOMEM ã€‚è®¾ç½®å‡½æ•°çš„å‚æ•°å°±æ˜¯ name=value 这样的字符串。

p180 environ å˜é‡ 所有的环境å˜é‡éƒ½ä¿å­˜åœ¨ä¸€ä¸ªå为 environ 的字符串数组中:

#include <stdlib.h>
extern char **environ;

ä¸è¿‡æ²¡æœ‰ç‰¹æ®Šè¦æ±‚,一般建议通过 getenv å’Œ putenv æ¥æ“作环境å˜é‡ï¼Œä¸è¦ç›´æŽ¥æ“作 environ 。下é¢è¿™æ ·å¯ä»¥è¾“出所有的环境å˜é‡ï¼š

#include <stdlib.h>
#include <stdio.h>
extern char **environ;
int main()
{
        char **env = environ;
        while(*env) {
                    printf(“%s\nâ€,*env);
                    env++;
        }
        exit(0);
}

文件æ“作4(Beginning Linux Programming 笔记7)

Posted in: Linux 编程 — Hick @ 2009/06/06 8:59:14 Comments (0)

p152 stream 错误: 标准 io 库出错的时候,通常都返回一个 null 指针或者 EOF ï¼ŒåŒæ—¶æŠŠé”™è¯¯çš„具体信æ¯ä¿å­˜åˆ°å…¨å±€å˜é‡ errno 中。这是个很多函数比如 fprintf 都å¯èƒ½æ”¹å†™çš„å˜é‡ï¼Œåªæœ‰å½“出错时,其值æ‰å…·å¤‡å‚考价值。

#include <errno.h>
extern int errno;

由于文件到达末尾和出错都å¯èƒ½è¿”å›žåŒæ ·çš„值比如 EOF ,库函数æä¾›ä¸€äº›æ–¹æ³•æ¥åŒºåˆ†ã€‚

#include <stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);
void clearerr(FILE *stream);

ferror ç”¨æ¥æ£€æŸ¥ stream 上是å¦å‘生了错误,没有出错返回 0 ï¼Œå‡ºé”™è¿”å›žéž 0 值。 feof 检测是å¦åˆ°è¾¾äº† stream çš„æœ«å°¾ï¼Œéž 0 值表示到达文件末尾。

stream å’Œ fd 的关系: æ¯ä¸ª stream 都和个 fd 相关è”,甚至是 fd çš„æ“作和 stream çš„æ“作在转æ¢åŽå¯ä»¥æ··åˆä½¿ç”¨ï¼Œä½†æ˜¯è¿™æ ·æ²¡æ³•利用 io 库的 buffer 等特性,如果有相关æ“作å¯èƒ½é€ æˆä¸å¯é¢„è§çš„问题。他们之间通过下é¢çš„函数å¯ä»¥è¿›è¡Œè½¬æ¢ï¼š

#include <stdio.h>
int fileno(FILE *stream);
FILE *fdopen(int fildes, const char *mode);

有些特殊情况下,这些函数会比较实用。比如如果è¦é€šè¿‡ç³»ç»Ÿè°ƒç”¨èŽ·å¾—å·²ç»æ‰“开的 stream 的一些信æ¯ï¼Œå¯ä»¥é€šè¿‡ fileno 获得 fd ,然åŽè°ƒç”¨ç³»ç»Ÿè°ƒç”¨æ¯”如 fstat æ¥èŽ·å¾—æ–‡ä»¶çš„ä¿¡æ¯ï¼›å过æ¥ï¼Œä¹Ÿå¯ä»¥æŠŠå·²ç»æ‰“开的 fd 转æ¢ä¸º stream 以åŽï¼Œåˆ©ç”¨æ ‡å‡†åº“çš„ buffer 等特性æ“作文件。

p153 文件和目录维护 下é¢ä»‹ç»ç³»ç»Ÿè°ƒç”¨ä¸€èˆ¬éƒ½æœ‰å¯¹åº” shell 命令å¯ä»¥å‚照。chmod å’Œ chown 为系统调用:

# chmod
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);

# chown
#include <sys/types.h>
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);

unlink 用æ¥åˆ é™¤ä¸€ä¸ªæ–‡ä»¶åœ¨ç›®å½•中的入å£ï¼Œè¿”回 0 表示删除æˆåŠŸã€‚é€šå¸¸æœ‰ä¸€ä¸ªå°æŠ€å·§å°±æ˜¯å…ˆ open 一个文件,然åŽè¿›è¡Œå®Œä¸€å®šçš„æ“ä½œä»¥åŽè¿›è¡Œ unlink æ“作,这就æ„味ç€è¿™ä¸ªæ–‡ä»¶åªåœ¨ç¨‹åºæ‰§è¡Œæ—¶å­˜åœ¨ï¼Œä¸ºä¸´æ—¶æ–‡ä»¶ï¼Œè¿›ç¨‹æ‰§è¡Œå®Œæ¯•以åŽï¼Œæ–‡ä»¶è‡ªåŠ¨è¢«å…³é—­ã€‚

#include <unistd.h>
int unlink(const char *path);
int link(const char *path1, const char *path2);
int symlink(const char *path1, const char *path2);

rmdir å’Œ mkdir 分别用æ¥åˆ é™¤å’Œåˆ›å»ºç›®å½•:

# mkdir
#include <sys/types.h>
#include <sys/stat.h>
int mkdir(const char *path, mode_t mode);

# rmdir
#include <unistd.h>
int rmdir(const char *path);

æ¯ä¸ªè¿›ç¨‹éƒ½ä¼šæœ‰ä¸€ä¸ªå½“å‰ç›®å½•,å¯ä»¥é€šè¿‡ chdir æ¥æ”¹å˜å½“å‰ç›®å½•,也å¯ä»¥é€šè¿‡ getcwd æ¥è޷得当å‰å·¥ä½œç›®å½•。

# chdir
#include <unistd.h>
int chdir(const char *path);

# getcwd
#include <unistd.h>
char *getcwd(char *buf, size_t size);

getcwd 函数会把获得路径åä¿å­˜åˆ° buffer ,如果路径å大于 size(ERANGE error), 函数返回 null , 如果æ“作æˆåŠŸåˆ™è¿”å›ž buf 。

p155 é历目录 目录æ“作的相关函数是在 dirent.h 中声明的,标准库定义了一个结构 DIR ç”¨æ¥æè¿°ç›®å½•ã€‚æŒ‡å‘è¿™ç§ç»“构的指针被称为 directory stream, è·Ÿ file stream 类似。需è¦åŒ…å«çš„头文件都一样,相关函数原型如下:

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
long int telldir(DIR *dirp);
void seekdir(DIR *dirp, long int loc);
int closedir(DIR *dirp);

- opendir 打开一个目录(文件),æ“作æˆåŠŸè¿”å›ž directory stream ,å¦åˆ™è¿”回 null 指针。

- readdir 返回指定的 directory stream 中的下一个项(文件或å­ç›®å½•)的结构。按照 POSIX 兼容标准,读到最åŽè¿”回 null ï¼Œå¹¶ä¸”ä¸æ”¹å˜ errno 的值;读å–出错时也返回 null ï¼ŒåŒæ—¶æ”¹å˜ errno 的值。 注æ„如果该函数执行过程中有其他进程在对该目录下的项进行æ“作,该函数ä¸ä¿è¯æ‹¿åˆ°æœ€æ–°ä¿®æ”¹åŽçš„æ•°æ®ã€‚返回的 dirent 结构中包å«ä¸‹é¢ä¸¤é¡¹ï¼š ino_t d_ino 文件(包括å­ç›®å½•)çš„ inode 节点; char d_name[]: 文件(包括å­ç›®å½•)åã€‚è¦æ£€æŸ¥èŽ·å¾—çš„é¡¹æ˜¯ä¸€èˆ¬æ–‡ä»¶è¿˜æ˜¯ç›®å½•ï¼Œéœ€è¦è°ƒç”¨å‰é¢ä»‹ç»çš„ stat 函数。

- telldir 用æ¥è¿”å›žè¡¨ç¤ºå½“å‰ directory stream çš„ä½ç½®çš„æ•´æ•°ï¼Œä¸€èˆ¬é€šè¿‡é…åˆä¸‹é¢çš„ seekdir 使用。

- seekdir é‡è®¾è¯»å–çš„ dirp çš„ä½ç½®ã€‚

- closedir 用æ¥å…³é—­ä¸€ä¸ª directory stream ,在é历目录时,为了ä¿è¯å½“剿‰“开的目录数ä¸è¾¾åˆ°ç³»ç»Ÿé™åˆ¶ï¼Œä¸€èˆ¬éœ€è¦å³ä½¿å…³é—­æ‰“开的 directory stream 。

䏋颿˜¯ä¸€ä¸ªç›¸å…³å‡½æ•°çš„使用实例:

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

void 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)) {
            /* Found a directory, but ignore . and .. */
            if(strcmp(".",entry->d_name) == 0 ||
                strcmp("..",entry->d_name) == 0)
                continue;
            printf("%*s%s/\n",depth,"",entry->d_name);
            /* Recurse at a new indent level */
            printdir(entry->d_name,depth+4);
        }
        else printf("%*s%s\n",depth,"",entry->d_name);
    }
    chdir("..");
    closedir(dp);
}

/*  Now we move onto the main function.  */
int main()
{
    printf("Directory scan of /home:\n");
    printdir("/home",0);
    printf("done.\n");

    exit(0);
}

p160 错误 error.h 中定义的常用错误包括:

- EPERM 没有æ“作æƒé™
- ENOENT æ“作的文件或者目录ä¸å­˜åœ¨
- EINTR 系统调用被中断
- EIO I/O 错误
- EBUSY 设置或者资æºç¹å¿™
- EEXIST 文件已ç»å­˜åœ¨
- EINVAL 傿•°ä¸åˆæ³•
- EMFILE 打开文件 fd 太多
- ENODEV æ²¡æœ‰éœ€è¦æ‰“开的设备
- EISDIR æ“作对象是目录
- ENOTDIR æ“ä½œå¯¹è±¡ä¸æ˜¯ç›®å½•

å¦å¤–还有两个用有的函数用æ¥è§£è¯» errno :

#include <string.h>
char *strerror(int errnum);

#include <stdio.h>
void perror(const char *s);

strerror ç”¨æ¥æŠŠé”™è¯¯å· errno 错误信æ¯çš„æ–‡æœ¬æè¿°ã€‚而 perror 则是自动获å–当å‰çš„ errno 并用文本æè¿°é”™è¯¯ä¿¡æ¯ï¼Œåœ¨é”™è¯¯ä¿¡æ¯å‰é¢åŠ ä¸Šå­—ç¬¦ä¸² s 和一个空格。

p162 文件系统中的 /proc 通过系统调用访问 /dev 下的文件没有å¯ä»¥å®žçŽ°å’Œç¡¬ä»¶çš„äº¤äº’ã€‚é€šå¸¸æˆ‘ä»¬éƒ½ä¼šæœ‰ä¸€äº›å®žç”¨å·¥å…·èƒ½å¤Ÿè®¿é—®ç³»ç»Ÿç¡¬ä»¶è®¾å¤‡ï¼Œæ¯”å¦‚ hdparm ç”¨æ¥æŽ§åˆ¶ç£ç›˜å‚数, ifconfig 用æ¥é…ç½®ç½‘ç»œè®¾å¤‡çš„å‚æ•°ã€‚ linux 也æä¾›ç‰¹æ®Šçš„æ–‡ä»¶ç³»ç»Ÿ procfs ,也就是 /proc 下的文件,让我们能够从更高的层é¢è®¿é—®é©±åŠ¨ç¨‹åºå’Œç³»ç»Ÿå†…核。

/proc 下的æŸäº›æ–‡ä»¶ä¸­ä¿å­˜äº†ä¸€äº›åªè¯»çš„系统信æ¯ï¼Œæ¯”如 /proc/cpuinfo 中ä¿å­˜äº†ç³»ç»Ÿ CPU 的情况, /proc/meminfo å’Œ /proc/version ä¿å­˜äº†å†…存以åŠå†…æ ¸ç‰ˆæœ¬çš„æƒ…å†µã€‚æ¯æ¬¡è¿™äº›æ–‡ä»¶è¢«è¯»å–时,系统会去获得最新的相关信æ¯ã€‚比如 /proc/net/sockstat 中ä¿å­˜äº†å½“å‰çš„网络 socket 的使用情况,而其使用情况是å˜åŒ–比较频ç¹çš„。

/proc 下也有一些文件å¯ä»¥è¢«å†™ï¼Œæ¯”å¦‚ç³»ç»ŸåŒæ—¶è¿è¡Œçš„进程打开的总文件数å¯ä»¥ç›´æŽ¥ä»Ž /proc/sys/fs/file-max 中获得(ä¸ºå†…æ ¸å‚æ•°),我们也å¯ä»¥ç”¨ echo 80000 >/proc/sys/fs/file-max çš„æ–¹å¼æ¥ä¸´æ—¶ä¿®æ”¹è¯¥å€¼ã€‚

/proc 目录下有很多数字命令的目录,æ¯ä¸ªç›®å½•代表一个正在è¿è¡Œçš„进程,目录åå³è¿›ç¨‹ id,目录下ä¿å­˜äº†å½“å‰è¿›ç¨‹çš„相关执行信æ¯ï¼Œæ¯”如进程的路径,进程执行的”当å‰è·¯å¾„”等。下é¢çš„ç¬¬å››ç« ä¼šæœ‰è¯¦ç»†æŽ¢è®¨ã€‚æ³¨æ„ fd å­ç›®å½•ä¿å­˜äº†å½“å‰è¿›ç¨‹æ‰“开的文件信æ¯ï¼Œå…·ä½“æ¥è¯´ï¼Œæˆ‘能够看到 fd 数字,比如标准设备 0, 1, 2 。

p165 fcntl 和 mmap

fcntl 能够对原始的 fd 进行上é¢ä»‹ç»ä»¥å¤–的更多æ“作,比如å¤åˆ¶ fd, 获得或设置 fd 的标记(flag)ï¼Œç®¡ç†æ–‡ä»¶å—等。

#include <fcntl.h>
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);

ä¸åŒçš„æ“ä½œæœ‰ä¸åŒçš„ç¬¬ä¸‰ä¸ªå‚æ•°ã€‚比如:

- fcntl(fildes, F_DUPFD, newfd) 用æ¥å¤åˆ¶ä¸€ä¸ª fd ï¼Œå®ƒæ ¹æ® fildes å¤åˆ¶å‡ºä¸€ä¸ª fd 数等于或者大于 newfd 的数,并返回一个新的 fd ,跟 filedes 指å‘åŒä¸€ä¸ªæ–‡ä»¶ã€‚也就是说,该功能很大程度上跟 dup 函数类似。

- fcntl(fildes, F_GETFD) 返回 fcntl.h 中定义的 fd 标记,比如 FD_CLOEXEC 表示 exec 系列函数调用æˆåŠŸä»¥åŽï¼Œfd 是å¦è¢«å…³é—­ã€‚

- fcntl(fildes, F_SETFD, flags) 跟上é¢çš„函数对应,是用æ¥è®¾ç½® fd 标记的,一般常用的也是 FD_CLOEXEC 。

- fcntl(fildes, F_GETFL) å’Œ fcntl(fildes, F_SETFL, flags) ç”¨ç±»èŽ·å¾—å’Œè®¾ç½®æ–‡ä»¶çŠ¶æ€æ ‡è®°ä»¥åŠè®¿é—®æ–¹å¼(access mode),你å¯ä»¥ä½¿ç”¨ fcntl.h 中定义的 mask O_ACCMODE æ¥å¯¹æ¯”出文件访问方å¼ã€‚open 函数 O_CREAT æ—¶ç¬¬ä¸‰ä¸ªå‚æ•°çš„相关 flag 也å¯ä»¥é€šè¿‡è¿™é‡Œæ¥è®¾ç½®ï¼Œæ³¨æ„并䏿˜¯æ‰€æœ‰çš„设置都能够在这里æ“作,典型的是文件访问æƒé™ä¸èƒ½å¤Ÿé€šè¿‡ fcntl 设置。

p166 mmap 是 memery map çš„æ„æ€ï¼Œæ˜¯ linux 内核 2.0 ä»¥åŽæ”¯æŒçš„功能,它å…许我们把文件映射到内存中,这样ä¸åŒè¿›ç¨‹èƒ½å¤Ÿåƒæ“ä½œæ–‡ä»¶ä¸€æ ·æ¥æ“作内存。 mmap å‡½æ•°å°±æ˜¯ç”¨æ¥æŠŠä¸€ä¸ªæ‰“å¼€çš„æ–‡ä»¶ fd 映射到内存:

#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

å¯ä»¥é€šè¿‡æŒ‡å®š off 文件内容中映射到内存的开始ä½ç½®ï¼›addr 为内存的开始地å€ï¼Œä¸º 0 时,会自动分é…å¯ç”¨çš„内存,为了防止一出现访问到使用中的内存,一般使用 0 ä½œä¸ºè¯¥å‚æ•°å€¼; len 为分é…的内存长度; filedes ä¸ºéœ€è¦æ˜ å°„的文件的 fdï¼› prot ä¸ºå†…å­˜ä¿æŠ¤æ ‡å¿—(访问模å¼),ä¸èƒ½ä¸Žæ–‡ä»¶çš„æ‰“开模å¼å†²çªï¼Œä¸€èˆ¬ä½¿ç”¨ä¸‹åˆ—值的或è¿ç®—:

- PROT_EXEC 内存段(segment)坿‰§è¡Œ
- PROT_READ å¯ä»¥è¢«è¯»å–
- PROT_WRITE å¯ä»¥è¢«å†™å…¥
- PROT_NONE ä¸å¯è®¿é—®

flags 傿•°æŽ§åˆ¶ç¨‹åºä¿®æ”¹å†…å­˜æ®µå†…å®¹æ—¶å½±å“æ–‡ä»¶çš„æ–¹å¼:

- MAP_PRIVATE å†…å­˜æ®µä¸ºç§æœ‰(改å˜åªé’ˆå¯¹è¯¥è¿›ç¨‹)
- MAP_SHARED 内存段共享, 所有改å˜éƒ½ä¼šè¢«åˆ·åˆ°æ–‡ä»¶ä¸­
- MAP_FIXED å¿…é¡»æŒ‡å®šå†…å­˜æ®µçš„åœ°å€ addr(###)

msync 将映射内存中的数æ®å†™å›žç£ç›˜ã€‚å¯ä»¥æŒ‡å®šå†…存地å€å’Œé•¿åº¦ä»¥åŠå†™å›žæ¨¡å¼ï¼š

#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);

munmap 函数用æ¥é‡Šæ”¾å†…å­˜:

#include <sys/mman.h>
int munmap(void *addr, size_t len);
Next Page »