Linux编程实践教程小结

来源:互联网 发布:js正则表达式匹配数字 编辑:程序博客网 时间:2024/05/25 23:29

编写Linux who 命令

Linux文件操作函数:
- 打开一个文件
int fd = open(char *name, int how)
参数 how 代表文件的打开方式:
- O_RDONLY, O_WRONLYO_RDWR 代表只读,只写,和读写

  • 通过文件读取数据: ssize_t numread = read(int fd, void *buf, size_t qty)

    • fd 文件描述符
    • buf 用来存放数据的目的缓冲区
    • qty 要读取的字节数
  • 关闭文件: int result = close(int fd)

用户登录信息存放在 utmp 结构中通过打开操作:
utmpfd = open(UTMP_FILE, O_RDONLY) 获得

utmput_time 代表时间,可以使用 ctime 函数将 long 类型的时间转换成表示时间的字符串:
char *ctime(const time_t *timep);

who 命令代码:

#include <sys/types.h>#include <utmp.h>#include <stdlib.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <time.h>#include "utmplib.c"#define SHOWHOSTvoid show_time(long);void show_info(struct utmp * utbufp);int main(){    struct utmp *utbufp, *utmp_next();  //  utmp结构,存储读入的信息    if (utmp_open(UTMP_FILE) == -1)    {        perror(UTMP_FILE);        exit(1);    }    while((utbufp = utmp_next()) != NULLUT)        show_info(utbufp);    utmp_close();    return 0;}void show_info(struct utmp *utbufp){    if (utbufp->ut_type != USER_PROCESS)        return; //修改  只显示当前活动用户    printf("%-8.8s", utbufp->ut_name);    printf(" ");    printf("%-8.8s", utbufp->ut_line);    printf(" ");    show_time(utbufp->ut_time); //使用ctime函数转换时间    printf(" ");#ifdef SHOWHOST    if (utbufp->ut_host[0] != '\0')        printf("(%s)", utbufp->ut_host);#endif    printf("\n");}void show_time(long time_val){    struct tm *p;    p = localtime(&time_val);    printf(" %d-%d-%d %d:%d ",1900+p->tm_year, 1+p->tm_mon, p->tm_mday,p->tm_hour, p->tm_min);  //年份加1900 月份加1}
  1. 编写 cp 命令: 创建新文件, 从源文件中读取数据写入新文件中

    • 创建一个新文件: int fd = creat(char *filename, mode_t mode)
    • mode 表示新文件的访问模式 即权限

    • 向已打开文件写入数据:ssize_t result = write(int fd, void *buf, size_t amt)

    • amt 要写入的字节数
    • 返回值: -1 遇到错误, 或者返回写入的字节数

编写 ls 命令: 列出目录内容,显示文件信息

思路: 查看系统 ls 命令 ,得知需要获得当前目录下所有文件和文件夹名称,以及其的信息,包括用户,权限,文件大小,创建时间等。

解决:
- 获取当前目录信息可使用 struct dirent *readdir(DIR *dirp); 函数返回一个结构体,里面包含了:

 struct  dirent {    ino_t          d_ino;       /* inode number */    off_t          d_off;       /* not an offset; see NOTES */    unsigned short d_reclen;    /* length of this record */    unsigned char  d_type;      /* type of file; not supported                                    by all filesystem types */    char           d_name[256]; /* filename */};
  • 获取文件状态信息使用: int result = stat(char *fname, struct stat *bufp)
    • bufp 为指向存储信息的 buffer 指针
      struct stat 结构体包含:
struct stat {    dev_t     st_dev;     /* ID of device containing file */    ino_t     st_ino;     /* inode number */    mode_t    st_mode;    /* protection */    nlink_t   st_nlink;   /* number of hard links */    uid_t     st_uid;     /* user ID of owner */    gid_t     st_gid;     /* group ID of owner */    dev_t     st_rdev;    /* device ID (if special file) */    off_t     st_size;    /* total size, in bytes */    blksize_t st_blksize; /* blocksize for filesystem I/O */    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */    time_t    st_atime;   /* time of last access */    time_t    st_mtime;   /* time of last modification */    time_t    st_ctime;   /* time of last status change */};
  • 其中 st_mode 是一个16位的二进制数 每一位表示一个文件类型或者权限信息,使用掩码可以得到具体信息,在 <sys/stat.h> 中也定义了一些宏用来表示掩码值,如:
    S_ISDIR(mode) 判断是否是文件夹
    if (mode & S_IRUSR) 用来判断用户读权限

  • st_uid 表示用户ID 可用struct passwd *getpwuid(uid_t uid); 函数获得一个表示用户信息的结构体指针,其中包含有用户名:

