Linux编程实践教程小结

编写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 SHOWHOST

void 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 int
compar(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_DUP

int 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指向的流读入 buf
pclose(fp);
  1. 管道在一个进程中被创建,通过fork来实现共享。所以,管道只能链接一个主机上的相关进程。 socket 可以在不同主机进程之间创建连接。

socket 相关接口:

服务器端:

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

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

参数说明
sockidsocket的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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值