[英]Using fork and waitpid functions in C to count characters, words, and lines from file with multiple processes
[英]Count lines of each text file in a folder parallel using fork() in C
我正在尝试完成作业的代码。 任务是创建一个程序,该程序计算代码以并行方式运行的文件夹中每个文本文件的行数。 所以这是我的代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include "error.c"
char *getFileNameExtension(char *);
int isTxtFile(struct dirent *);
int countLines(struct dirent *);
int main(int argc , char *argv[]){
//Current Directory
DIR *currentDir;
//Current File
struct dirent *currDirFile;
pid_t pid;
int counter = 0;
/*
Struct stat "buf" variable , we'll need it
to check the selected file's st_mode(file type)
*/
struct stat buf;
int *lines = malloc(sizeof(int));
//Opens the directory where the executable exists with the dot(.)
currentDir = opendir(".");
if (currentDir){
/*In the while loop we read each file
of the current directory until we get a Nullable value */
while( ( currDirFile = readdir(currentDir) ) != NULL ){
if ( isTxtFile(currDirFile) == 1 ){
if ((pid=fork())<0){
err_sys("fork error");
}
//Child
else if(pid == 0){
exit(0);
}
//Father
else{
lines = realloc( lines , sizeof(int)*(counter+1) );
lines[counter] = countLines(currDirFile);
++counter;
}
}
if (pid==0){
printf("Child , pid = %d\n",pid);
}
else printf ("Father , pid = %d\n",pid);
}//End while
//close(currentDir);
}
int i ;
for(i=0;i<counter;++i){
printf("%d\n",lines[i]);
}
exit(1);
}
char *getFileNameExtension(char *filename){
//Gets the memory location of the last dot(.)
char *ext = strrchr(filename, '.');
return ext;
}
int isTxtFile(struct dirent *currDirFile){
struct stat buf;
/*With the lstat function we try to pass
the current file into the buf variable*/
if (lstat(currDirFile->d_name,&buf)<0){
printf("lstat error");
return 0;
}
/*If the st_mode(type) of the current file is regular
we print the name of the file*/
if (S_ISREG(buf.st_mode)){
char *c = getFileNameExtension(currDirFile->d_name);
if (c!=NULL){
//Check only for .txt files
if (strcmp(c,".txt")==0){
return 1;
}
else{
return 0;
}//End strcmp
}//End c!=NULL
}
else{
return 0;
}//END S_ISREG
}
int countLines(struct dirent *currentFile){
int fd = open(currentFile->d_name,O_RDONLY);
int n;
int lines = 0;
char buf;
while ((n=read(fd,&buf,1))>0){
if (buf=='\n') ++lines;
}
return (lines);
}
我以这种方式使用了fork函数,但是我不确定它是否正确,因为当我运行它时,有4个父进程正在运行(正如我预期的那样),但还有3个子进程正在运行。 有人可以帮我吗? 实际上,我正在尝试为每个文本文件创建一个进程。
最初的问题是您的父母/孩子颠倒了。 从fork
返回时,子代的pid == 0
,父代将返回子代的pid(即,非零的正数)。 您当前正在分叉并立即退出孩子,然后打印出错误的信息。 当您执行printf
,pid的值将是子代(在fork
返回)的值,而不是其自己的pid。 如果您想要父母的pid,请使用getpid
。 当然,孩子的printf将永远不会打印,因为它已经退出并且不再存在。
您的另一个大问题是,您需要牢记,孩子得到的地址空间与父母分开。 虽然孩子是最初的父母两个分歧随着时间的推移和孩子所做的更改的副本都没有见过父母。
最后,指望并等待您的孩子。 这不仅是一种好习惯,而且还可以防止您的父母在所有孩子都这样做之前就退出。
编辑:问题1
这是您的第一个问题,请参见代码中带编号的注释
while ( ( currDirFile = readdir(currentDir) ) != NULL )
{
if ( isTxtFile(currDirFile) == 1 )
{
if ((pid = fork()) < 0)
{
err_sys("fork error");
}
else
if (pid == 0) //(1) This is the child; it immediately dies
{
exit(0);
}
else //(2) Parent - it does ALL the work
{
lines = realloc( lines , sizeof(int) * (counter + 1) );
lines[counter] = countLines(currDirFile);
++counter;
}
}
if (pid == 0) //(3) This is reversed again which is why you think
{ // you have multiple "fathers" when they are really children
printf("Child , pid = %d\n", pid);
}
else
printf ("Father , pid = %d\n", pid);
} //End while
输出:
Father , pid = 17881
Father , pid = 17882
Father , pid = 17883
Father , pid = 17884
[nearly identical lines skipped]
114
14
7
3
128
12
因此,使此正确,我们有:
printf ("Father pid = %d\n", getpid()); //(2b)
if (currentDir)
{
while ( ( currDirFile = readdir(currentDir) ) != NULL )
{
if ( isTxtFile(currDirFile) == 1 )
{
if ((pid = fork()) < 0)
err_sys("fork error");
if (pid == 0)
{
//printf("Child pid = %d\n", pid); // (1)
printf("Child pid = %d\n", getpid());
lines = realloc( lines , sizeof(int) * (counter + 1) );
lines[counter] = countLines(currDirFile);
++counter;
exit(0);
}
}
//printf ("Father pid = %d\n", pid); //(2a)
} //End while
//close(currentDir);
}
(1)我们不会在子项中打印pid
的值,因为它将始终为零。 而是通过getpid()
获得孩子的真实pid值。
(2)将父printf
保留在循环(2a)中将使它每次通过循环都进行打印,而不管它是否是文本文件,这很烦人。 因此,我们将其移动到循环(2b)之前,但是由于它在派生之前,所以pid
中将没有有效值,因此我们也使用getpid()
来获取该值。
所以代替
Father pid = 18136
Father pid = 18136
Father pid = 18136
[many identical lines]
Child pid = 0
Child pid = 0
Child pid = 0
我们有所需的输出
Father pid = 18224
Child pid = 18225
Child pid = 18239
Child pid = 18240
[etc...]
旁注,出于编译目的,我假设err_sys
是这样的
void err_sys(const char *str)
{
perror(str);
exit(1);
}
问题2
现在,您没有文件中行数的输出。
if (pid == 0)
{
printf("Child pid = %d\n", getpid());
lines = realloc( lines , sizeof(int) * (counter + 1) );
lines[counter] = countLines(currDirFile);
++counter; //(1)
exit(0);
}
for (i = 0;i < counter;++i) //(2)
{
printf("%d\n", lines[i]);
}
因为父级和子级具有单独的内存,所以(1) counter
的增量永远不会在父级中看到。 常量(2)将被跳过,因为它为零且没有任何输出。
因此有两个选择:(1)从子级打印或(2)设置某种IPC机制,以便子级将其结果报告给父级,以便可以打印它们。 我将继续进行,好像选项1是正确的做法。
我们认识到其中的一些是无用的,并通过(1)摆脱数组-每个孩子只有1个line值-和(2)将printf
移到可以起到一定作用的位置进行调整。
//int *lines = malloc(sizeof(int));
int lines = 0;
//........
if (pid == 0)
{
printf("Child pid = %d\n", getpid());
//lines = realloc( lines , sizeof(int) * (counter + 1) );
lines = countLines(currDirFile);
//++counter;
printf("%d\n",lines);
exit(0);
}
如果输出难看,这将导致正确的输出:
Father pid = 18513
Child pid = 18514
114
Child pid = 18526
Child pid = 18527
4
7
所以整理一下代码并修饰输出,我们得到了
if (pid == 0)
{
lines = countLines(currDirFile);
printf("Child [%d] of parent [%d]: %s = %d lines\n",
getpid(), getppid(), currDirFile->d_name, lines);
exit(0);
}
我们看到输出结果
Child [18566] of parent [18565]: output.txt = 114 lines
Child [18569] of parent [18565]: list.txt = 3 lines
Child [18574] of parent [18565]: bfile.txt = 9 lines
Child [18579] of parent [18565]: nums.txt = 4 lines
Child [18581] of parent [18565]: afile.txt = 9 lines
Child [18571] of parent [1]: output.txt = 12 lines
Child [18575] of parent [1]: cfile.txt = 18 lines
Child [18572] of parent [1]: alphabet.txt = 3 lines
没关系,但是在printf
中使用getppid
告诉我们,父级在某些子级之前结束,并且在成为孤儿之后被init process
(pid = 1)继承。 所以....
问题3
我们需要防止父母在孩子面前结束比赛的情况,并防止孩子丧尸。 这就是wait
和waitpid
调用的目的。 这里的难题是,如果我们将wait
放置在循环中,则父母将阻止并等待每个孩子,导致一个孩子同时奔跑。
while ( ( currDirFile = readdir(currentDir) ) != NULL )
{
if ( isTxtFile(currDirFile) == 1 )
{
if ((pid = fork()) < 0)
err_sys("fork error");
if (pid == 0)
{
lines = countLines(currDirFile);
printf("Child [%d] of parent [%d]: %s = %d lines\n",
getpid(), getppid(), currDirFile->d_name, lines);
exit(0);
}
// ** this is the parent **
if (wait(NULL) == -1)
err_sys("wait");
}
} //End while
Child [18656] of parent [18655]: out.txt = 114 lines
Child [18657] of parent [18655]: input.txt = 7 lines
Child [18658] of parent [18655]: list.txt = 3 lines
Child [18659] of parent [18655]: output.txt = 12 lines
由于需要并行处理,因此一种方法是将wait
移到循环外,并在创建子进程时对子进程进行计数,以便以后可以收获它们。
int main(int argc , char *argv[])
{
DIR *currentDir;
struct dirent *currDirFile;
pid_t pid;
int counter = 0;
struct stat buf;
int lines = 0;
int numChildren = 0;
currentDir = opendir(".");
printf ("Father pid = %d\n", getpid());
if (currentDir)
{
while ( ( currDirFile = readdir(currentDir) ) != NULL )
{
if ( isTxtFile(currDirFile) == 1 )
{
if ((pid = fork()) < 0)
err_sys("fork error");
if (pid == 0)
{
lines = countLines(currDirFile);
printf("Child [%d] of parent [%d]: %s = %d lines\n",
getpid(), getppid(), currDirFile->d_name, lines);
exit(0);
}
// ** this is the parent **
numChildren++;
}
} //End while
closedir(currentDir); // <-- note we use closedir not close for directories
}
int i;
for (i = 0; i < numChildren; ++i)
{
if ((pid = waitpid(-1, NULL, 0)) == -1)
err_sys("fork error");
else
printf("parent [%d] reaped child [%d]\n", getpid(), pid);
}
exit(0);
}
导致类似
Father pid = 18737
Child [18738] of parent [18737]: out.txt = 114 lines
Child [18741] of parent [18737]: list.txt = 3 lines
Child [18743] of parent [18737]: output.txt = 12 lines
Child [18747] of parent [18737]: cfile.txt = 18 lines
parent [18737] reaped child [18738]
parent [18737] reaped child [18741]
parent [18737] reaped child [18743]
Child [18754] of parent [18737]: append.txt = 6 lines
parent [18737] reaped child [18747]
parent [18737] reaped child [18754]
这很丑陋但正确。 由于大部分输出仅用于演示,因此您可以对其进行改进。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.