struct passwd {    char   *pw_name;       /* username */    char   *pw_passwd;     /* user password */    uid_t   pw_uid;        /* user ID */    gid_t   pw_gid;        /* group ID */    char   *pw_gecos;      /* user information */    char   *pw_dir;        /* home directory */    char   *pw_shell;      /* shell program */};

最终代码如下:

#include <string.h>#include <sys/types.h>#include <stdlib.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <dirent.h>#include <grp.h>#include <pwd.h>#include <time.h>#include <malloc.h>#include <sys/ioctl.h>//ls版本3 实现输出排序 和 参数为目录//还有许多不足 如没有检查内存分配成功与否  结构安排不合理 等等int get_win_size(struct winsize *size);void do_ls(char []);void do_ls_l(char []);void do_stat(char *);   //打印文件状态void show_file_info(char*, struct stat *);void mode_to_letters(mode_t , char[]);char * uid_to_name(uid_t);char * gid_to_name(gid_t);//按照名称排序static intcompar(const void* p1, const void *p2){    return strcmp((*(struct dirent **)p1)->d_name, (*(struct dirent **)p2)->d_name);}void qsort(void *base, size_t nmemb, size_t size,            int (*compar)(const void *, const void *));//获取窗口大小int get_win_size(struct winsize *size){    if (isatty(STDOUT_FILENO) == 0)        exit(1);    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, size)<0)    {        perror("ioctl TIOCGWINSZ error");        exit(1);    }    return 0;}//只需列出档期内文件夹下的文件名void do_ls(char * dirname){    size_t total = 0;    //记录目录下文件总数    DIR * dir_ptr;    struct dirent *direntp;    char ** name_array;   //保存文件名的数组    size_t array_size = 64; //初始时数组大小是64    size_t max_name_len = 0;    //最大文件名长度 字节为单位    struct winsize size;    //窗口大小信息    get_win_size(&size);    name_array = (char **)malloc(sizeof(char[MAXNAMLEN+1]) * array_size);  //一次性分配64个单元 不够再分    if ((dir_ptr = opendir(dirname)) == NULL)   //打开目录    {        fprintf(stderr, "ls1: cannot open%s\n", dirname);    }    else    {        while((direntp = readdir(dir_ptr)) != NULL) //读取目录信息, 存储在目录的结构指针中        {            //重新分配内存            if(total == array_size)            {   array_size =  array_size * 2;                name_array = (char **)realloc(name_array,sizeof(char[MAXNAMLEN+1]) * array_size);            }            if (strlen(direntp->d_name) > max_name_len)                max_name_len = strlen(direntp->d_name);     //更新最大名字长度            *(name_array + total) = direntp->d_name;    //向数组里填充数据            total++;    //文件数加1        }        size_t col = size.ws_col / (max_name_len+1);    //每行最多放置的文件数量        size_t i=0 ,j = 0;        printf("total:%d\n", total);        for(;i < total; i++)        {            if (**(name_array+i) == '.')                continue;            else{                printf("%-*s",max_name_len+1, *(name_array + i));    //控制输出宽度                //不能处理文件名含有汉字情况                j++;                if( j % col == 0)                    printf("\n");//达到每行最多数量 回车            }        }        printf("\n");        closedir(dir_ptr);    }}//列出详细信息void do_ls_l(char dirname[]){    size_t total = 0;    //记录目录下文件总数    DIR * dir_ptr;    struct dirent *direntp;    struct dirent ** dparray;   //目录数组    size_t array_size = 64; //初始时数组大小是64    dparray = (struct dirent **)malloc(sizeof(direntp) * array_size);  //一次性分配64个单元 不够再分    if ((dir_ptr = opendir(dirname)) == NULL)   //打开目录    {        fprintf(stderr, "ls1: cannot open%s\n", dirname);    }    else    {        while((direntp = readdir(dir_ptr)) != NULL) //读取目录信息, 存储在目录的结构指针中        {            //重新分配内存            if(total == array_size)            {   array_size = array_size*2;                dparray = (struct dirent **)realloc(dparray,sizeof(direntp) * array_size);            }            *(dparray + total) = direntp;   //向数组里填充数据            total++;    //文件数加1        }        qsort(dparray, total, sizeof(struct dirent *), compar); //按照名称排序        size_t i = 0;        for (i = 0; i < total; i++)              do_stat((*(dparray+i))->d_name);        closedir(dir_ptr);    }}void do_stat(char *filename){    struct stat info;   //存储文件状态信息    if(stat(filename, &info) == -1) //stat 将 filename 中的状态信息存储在info中        perror(filename);    else        show_file_info(filename, &info);}void show_file_info(char* filename, struct stat *infop){    //打印保存在 struct stat中的文件信息    char modestr[11];   //用来存储 文件类型说明    mode_to_letters(infop->st_mode, modestr);   //将文件类型转换成合适的形式    printf("%s ", modestr); //文件类型及权限信息    printf("%-4d", (int)infop->st_nlink);   //文件当前链接信息    printf("%-8s", uid_to_name(infop->st_uid)); //用户名    printf("%-8s", gid_to_name(infop->st_gid)); //组名    printf("%-12ld", (long)infop->st_size); //文件大小    printf("%.12s", 4 + ctime((const time_t*)&infop->st_mtim)); //修改时间    printf(" %s\n", filename);}void mode_to_letters(mode_t  mode, char str[]){    strcpy(str, "----------");  //10个字符 表示文件类型信息    //下面判断文件类型以及权限    if (S_ISDIR(mode)) str[0] = 'd';    if (S_ISCHR(mode)) str[0] = 'c';    //字符设备    if (S_ISBLK(mode)) str[0] = 'b';    //块设备    //判断用户权限权限    if (mode & S_IRUSR) str[1] = 'r';    if (mode & S_IWUSR) str[2] = 'w';    if (mode & S_IXUSR) str[3] = 'x';    //判断组权限    if (mode & S_IRGRP) str[4] = 'r';    if (mode & S_IWGRP) str[5] = 'w';    if (mode & S_IXGRP) str[6] = 'x';    //判断其他用户权限    if (mode & S_IROTH) str[7] = 'r';    if (mode & S_IWOTH) str[8] = 'w';    if (mode & S_IXOTH) str[9] = 'x';}char* uid_to_name(uid_t uid){    struct passwd* pw_p;    static char numstr[10];    if ((pw_p = getpwuid(uid)) == NULL)    {        sprintf(numstr, "%d", uid); //如果获取用户名失败 则打印用户ID        return numstr;    }    else      return pw_p->pw_name;}char* gid_to_name(gid_t gid){    struct group * gr_p;    static char numstr[10];    if ((gr_p = getgrgid(gid)) == NULL)    {        sprintf(numstr, "%d", gid);        return numstr;    }    else      return gr_p->gr_name;}int main(int ac, char * av[]){    if (ac == 1)      do_ls(".");    if (ac == 2)    {        if (*av[1] == '-')            do_ls_l(".");        else{            chdir(av[1]);            do_ls(".");        }    }    if(ac > 2)    {        int flag = 1;        if (*av[1] == '-')            flag = 2;        while(ac != flag)        {            chdir(av[ac-1]);            if (flag == 2)                do_ls_l(".");            if (flag == 1)                do_ls(".");            ac--;        }    }    return 0;}

