我正在尝试学习多线程和多进程编程。我对多线程/处理编程和Ubuntu环境都很陌生。我在下面的代码上工作了10个小时并修复了所有错误和警告。我开始使用xCode对其进行编码,它运行得很完美,并且正是我想要它做的,没有任何警告或错误。但是当尝试在Ubuntu上编译并运行时,我得到了一个分段错误(核心转储)我无法理解导致此错误的代码的哪一部分。关于哪个部分可能导致错误的任何想法?或者为什么我这样做?我记得Linux没有核心吗?非常感谢你!
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
int pid, i, rc, pid1, counter;
char* iterator[500];
char* file[500];
enum {
WALK_OK = 0,
WALK_BADPATTERN,
WALK_BADOPEN,
};
int walker(const char *dir, const char *pattern)
{
struct dirent *entry;
regex_t reg;
DIR *d;
counter=0;
if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB))
return WALK_BADPATTERN;
if (!(d = opendir(dir)))
return WALK_BADOPEN;
while ((entry = (readdir(d))) ){
if (!regexec(®, entry->d_name, 0, NULL, 0)){
puts(entry->d_name);
file[counter]=entry->d_name;
counter=counter+1;}
}
closedir(d);
regfree(®);
return counter;
}
void* project_statistics(int i){
FILE* f;
// size_t len;
char* line;
int read[3];
int arr[1000];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",&p, &m, &fnl);
arr[counter2]= p;
counter2++;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k++) {
l = k;
while (l > 0 && firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %d\n", i+1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
void* midterm_statistics(int i){
FILE* f;
int read[3];
char* line;
int arr2[1000];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",&p, &m, &fnl);
arr2[counter2]=m;
counter2++;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr2, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k++) {
l = k;
while (l > 0 && firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %d\n", i+1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
void* final_statistics(int i){
FILE* f;
char* line;
int arr3[1000];
int read[3];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",&p, &m, &fnl);
arr3[counter2]=fnl;
counter2++;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr3, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k++) {
l = k;
while (l > 0 && firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %d\n", i+1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
int main(int argc, const char * argv[]) {
char k[500];
int counter1=walker("/home/ey/Desktop/sampleFolder/", ".\\.txt");
for (i=0; i<counter1; i++) {
strcpy(k, "/home/ey/Desktop/sampleFolder/");
strcat(k, file[i]);
iterator[i]=strdup(k);
printf("%s",iterator[i]);
}
printf("\nMaster is starting\n");
pthread_t tid1[counter1], tid2[counter1], tid3[counter1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
printf("\nslave1 start\n");
printf("\n~Project Statistics~\n");
sleep(2);
for (i=0; i<counter1; i++) {
rc=pthread_create(&tid1[i], &attr, (void*)*project_statistics,(void*)(intptr_t)i);
}
sleep(2);
printf("\nslave1 done\n");
printf("\nslave2 start\n");
printf("\n~Midterm Statistics~\n");
pid=fork();
sleep(2);
if (pid==0) {
for (i=0; i<counter1; i++) {
rc=pthread_create(&tid2[i], &attr,(void*)*midterm_statistics, (void*)(intptr_t)i);
}
sleep(2);
printf("\nslave2 done\n");
printf("\nslave3 start\n");
printf("\n~Final Statistics~\n");
}
sleep(2);
pid1=fork();
sleep(2);
if ((pid1==0)&&(pid==0)) {
for (i=0; i<counter1; i++) {
rc=pthread_create(&tid3[i], &attr, (void*)*final_statistics, (void*)(intptr_t)i);
}
sleep(2);
printf("\nslave3 done\n");
printf("\nMaster is done\n");
}
sleep(1);
pthread_attr_destroy(&attr);
pthread_exit(NULL);
}
答案 0 :(得分:1)
在main
中,您的strcat
发生了错误。
源地址为file[i]
。 file
是char *
指针的全局数组。但是,它[显然]从未初始化为任何东西。
因此strcat
调用将有NULL
的第二个参数,这会导致段错误。
如果walker
返回非零值,则可能会发生这种情况,如果目录不存在(即返回为WALK_BADOPEN
),则会发生这种情况。这可以解释为什么它在一个系统上工作而在另一个系统上工作(即目录存在于一个系统而不存在于另一个系统上)。
因此,walker
正在使用返回错误代码,但main
正在使用此返回值作为计数。这个逻辑不正确。我相信您需要更改walker
的返回值或让main
以不同的方式获取计数。
解决此问题的简单方法是使错误代码为负值,并main
进行检查。然后,walker
可以正确返回计数。
因此,如果目录不存在,则返回值为2. main
中的循环将在file[0]
上出错,因为file
中的任何内容都未设置为任何内容。
<强>更新强>
但是这次因为我知道目录确实存在,我可以尝试以错误的方式打开它吗?
没有&#34;错误&#34;使用opendir
的方式 - 它已打开或失败,您已经处理过。
但是,在walker
内,您不能依赖循环迭代到迭代的d_name
值,因此,您必须使用strdup
。
变化:
file[counter] = entry->d_name;
分为:
file[counter] = strdup(entry->d_name);
此外,您应该限制检查file
的最大值(例如,目前只有500)
更新#2:
在你的线程函数中,由于libc的fgets
函数,你正在read
进入read
[而不是这是一个不错的选择]。但是,它是:
int read[3];
因此,行缓冲区仅 12个字节长。这可能会导致fgets
读取一行两个部分拆分行。此可能导致arr
数组溢出
我将其更改为:
char buf[1000];
我将线程函数的复制代码组合到一个公共代码中。
请注意firstHalf
已分配,但从未释放。所以,这是&#34;泄漏&#34;。我为它添加了free
电话。
另请注意,没有fclose(f)
可能会导致fopen
返回NULL
(即段错误的其他来源)。
我还重做了线程连接和fork
逻辑,并添加了waitpid
。另请注意在fork的子代码中添加exit(0)
。
当我试图理解事物时,我正在简化事情,所以以下是一个公平的返工,可能看起来有点&#34;外星人&#34;起初[请原谅无偿的风格清理]:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#if 1
#define MYDIR "/home/ey/Desktop/sampleFolder/"
#else
#define MYDIR "/tmp/data/"
#endif
#define dbgprt(_fmt...) \
do { \
if (opt_dbg) \
printf(_fmt); \
} while (0)
int opt_dbg;
int pid;
int i;
int rc;
int pid1;
int counter;
char *iterator[500];
char *file[500];
enum {
WALK_OK = 0,
WALK_BADPATTERN = -1,
WALK_BADOPEN = -2,
};
int
walker(const char *dir,const char *pattern)
{
struct dirent *entry;
regex_t reg;
DIR *d;
counter = 0;
if (regcomp(®,pattern,REG_EXTENDED | REG_NOSUB))
return WALK_BADPATTERN;
d = opendir(dir);
if (d == NULL)
return WALK_BADOPEN;
while (1) {
entry = readdir(d);
if (entry == NULL)
break;
if (!regexec(®,entry->d_name,0,NULL,0)) {
puts(entry->d_name);
file[counter] = strdup(entry->d_name);
counter = counter + 1;
}
}
closedir(d);
regfree(®);
return counter;
}
void *
thread_common(void *arg,int column)
{
intptr_t i = (intptr_t) arg;
FILE *f;
// size_t len;
char *line;
int data[3];
char buf[1000];
int arr[1000];
int counter2 = 0;
f = fopen(iterator[i],"r");
if (f == NULL) {
err(1,"%s",iterator[i]);
}
dbgprt("DEBUG reading ...\n");
while (1) {
line = fgets(buf,sizeof(buf),f);
if (line == NULL)
break;
sscanf(line,"%d %d %d",&data[0],&data[1],&data[2]);
arr[counter2] = data[column];
counter2++;
dbgprt("DEBUG line %d %s\n",counter2,iterator[i]);
if (counter2 >= 1000) {
printf("overflow %s\n",iterator[i]);
exit(1);
}
}
if (!feof(f)) {
err(1,"getIn");
}
fclose(f);
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf,arr,(counter2) * sizeof(int));
// sort array;
int k,
l,
tmp;
dbgprt("DEBUG sorting ...\n");
for (k = 1; k < counter2; k++) {
for (l = k; (l > 0) && (firstHalf[l - 1] > firstHalf[l]); l--) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l - 1];
firstHalf[l - 1] = tmp;
l--;
}
}
printf("course %ld project median: %d, project min: %d, project max: %d\n",
i + 1,firstHalf[counter2 / 2],firstHalf[0],firstHalf[counter2 - 1]);
free(firstHalf);
return (void *) 0;
}
void *
project_statistics(void *arg)
{
return thread_common(arg,0);
}
void *
midterm_statistics(void *arg)
{
return thread_common(arg,1);
}
void *
final_statistics(void *arg)
{
return thread_common(arg,2);
}
int
main(int argc,char **argv)
{
intptr_t i;
char *cp;
char krkt[500];
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'd':
opt_dbg = 1;
break;
default:
break;
}
}
int counter1 = walker(MYDIR,".\\.txt");
dbgprt("main: walker returned %d\n",counter1);
if (counter1 <= 0)
exit(1);
for (i = 0; i < counter1; i++) {
strcpy(krkt,MYDIR);
if (file[i] == NULL)
exit(3);
strcat(krkt,file[i]);
iterator[i] = strdup(krkt);
printf("%s\n",iterator[i]);
}
printf("\nMaster is starting\n");
pthread_t tid1[counter1];
pthread_t tid2[counter1];
pthread_t tid3[counter1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
printf("\nslave1 start\n");
printf("\n~Project Statistics~\n");
//sleep(2);
for (i = 0; i < counter1; i++)
rc = pthread_create(&tid1[i],&attr,project_statistics,(void *) i);
for (i = 0; i < counter1; i++)
rc = pthread_join(tid1[i],NULL);
printf("\nslave1 done\n");
pid = fork();
if (pid == 0) {
printf("\nslave2 start\n");
printf("\n~Midterm Statistics~\n");
for (i = 0; i < counter1; i++)
rc = pthread_create(&tid2[i],&attr,midterm_statistics,(void *) i);
for (i = 0; i < counter1; i++)
rc = pthread_join(tid2[i],NULL);
printf("\nslave2 done\n");
exit(0);
}
pid1 = fork();
if (pid1 == 0) {
printf("\nslave3 start\n");
printf("\n~Final Statistics~\n");
for (i = 0; i < counter1; i++)
rc = pthread_create(&tid3[i],&attr,final_statistics,(void *) i);
for (i = 0; i < counter1; i++)
rc = pthread_join(tid3[i],NULL);
printf("\nslave3 done\n");
exit(0);
}
waitpid(pid,NULL,0);
waitpid(pid1,NULL,0);
printf("\nMaster is done\n");
pthread_attr_destroy(&attr);
return 0;
}
更新#3:
主要的开头对我来说也不是那么清楚,为什么我们要等待一个&#39; d&#39;并制作一个switch-case,为什么需要在代码中添加argv和argc?由于代码在某种程度上依赖于argv和argc,我的编译方式是否会导致问题?
argc/argv
代码只解析选项参数。这是非常标准的样板。
在这种情况下,如果您执行./main -d
,则会设置opt_d
。然后,dbgprt
宏对此进行测试,如果设置,则执行printf
。因此,与调试输出相关的所有printf
都更改为dbgprt
。
这样做不改变程序的执行,只需添加额外的调试输出。如果您愿意,可以添加更多dbgprt
。
并且,您可以通过将它们添加到switch/case
来添加自己的命令行选项。
这种技术用于&#34; printf调试&#34;很常见我更喜欢在可能的情况下使用gdb
。就个人而言,我尝试使用gdb
来调用该程序,只有当我有一个&#34;严重的&#34;错误,例如段错误。 gdb
我可以识别断层线。然后,我添加assert
,调试打印等内容,以预先缓解问题。
我有点理解逻辑,但我无法运行代码。我的意思是它仍在使用xcode。
我修复的错误也适用于xcode
版本。
但是在Linux中它没有给出任何错误或警告,但是当键入./main时我什么都没得到......
如果您在Linux上运行,请使用-d
。然后,在调用dbgprt
之后,请注意第一个walker
的输出。
我最好的猜测是walker
返回一个负值(即目录不存在 - 模式没问题,因此剩下的是什么)。或者,返回0表示目录没有文件,或者没有与模式匹配的文件。
该程序应以exit(1)
结束,因此请检查错误代码(例如echo $?
)
你可以 [并且在考虑之后,可能应该]将第一个dbgprt
更改回printf
,因此它始终打印即使你没有指定-d
,也是如此。这样,你就不会得到#34;沉默&#34;失败,但程序会事先告诉你,如果有什么不妥。
帮助调试此的一种方法是以使用gdb
。做gdb ./main
。然后,执行b walker
在walker
上设置断点,然后键入run
。 gdb
将在walker
的第一个语句处停止该程序。
然后,您可以将s
键入&#34;单步&#34;该程序。你可以继续重复这个。当您有提示时,您可以使用gdb的p
命令来打印变量。这样您就可以了解walker
的作用。
当一行调用libc
函数时,例如opendir
,readdir
,strdup
等,执行s
会尝试单步这些功能。冗长,并没有那么有用。因此,在这样一行上,请改用n
。如果您误操作s
,则可以输入finish
。
如果您认为自己已经走得够多,可以输入c
,这将继续全速执行程序。
gdb
有很多命令,上面只是几个。它具有内联帮助,因此请在提示符下键入help
。或者,help b
等。有许多教程可用。