程序运行中常见的错误原因及解决方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(1)pthread问题
照着GUN/Linux编程指南中的一个例子输入编译,结果出现如下错误:
undefined reference to 'pthread_create'
undefined reference to 'pthread_join'
问题原因:
pthread 库不是Linux 系统默认的库,连接时需要使用静态库libpthread.a,所以在使用pthread_create()创建线程,以及调用pthread_atfork()函数建立fork处理程序时,需要链接该库。
问题解决:
在编译中要加-lpthread参数
gcc thread.c -o thread -lpthread
thread.c为你些的源文件,不要忘了加上头文件#include
(2)段错误问题
a) P117页中atoi函数应该包括头文件#include
或者对代码行“pthread_create(&tid, &attr, runner, NULL);”前面判断int tmp;if((tmp=pthread_create(&tid, &attr, runner, NULL))!=0) printf("线程创建失败!\n");
(3) int main(int argc,char *argv[]) 参数问题
int main(int argc,char *argv[])是UNIX 和Linux 中的标准写法,而int main() 只是UNIX 及Linux 默许的用法..
argv[0] 就是你的执行文件名字
argv[1] 才是第一个参数
参数之间靠空格区分
* argc:整数,为传给main()的命令行参数个数。
* argv:字符串数组。在DOS 3.X版本中, argv[0]为程序运行的全路径名;对DOS 3.0 以下的版本, argv[0]为空串( " ")
4.fork()函数
#include
#include
main ()
{
pid_t pid;
pid=fork();
if (pid < 0)
printf("error in fork!");
else if (pid == 0)
printf("i am the child process, my process id is %d\n",getpid());
else
printf("i am the parent process, my process id is %d\n",getpid());
}
输出:i am the child process, my process id is 4286
i am the parent process, my process id is 4285
fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因。
至于那一个最先运行,可能与操作系统有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决
在fork前父进程的东西子进程可以继承,而在fork后子进程没有任何和父进程的继承关系了。在子进程里创建的东西是子进程的,在父进程创建的东西是父进程的。可以完全看成两个进程。
在程序段里用了fork();之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。
(5)父进程结束后,子进程一定结束吗?
子进程是不会随父进程结束的,子进程创建后有自己的进程地址空间,和父进程独立,父进程结束不会影响子进程
默认设置,新创建进程不会因为父进程自动结束.最明显就是explorer.exe,你登陆系统后启动的程序都是以explorer.exe为父进程的,你试下杀掉explorer.exe,你启动的程序还在的.
若父进程退出,子进程尚未结束,则子进程会变为孤儿进程,被init进程领养,也就是说init进程将成为该子进程的父进程。
(6)a)附录中的exec函数(Exec执行成功后将用一个新的程序代替原进程,但但进程号不变);
b)fork和exec
许多朋友对fork和exec调用概念比较模糊,下面我简单描述下这方面的知识。
学过C语言的都知道,Unix下某个进程的内存分成三部分:代码段,堆栈段,数据段。代码段用来存放程序运行的代码,堆栈段用来存放子程序的局部变量,数据段用来存放全局变量。当发生fork调用时,实际上发生如下事:
父进程将代码段,堆栈段,数据段完全复制一份给子进程。也就是说,在子进程运行之初,它拥有父进程的一切变量和句柄。例如,父进程申明了某个hash表,那这个hash表也会被子进程拥有。
然而,一旦子进程开始运行,它的数据段和堆栈段就在内存里完全和父进程分离开了。也就是说,两个进程间不再共享任何数据。例如前面所说的hash表,虽然子进程从父进程处继承了这个数据结构,但子进程写往hash里的数据,不会被父进程访问到。在shell里用ps命令,可以看到2个独立运行的进程。通常你kill掉1个,不会影响另1个的运行。
那么父进程和fork出来的子进程如何通信呢?父进程和子进程间的通信有多种方法,最常见的是信号,另外还有管道,Socket,消息队列等,不在这里详叙。而2个进程间共享数据的办法,可以用线程或共享内存。
如果大概明白了fork,那么exec就容易理解了。
一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。调用exec后,原进程就完全消失,由于消失了,它也就不会从新进程接受到任何返回值,除非新进程意外终止,原进程会接受到错误值。
(7)附录中两个程序