编写 pwd 命令

Unix 文件由内容和属性两部分组成, 文件内容存放在数据区, 文件属性放在 i-node
i-Node 中存放有文件存储数据块列表,目录中存放文件名和对应 i-node 位置

文件在目录中即文件的 iNode 入口和文件名存放在目录中

思路: 得到当前目录中名称为 .iNode: n ,然后切换到上一目录,得到iNoden 的目录名,一直重复,直到根目录。

pwd 命令中涉及到的系统调用:

  1. 创建目录: int result = mkdir(char *pathname, mode_t mode)
  2. 删除目录,此目录必须为空: int result = rmdir(const char *path)
  3. 删除一个链接,即从一个目录文件中删除一个记录:int result = unlink(const char* path)
  4. 创建一个文件的链接 int result = link(const char*orig, const char *new)
  5. 重命名或者删除一个链接 int result = rename(const char *from, const char* to)
  6. 切换当前工作目录: int result = chdir(const char *path)

信号处理:

result = signal = (int signum, void(*action)(int))

  • signum 需响应的信号
  • action 处理信号的函数

其中action 可以是信号处理函数,或者是 SIG_IGNSIG_DFL 分别代表忽略信号或默认处理

时钟编程

  1. 设置发送信号的计时器: unsigned old = alarm(unsigned seconds)

    #include <stdio.h>#include <signal.h>int main(){ void wakeup(int); printf("about sleep for 4 seconds\n"); signal(SIGALRM, wakeup);   //设置时钟信号处理函数 wakeup alarm(4);  //设置时钟 隔四秒发送时钟信号 pause();   //挂起等待信号 printf("Morning so soon?\n");}void wakeup(int signum){ printf("Alarm received from kernal\n");}
  2. 使用间隔计时器:
    在间隔计时器设置的结构体中有两个参数,it_valueit_interval.分别表示初始时间和间隔时间.
    通过使用getitimersettimer可以获得和设置间隔计时器:
    result = getitimer(int which, struct itimerval *val);
    result = setitimer(int which, cosnt struct itimerval *newval, struct itimerval *oldval);

    • which 获取或设置的计时器
    • val 指向向当前设置值的指针
    • newval 指向要被设置值的指针
    • oldval 指向被替换的设置值的指针

