编写Linux who
命令
Linux文件操作函数:
- 打开一个文件
int fd = open(char *name, int how)
参数 how
代表文件的打开方式:
- O_RDONLY
, O_WRONLY
和 O_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)
获得
utmp
中 ut_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
}
编写
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
,然后切换到上一目录,得到iNode
为 n
的目录名,一直重复,直到根目录。
pwd
命令中涉及到的系统调用:
- 创建目录:
int result = mkdir(char *pathname, mode_t mode)
- 删除目录,此目录必须为空:
int result = rmdir(const char *path)
- 删除一个链接,即从一个目录文件中删除一个记录:
int result = unlink(const char* path)
- 创建一个文件的链接
int result = link(const char*orig, const char *new)
- 重命名或者删除一个链接
int result = rename(const char *from, const char* to)
- 切换当前工作目录:
int result = chdir(const char *path)
信号处理:
result = signal = (int signum, void(*action)(int))
signum
需响应的信号action
处理信号的函数
其中action
可以是信号处理函数,或者是 SIG_IGN
或 SIG_DFL
分别代表忽略信号或默认处理
时钟编程
设置发送信号的计时器:
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"); }
使用间隔计时器:
在间隔计时器设置的结构体中有两个参数,it_value
和it_interval
.分别表示初始时间和间隔时间.
通过使用getitimer
和settimer
可以获得和设置间隔计时器:
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);
}
进程和程序:
- 在指定的路径中查找并执行一个文件
result = execvp(const char *file, const char *argv[])
- 建立一个新进程
pid_t result = fork()
在父进程中返回值是子进程的pid
,在子进程中返回的是0. - 等待进程的结束:
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;
}
输入输出重定向
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;
}
- 管道
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
.
FILE* fdopen(int fd, char* mode)
能像操作文件一样操作管道:
...
char mypipe[2];
pipe(mypipe);
FILE *fp;
fp = fdopen(mypipe[0], "r");
...
popen
能像操作文件一样,打开一个指向进程的带缓冲的连接:
FILE* fp;
fp = popen("ls", "r");
fgets(buf, len, fp); //将fp指向的流读入 buf
pclose(fp);
- 管道在一个进程中被创建,通过
fork
来实现共享。所以,管道只能链接一个主机上的相关进程。socket
可以在不同主机进程之间创建连接。
socket
相关接口:
服务器端:
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
的成员。
result = listen(int sockid, int qsize)
参数 | 说明 |
---|---|
qsize | 允许介入连接的数目 |
listen
请求内核允许制定的 socket
接受接入呼叫
fd = accept(int sockid, struct sockaddr *callerid, socklen_t *addrlenp)
参数 | 说明 |
---|---|
callerid | 指向呼叫者地址结构的指针 |
addrlenp | 指向呼叫者地质结构长度的指针 |
返回 | -1, fd (用于读写的文件描述符) |
accept
阻塞当前进程,一直到制定的 socket
上的接入连接被建立起来,然后 accept
将返回文件描述符,次文件描述符链接到呼叫进程的某个文件描述符。
result = connect(int sockid, struct sockaddr *serv_addrp,
socklen_t addrlen)
参数 | 说明 |
---|---|
serv_addrp | 指向服务器地址结构的指针 |
sickid | 用于建立连接的 socket |
addrlen | 结构长度 |
result | -1, 0 |
connect
把 sockid
表示的socket
和由 serv_addrp
所指向的socket
地址相连接
connect
用于客户端建立链接
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
函数
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
`int getpeername(int sockfd, struct sockaddr* addr, socklen_t *addrlen);`
获得远端的sockaddrint getsockname(int sockfd, struct sockaddr* addr, socklen_t *addlen);
得到自身的sockaddr
多线程相关函数
- 创建一个新的线程
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)
- 线程间使用条件变量通信:
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;
}