LinuxAPP学习笔记---持续更新

来源:互联网 发布:ghost远控源码 编辑:程序博客网 时间:2024/05/21 17:20

1、APP对于底层设备的操作

APP对于底层设备的读写操作,最稳妥的方式是使用select模式读写,不建议使用在主循环中轮询的方式来读取数据。在主循环中对设备数据进行读写对轮询的时间间隔比较敏感,比如串口慢速设备,如果在轮询中没有延时,就会一直导致读不出数据,因为内核中驱动是使用信号量的方式监听数据,APP发起的读操作几乎没有时间间隔的话,会导致信号量永远没有机会返回。


2、对于TCP连接的维护。阻塞方式
   服务器端可以通过监听收到数据是否为0来实现。客户端也可以通过监听收到数据是否为0实现。
   但是区别是,客户端在连接服务器端之前,无法判断服务器端是否ok;服务器端在监听客户端之前,
   可以通过服务器的socket句柄来判断是否可以建立客户端监听。
  非阻塞方式,可以通过receive返回的错误码来判定是否断开

3、线程间和进程间通信方式简介

(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
(7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统
上:Linux和System V的变种都支持套接字。

(9)条件变量(Conditions):如果现在在等待一个信号。如果该信号被设置,则继续运行。如果没有条件变量,我们将会不停的去查询该信号是否被设置,这样就会浪费大量的cpu。而通过使用条件变量,我们就可以将等待信号的线程阻塞,直到有信号的时候再去唤醒它。条件变量最大的好处就是可以使等待条件的线程处于阻塞而不是忙等待状态。其实使用线程间全局变量共享的方法也可以达到线程间同步的目的,但是缺点就是需要忙等待,耗用CPU资源,并且为了支持多线程,也必须加互斥锁保护。

4、pthread_create()在使用的过程中遇到的一个问题:

 今天在使用线程的时候出现了一个问题,利用valgrind监测程序的运行时出现了memory leak。感觉到很奇怪,这样的情况应该不会发生的啊,毕竟是系统的函数。去网上搜了下,发现国内国外都有这个问题 呵呵。大概了解了下原因:创建的线程需要进行join或者detach状态,这样才能保证创建线程过程中分配的内存得以释放,才能避免memory leak的情况。因此,记得需要将线程进行join或者detach。

       线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。

有以下的方法来解决这个问题:

  1. 1、创建线程,使用默认的非分离状态,然后进行join

pthread_create(&threadid, NULL, func, NULL);

pthread_join(threadid, &state);

  • 2、创建线程时利用pthread_attr_setdeatchstate()指定线程的属性为PTHREAD_CREATE_DETACHED

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_create(&threadid, &attr, func, NULL);

pthread_attr_destroy(&attr);

    • 3、在默认创建完进程后,使用pthread_detach函数,使得线程处于detach状态;或者在func中调用pthread_detach函数,线程自行退出

    pthread_create(&threadid, NULL, func, NULL);

    pthread_detach(threadid);

    或者:

    pthread_create(&threadid, NULL, func, NULL);

    void* func(void*)

    {

    pthread_detach(pthread_self());

    return ((void*)0);

    }

5、关于多线程编程中,频繁调用system函数,会导致segment fault出错;经过实际测试发现可以通过使用管道的方式(popen,但是要记得pclose)执行sh脚本,并且不会导致调用进程异常:

linux下使用多线程的fork和system会出现问题

根据我的经验,linux下使用pthread库写多线程程序时,在调用系统调用/库函数方面,应注意至少如下几点:

1、创建了线程后,不要再使用fork()/vfork()创建子进程

2、尽量不使用signal机制

3、...

 关于1,有个具体的教训。我实现了一个动态库,该库的功能在一个独立线程里运行。同事A实现了另一个动态库,库的功能在也一个独立线程里运行。这两个库需要访问一设备,该设备只支持一个使用者访问,因此有个全局的信号量,来保证这两个线程能交替的访问该设备。我们的测试代码表明这两个库可以正常的配合工作。然后这两个库被交给同事B,集成到他的应用程序里。问题出现了,只要我和A的线程同时运行,几乎必然出现同时访问设备的错误情况,似乎用来保护的信号量失败了。多次检查库的代码,没有发现问题。偶然间,发现同事B的代码调用了system(),把system()调用替换成相应的代码实现,问题消失。原因在于system()实际上调用了fork()/vfork()。

为什么在多线程调用fork会出现问题呢?我分析是因为fork会把当前进程复制到子进程,子进程也是多线程。注意在子进程执行fork()-exec()这段时间,子进程的其他线程也在运行,同时信号量也被复制到子进程中。因为父子进程有独立的信号量来保护设备,这样父进程访问设备的时候,子进程也可以访问设备,于是出现问题。

  linux下创建子进程和多线程冲突的根本原因在于fork会复制当前进程,而Windows创建子进程就没有这个问题。为什么会这样呢?个人认为可能是历史原因造成的。早期的unix注意是作为服务器使用,例如web/ftp服务等。在没有多线程支持的年代,服务器支持多用户访问支持的方法就是为每个用户创建一个服务子进程。服务子进程通常就是服务器本身,因此提供一个fork调用,是可以提高效率的。但是当初设计fork调用的时候,并没有考虑到多线程,导致与多线程冲突。unix的历史负担很多,比如线程局部存储(TLS),个人认为就是为了解决早期程序设计者喜欢大量使用全局变量和多进程提出的。

 那如果确实需要在多线程程序里运行另一个程序(而且只有可执行文件没有源代码)怎么办呢?可以把要执行的程序包装一下,多线程的主程序通过进程间通信的方式包装后的子进程通信。

 与关于第2点多线程与signal机制的问题,网上很多,大家搜索一下就有了,不多说了

6、core dump使用

//设置生成core dump文件有pid后缀

echo "1" > /proc/sys/kernel/core_uses_pid
//设置core dump文件大小无限制
ulimit -c unlimited
//设置core dump文件生成目录
echo "/corefile/core-%e-%p-%t" >core_pattern

7、TCP编程经验-CLIENT

a、如果是客户端从来没有connect过server,那recv函数返回的是-1,如果曾经connect过server,但是server断开,那么recv返回的是0,并且该
   情况下如果不关闭socket并重新建立的话,connect函数会返回失败(无论server是否启动)。






8、TCP编程经验-SERVER





原创粉丝点击