示例代码:

#include <stdio.h>#include <sys/time.h>#include <signal.h>#include <stdlib.h>void countdown(int);int set_ticker(int);int main(int argc, const char *argv[]){    signal(SIGALRM, countdown);    if (set_ticker(500) == -1)        perror("set_ticker");    else        while(1)            pause();    return 0;}void countdown(int signum){    static int num = 10;    printf("%d..", num--);    fflush(stdout);    if (num < 0){        printf("DONE!\n");        exit(0);    }}int set_ticker(int n_msecs){    struct itimerval new_timeset;    long n_sec, n_usecs;    //设置秒数和微秒数    n_sec = n_msecs / 1000;    n_usecs = (n_msecs % 1000) * 1000L;    new_timeset.it_interval.tv_sec = n_sec;    new_timeset.it_interval.tv_usec = n_usecs;    new_timeset.it_value.tv_sec = n_sec;    new_timeset.it_value.tv_usec = n_usecs;    return setitimer(ITIMER_REAL, &new_timeset, NULL);}

进程和程序:

  1. 在指定的路径中查找并执行一个文件 result = execvp(const char *file, const char *argv[])
  2. 建立一个新进程 pid_t result = fork() 在父进程中返回值是子进程的pid,在子进程中返回的是0.
  3. 等待进程的结束: pid_t result = wait(int *statusptr)

示例代码:

//通过输入参数执行系统命令的程序 带提示符 能执行程序#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#define MAXARGS     20  //最大命令数量#define ARGLEN      100 //最大命令长度int execute(char* arglist[]);   //执行命令函数char* makestring(char* buf);    //将输入字符转换成字符串int main(){    char *arglist[MAXARGS+1];    int  numargs;    char argbuf[ARGLEN];    numargs = 0;    while(numargs < MAXARGS)    {        printf("Arg[%d]?", numargs);    //提示输入参数        if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')        {            //添加退出条件            if (strncmp(argbuf,"exit",strlen(argbuf)-1) == 0 || *argbuf == EOF)            {                exit(1);            }            arglist[numargs++] = makestring(argbuf);        }        else        {            if (numargs > 0)            {                arglist[numargs] = NULL;                int fork_ret = 0;                fork_ret = fork();                if (fork_ret == -1)                    perror("fork error");                              else if (fork_ret == 0)                    execute(arglist);                else                {                    signal(SIGINT, SIG_IGN);    //父进程忽略中断信号                    wait(NULL);     //等待子进程退出                    numargs = 0;                    continue;                }            }        }    }    return 0;}int execute(char *arglist[]){    execvp(arglist[0], arglist);    perror("execvp");    exit(1);}char * makestring(char *buf){    char *cp;    buf[strlen(buf) - 1] = '\0';    cp = (char *)malloc(strlen(buf) + 1);    if (cp == NULL)    {        fprintf(stderr, "no memory\n");        exit(1);    }    strcpy(cp, buf);    return cp;}

