#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
void* copyFile(void* arg) {
char *filename = ((char*)arg);
char *destname = strcat(filename, "copy");
int in = 0;
in = open(filename, O_RDONLY);
int out = 0;
out = open(destname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
struct stat statbuf;
fstat(in, &statbuf);
off_t insize = statbuf.st_size;
lseek(out, insize - 1, SEEK_SET);
ssize_t wrote = write(out, "", 1);
char* inmap = mmap(0, insize, PROT_READ, MAP_FILE | MAP_PRIVATE, in, 0);
char* outmap = mmap(0, insize, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, out, 0);
memcpy(outmap, inmap, insize);
close(out);
}
int main(int argc, char *argv[]) {
DIR* d;
struct dirent* e;
d = opendir(argv[1]);
int num_entries = 0;
int i = 0;
pthread_t threads[50];
while((e = readdir(d)) != NULL) {
char *filename = e->d_name;
printf("%s\n", filename);
pthread_create(&threads[i], NULL, copyFile, filename);
i ;
}
for(int j = 0; j < i; j ) {
pthread_join(threads[j], NULL);
}
}
您好,我正在嘗試使用 mmap 和 memcpy 來使用多執行緒復制整個檔案目錄。我有復制目錄中檔案的代碼,但是它強制所有復制的檔案大小為 1 位元組。我很好奇為什么會發生這種情況以及可以采取哪些措施來解決此錯誤。
我是 C 的新手,所以請原諒我明顯的語意錯誤。我感謝您的幫助!
uj5u.com熱心網友回復:
有許多錯誤。
在
main,filename將指向所有檔案的相同地址。我們必須提供唯一的地址/副本。一種方法是使用strdup. 并且,我們應該free在 的底部添加一個呼叫copyFile來釋放strdup.復制的時候一定要跳過
.和..。否則,代碼段錯誤。對輸入檔案和副本使用相同的目錄可能會導致無限回圈。也就是說,給定一個檔案
xyz被拷貝到xyzcopy時,readdir也許能夠看到xyzcopy作為輸入,并嘗試建立xyzcopycopy。最好使用單獨的輸出目錄。這將需要進行重大重寫。但是,一個更簡單的解決方法是跳過任何包含copy.該程式將[嘗試]復制目錄條目,即使它們不是普通檔案(例如,它會嘗試
open在目錄上執行)。我們應該檢查檔案型別并跳過非檔案型別。In
copyFile,即使有strdupinmain,字串filename也沒有足夠的空間來容納strcatof"copy"。我們需要一個單獨的緩沖區作為輸出檔案名。在
lseek/write輸出檔案的末尾添加一個多余的二進制零。這是為了擴展輸出檔案。更好地ftruncate用于那個。那是因為
write與混合mmap可能會出現問題,并且通常不是最佳實踐。在您的用例中,這可能沒問題(因為write是在之前完成的mmap),但是如果我們不厭其煩地使用mmap,最好只使用mmap緩沖區指標 和memcpy。copyFile缺少一個return. 而且,main還缺少一個return沒有
closeforinincopyFile。open,mmap,opendir呼叫沒有錯誤檢查。munmap電話之前應該有close電話。
未修復的錯誤:
該程式限制目錄可以包含的條目數。
隨著
pthread_t threads[50];我們只能在目錄中有50個條目。有沒有檢查該陣列的溢位。它使沒有實際意義做所有并行檔案。這沒有規模。
如果我們在目錄中有(例如)1,000,000 個條目,我們可能會耗盡資源。我們可能會用完未使用的行程槽,并
pthread_create在達到系統范圍的限制后開始失敗。我們可能會用完檔案描述符,并且在open打開大約 1024 個檔案后呼叫將開始失敗。在創建了一定數量的執行緒后(例如,實際上是 4-5 個),復制程序實際上會變慢,因為系統將花費大部分時間在執行緒之間切換而不是做有用的作業。
對于小檔案,執行緒創建/拆卸的開銷相對于執行緒的運行時間變得很重要。
最好有一個有限的“作業”執行緒“池”,并在pthread_create呼叫中穿插pthread_join呼叫,在執行緒完成時回收/重用現在未使用/空閑的“插槽”。
更有效的方法是創建執行緒池(通過在開始時pthread_create呼叫一次。每個執行緒都有一個郵箱/作業佇列要做。main可以將條目排入執行緒,當它看到一個執行緒時添加一個新條目空閑。pthread_join呼叫可以在最后完成一次
下面是更正后的代碼。
我使用前處理器條件來表示舊代碼與新代碼(例如):
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
無論如何,這是更正后的代碼。我已經用評論注釋了錯誤/修復:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
void *
copyFile(void *arg)
{
// NOTE/BUG: no cast is needed from a void *
#if 0
char *filename = ((char *) arg);
#else
char *filename = arg;
#endif
// NOTE/BUG: there is _not_ enough space in filename to add "copy" -- this is
// UB (undefined behavior)
#if 0
char *destname = strcat(filename, "copy");
#else
char destname[1024];
strcpy(destname,filename);
strcat(destname,"copy");
#endif
int in = 0;
in = open(filename, O_RDONLY);
// NOTE/FIX: check for error
#if 1
if (in < 0) {
perror(filename);
exit(1);
}
#endif
int out = 0;
out = open(destname, O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
// NOTE/FIX: check for error
#if 1
if (out < 0) {
perror(destname);
exit(1);
}
#endif
struct stat statbuf;
fstat(in, &statbuf);
off_t insize = statbuf.st_size;
// NOTE/BUG: this will add a binary zero at the end of the output file
#if 0
lseek(out, insize - 1, SEEK_SET);
ssize_t wrote = write(out, "", 1);
#else
ftruncate(out,insize);
#endif
char *inmap = mmap(0, insize, PROT_READ, MAP_FILE | MAP_PRIVATE, in, 0);
// NOTE/FIX: check for error
#if 1
if (inmap == MAP_FAILED) {
perror("mmap/in");
exit(1);
}
#endif
char *outmap = mmap(0, insize, PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, out, 0);
// NOTE/FIX: check for error
#if 1
if (inmap == MAP_FAILED) {
perror("mmap/in");
exit(1);
}
#endif
memcpy(outmap, inmap, insize);
// NOTE/FIX: we should unamp the output area
#if 1
munmap(outmap,insize);
#endif
close(out);
// NOTE/FIX: we should unmap and close the input descriptor
#if 1
munmap(inmap,insize);
close(in);
#endif
// NOTE/FIX: free the string allocated by strdup in main
#if 1
free(filename);
#endif
// NOTE/FIX: needs return value -- would be flagged by compiler if compiled
// with -Wall
#if 1
return (void *) 0;
#endif
}
int
main(int argc, char *argv[])
{
DIR *d;
struct dirent *e;
// NOTE/BUG: no checks for missing argument or bad directory
#if 0
d = opendir(argv[1]);
#else
if (argv[1] == NULL) {
fprintf(stderr,"missing argument\n");
exit(1);
}
d = opendir(argv[1]);
if (d == NULL) {
perror(argv[1]);
exit(1);
}
#endif
// NOTE/BUG: unused variable
#if 0
int num_entries = 0;
#endif
int i = 0;
pthread_t threads[50];
while ((e = readdir(d)) != NULL) {
// NOTE/FIX: must skip "." and ".."
#if 1
if (strcmp(e->d_name,".") == 0)
continue;
if (strcmp(e->d_name,"..") == 0)
continue;
#endif
// NOTE/FIX: only copy simple files (i.e. do _not_ copy subdirectories, etc.)
#if 1
if (e->d_type != DT_REG)
continue;
#endif
// NOTE/FIX: skip "copy" files
#if 1
if (strstr(e->d_name,"copy") != NULL)
continue;
#endif
// NOTE/BUG: this will present the _same_ memory address to all threads so there
// is a "race condition"
#if 0
char *filename = e->d_name;
#else
char *filename = strdup(e->d_name);
#endif
printf("%s\n", filename);
pthread_create(&threads[i], NULL, copyFile, filename);
i ;
}
for (int j = 0; j < i; j ) {
pthread_join(threads[j], NULL);
}
// NOTE/FIX: needs return
#if 1
return 0;
#endif
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/349076.html