输入输出重定向

  1. int dup(int fd); int dup2(int oldfd, int newfd);
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>//使用dup dup2重定向//dup : open -> close -> dup -> close//dup2: open -> dup2 -> close//#define CLOSE_DUPint main(int argc, const char *argv[]){    int fd;    int newfd;    char line[BUFSIZ];    fgets(line, 100, stdin); printf("%s\n", line);    if ((fd = open("/etc/passwd", O_RDONLY)) == -1)    {        perror("Can't open file as fd 0\n");        exit(1);    }#ifdef CLOSE_DUP    close(0);   //关闭原来使用的输入文件描述符    newfd = dup(fd);    //将新打开的文件描述符链接到 最小可用文件描述符(即上一行关闭 的 0)#else    newfd = dup2(fd, 0);    //完成 close(0) 和 dup(fd) 两步#endif    if (newfd != 0)    {        fprintf(stderr, "Can't duplicate fd to 0\n");        exit(1);    }    fgets(line, 100, stdin);    printf("%s\n", line);    return 0;}
  1. 管道 int pipe(int pipearray[2]);pipearray[1]写 从pipearray[0]
    #include <stdio.h>    #include <unistd.h>    #include <stdlib.h>    //管道    int main()    {        int len, apipe[2];        char buf[BUFSIZ];        apipe[0] = 3;apipe[1] = 4;  //管道从fd=4写入 从fd=3 读出        if (pipe(apipe) == -1)  //创建管道        {            perror("make pipe err:");            exit(1);        }        while(fgets(buf, BUFSIZ, stdin))    //从标准输入读取        {            len = strlen(buf);            //写入管道            if (write(apipe[1], buf, len) == -1)            {                perror("write pipe err:");                exit(1);            }            //从管道读出            len = read(apipe[0], buf, BUFSIZ);            if(len == -1)            {                perror("reading from pipe:");                exit(1);            }            //向标准输出写            if (write(1, buf, len) != len)            {                perror("writing to stdout:");                exit(1);            }        }        return 0;    }

父进程和子进程共享打开的文件描述符和管道
同一个管道既有父进程的两个文件描述符pipe[0] pipe[1]链接 也有子进程的pipe[0] pipe[1]链接
管道同一时刻只能有一个进程读或写。 当管道中没有数据时,读或写被阻塞,当管道的一段被关闭,对管道另一端的读或写将返回错误码EPIPE.

  1. FILE* fdopen(int fd, char* mode) 能像操作文件一样操作管道:
...char mypipe[2];pipe(mypipe);FILE *fp;fp = fdopen(mypipe[0], "r");...
  1. popen 能像操作文件一样,打开一个指向进程的带缓冲的连接:
FILE* fp;fp = popen("ls", "r");fgets(buf, len, fp);    //将fp指向的流读入 bufpclose(fp);
  1. 管道在一个进程中被创建,通过fork来实现共享。所以,管道只能链接一个主机上的相关进程。 socket 可以在不同主机进程之间创建连接。

socket 相关接口:

服务器端:

  1. sockid = socket(int domain, int type, int protocol)
参数 说明 domain 通信域如:PF_INET 用于 互联网socket type socket类型 protocol 内核中代码使用的协议(非网络协议) 返回值 -1, 0

2. reslult = bind(int sockid, struct sockaddr *addrp, socklen_t addrlen)

参数 说明 sockid socket的id addrp 指向包含地址结构的指针 addrlen 地址长度 返回值 -1, 0

其中 sockaddr定义如下:

struct sockaddr {    sa_family_t sa_family;    /* address family, AF_xxx */    char        sa_data[14];    /* 14 bytes of protocol address */}

bind 用来将一个地址分配给 socket ,在这之前应该先初始化 sockaddr的成员。

  1. result = listen(int sockid, int qsize)
参数 说明 qsize 允许介入连接的数目

listen请求内核允许制定的 socket 接受接入呼叫

  1. fd = accept(int sockid, struct sockaddr *callerid, socklen_t *addrlenp)
参数 说明 callerid 指向呼叫者地址结构的指针 addrlenp 指向呼叫者地质结构长度的指针 返回 -1, fd (用于读写的文件描述符)

accept 阻塞当前进程,一直到制定的 socket 上的接入连接被建立起来,然后 accept 将返回文件描述符,次文件描述符链接到呼叫进程的某个文件描述符。

  1. result = connect(int sockid, struct sockaddr *serv_addrp,
    socklen_t addrlen)
参数 说明 serv_addrp 指向服务器地址结构的指针 sickid 用于建立连接的 socket addrlen 结构长度 result -1, 0

connectsockid 表示的socket和由 serv_addrp所指向的socket地址相连接
connect 用于客户端建立链接

  1. sendto 从socket 发送消息

nchars = sendto (int socket, const void *msg, size_t len, int flags, const struct sockaddr *dest, socklen_t dest_len)

参数 说明 flags 比特的集合,设置发送属性,0表示普通 dest 指向远端的socket地址的指针 dest_len 地址长度 nchars 发送的字节数

sendto 用于UDP链接, TCP使用send函数

  1. nchars = recvfrom(int socket, const void *msg, size_t len,
    int flags, const struct sockaddr *sender, socklen_t *sender_len)

从socket接收消息

参数 说明 flags 表示接收属性的比特集合,0表示普通 sender 指向远端的socket的地址的指针

recvfrom用于UDP链接 TCP使用 recv

  1. `int getpeername(int sockfd, struct sockaddr* addr, socklen_t *addrlen);`
    获得远端的sockaddr

  2. int getsockname(int sockfd, struct sockaddr* addr, socklen_t *addlen);
    得到自身的sockaddr

多线程相关函数

  1. 创建一个新的线程
    int pthread_create(pthread_t *thread, pthread_attr_t *attr,
    void *(*func)(void *), void *arg)
参数 说明 thread 指向pthread_t 类型变量的指针 attr 指向 pthread_attr_t 类型变量的指针,或者为NULL func 指向新线程所运行函数的指针 arg 传递给func的参数 返回 0, errcod

2. int pthread_join(pthread_t thread, void * *retval)
等待某线程终止

参数 说明 retval 指向某线程返回值的变量

3. 互斥锁:
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)

  1. 线程间使用条件变量通信:
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
    int pthread_cond_signal(pthread_cond_t *cond)
cond mutex

示例代码:

/* * twordcount3.c * 多线程统计文件字符数量 *  Created on: 2017年4月9日 *      Author: yang * 通过使用信号量实现线程和主进程的通信 */#include <stdlib.h>#include <stdio.h>#include <pthread.h>#include <ctype.h>struct arg_set{    char *fname;    int count;};void *count_words(void *a);struct arg_set *mailbox;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t  flag = PTHREAD_COND_INITIALIZER;int main(int argc, char **argv) {    pthread_t   t1,t2;    struct arg_set args1, args2;    int     reports_in = 0;    int     total_words = 0;    if (argc != 3)    {        printf("usage: %s file1 file2", argv[0]);        exit(1);    }    pthread_mutex_lock(&lock);  //对信号箱加锁    args1.fname = argv[1];    args1.count = 0;    pthread_create(&t1, NULL, count_words, (void*)& args1);    args2.count = 0;    args2.fname = argv[2];    pthread_create(&t2, NULL, count_words, (void *) &args2);    while (reports_in < 2) {        printf("MAIN: waiting for flag to go up\n" );        pthread_cond_wait(&flag, &lock);    //等待线程信号        printf("MAIN: flag was raised\n");        printf("%7d: %s\n", mailbox->count, mailbox->fname);        total_words += mailbox->count;        if (mailbox == &args1)            pthread_join(t1, NULL); //收回线程        if (mailbox == &args2);            pthread_join(t2, NULL);        mailbox = NULL;        pthread_cond_signal(&flag);     //发送消息 通知线程 mailbox 为空 可用        reports_in++;    }    printf("%7d: total words\n", args1.count + args2.count);}void *count_words(void *a){    struct arg_set *args = (struct arg_set *)a;    FILE *fp;    int c, prevc = '\0';    if ((fp = fopen(args->fname, "r")) != NULL)    {        while( (c = getc(fp)) != EOF)        {            if (!isalnum(c) && isalnum(prevc))            {                args->count++;            }            prevc = c;        }        fclose(fp);    }    else        perror(args->fname);    printf("COUNT: waiting to get lock\n" );    pthread_mutex_lock(&lock);  //得到 mailbox 锁的控制权    printf("COUNT: have lock, storing data\n" );    if (mailbox != NULL)        pthread_cond_wait(&flag, &lock);    //如果 mailbox 不为空 则等待    mailbox = args;    printf("COUNT: raising flag\n");    pthread_cond_signal(&flag);     //向主线程发送信号 表明数据存储完毕    printf("COUNT: unlocking box\n");    pthread_mutex_unlock(&lock);    return NULL;}
0 0