Category Archives: C|C++

GDB & core dump

一、core dump

1.何谓 core dump?

我们在开发(或使用)一个程序时,最怕的就是程序莫明其妙地当掉。虽然系统没事,但我们下次仍可能遇到相同的问题。于是这时操作系统就会把程序当掉 时的内存内容 dump 出来(现在通常是写在一个叫 core的file里面),让 我们或是debugger 做为参考。这个动作就叫作 core dump。
在 C/C++语言中,最常发生错误的地方就是指 针有问题。您可以利用 core 文件和debugger工具(gdb)把错误找出来。除了SIGSEGV(无效内存访问)信号以外,SIGABRT(异常终止), SIGBUS(硬件故障),SIGEMT(硬件故障),SIGFPE(算术异常),SIGILL(非法硬件指令),SIGIOT(硬件故障),SIGQUIT,SIGSYS(无效系统调用),SIGTRAP(硬件故障)等。
Continue reading

Posted in C|C++, Linux.

找到二进制字符串中连续最长”0“出现位置

一道在线测试题:给一个二进制字符串str(即只包含0和1的字符),找到其中连续最长0出现的位置。

Posted in C|C++.

【APUE】守护进程

概念

守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统中大多数服务和系统任务都是通过守护进程实现的。例如,作业规划进程crond、打印进程lqd、nfsd、lockd、rpciod(这三个是NFS的守护进程)、portmap(端口映射守护进程提供将RPC程序号映射为port好的服务)、bdflush(当内存达到下限时,将buffer cache flush到磁盘上)、kupdated(每隔一段时间,kupdated将dirty page flush到磁盘上以免数据丢失)、syslogd(由帮助操作人员把系统消息记入日志的任何程序使用)、crond(指定的日期和时间执行命令)。

创建守护进程

(1).创建子进程,父进程退出,这样让init收养子进程。
(2).调用setsid子进程中创建新会话
会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始与用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
Continue reading

Posted in C|C++, Linux.

pthread线程和信号

pthread线程和信号
所有的异步信号发到整个进程的所有线程(异步信号如kill, lwp_kill, sigsend, kill等调用产生的都是,异步信号也称为中断),而且所有线程共享信号的处理行为(即sigaction的设置,对于同一信号的设置,某一线程的更改会影响到所有线程)。但每个线程可以有自己的mask来阻止信号的发送,所以可以通过线程对mask的设置来决定信号发送到哪个线程。
Continue reading

Posted in C|C++, Linux.

pthread线程私有数据

线程私有数据是存储和查询与某个线程相关的数据的一种机制。把这种数据称为线程私有/特定数据的原因是希望每个线程可以独立地访问数据副本而无需担心数据同步问题。
设计线程私有数据接口的原因是:(1).线程ID不能保证是小而连续的整数。例如上一章的程序threadid得到的结果:
main thread: pid 9508 tid 1611388704 (0x600bd720)
new thread: pid 9508 tid 1603331840 (0x5f90e700)
Continue reading

Posted in C|C++, Linux.

pthread线程同步机制

1.互斥量
当多个控制线程共享相同的内存时,需要确保每一个线程看到一致数据视图。如果不存在读取数据或者所有数据只读时不会存在一致性问题。如果某个线程正在修改变量而其他线程也可以读取或者修改这个变量的时候就需要对这些线程进行同步。在变量修改多于一个存储器访问周期时可能出现不一致的错误。(例如需要写的十一个长类型,需要多次写入内存。而写之间如果有其他线程读取就会出现同时读取一些修改过和没修改过的数据)。
通过使用pthread的互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥量mutex本质上是一把锁,在访问共享资源前对互斥量加锁,然后其他任何试图访问互斥量加锁的线程将会被阻塞。访问完成后释放互斥量上的锁,被阻塞的线程将会变成可运行状态。同理,第一个变为可运行状态的进程将会锁住信号量,这样后面要访问该互斥量线程会被继续阻塞,直到回去再次等待它重新变为可用。
互斥量用pthread_mutex_t数据类型表示,在使用互斥量以前,必须首先对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量)或者使用pthread_mutex_init函数进行初始化。如果动态分配(malloc)那么释放内存前需要调用(pthread_mutex_destroy)

Continue reading

Posted in C|C++, Linux.

pthread线程库简介

1.线程实现

线程是在进程的基础上进一步的抽象,也就是说一个进程分为两个部分:线程集合和资源集合。线程是进程中的一个动态对象,它应该是一组独立的指令流,进程中的所有线程将共享进程里的资源。但是线程应该有自己的私有对象:比如程序计数器、堆栈和寄存器上下文。
线程分为三种类型:
(1).内核线程:
它的创建和撤消是由内核的内部需求来决定的,用来负责执行一个指定的函数,一个内核线程不需要和一个用户进程联系起来。它共享内核的正文段核全局数据,具有自己的内核堆栈。它能够单独的被调度并且使用标准的内核同步机制,可以被单独的分配到一个处理器上运行。内核线程的调度由于不需要经过态的转换并进行地址空间的重新映射,因此在内核线程间做上下文切换比在进程间做上下文切换快得多。
(2).轻量级进程:
轻量级进程是内核支持的用户线程,它在一个单独的进程中提供多线程控制。这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上,而且在它的生命周期这种绑定都是有效的。轻量级进程被独立调度并且共享地址空间和进程中的其它资源,但是每个LWP都应该有自己的程序计数器、寄存器集合、核心栈和用户栈。
(3).用户线程:
用户线程是通过线程库实现的。它们可以在没有内核参与下创建、释放和管理。线程库提供了同步和调度的方法。这样进程可以使用大量的线程而不消耗内核资源,而且省去大量的系统开销。用户线程的实现是可能的,因为用户线程的上下文可以在没有内核干预的情况下保存和恢复。每个用户线程都可以有自己的用户堆栈,一块用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区。库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换。
内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力。用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程。当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞。
Linux线程是通过进程来实现。Linux kernel为进程创建提供一个clone()系统调用,clone的参数包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通过clone()的参数,新创建的进程,也称为LWP(Lightweight process)与父进程共享内存空间,文件句柄,信号处理等,从而达到创建线程相同的目的。
在Linux 2.6之前,Linux kernel并没有真正的thread支持,一些thread library都是在clone()基础上的一些基于user space的封装,因此通常在信号处理、进程调度(每个进程需要一个额外的调度线程)及多线程之间同步共享资源等方面存在一定问题。Linux 2.6的线程库叫NPTL(Native POSIX Thread Library)。POSIX thread(pthread)是一个编程规范,通过此规范开发的多线程程序具有良好的跨平台特性。尽管是基于进程的实现,但新版的NPTL创建线程的效率非常高。一些测试显示,基于NPTL的内核创建10万个线程只需要2秒,而没有NPTL支持的内核则需要长达15分钟。
在Linux中,每一个线程都有一个task_struct。线程和进程可以使用同一调度其调度。内核角度上来将LWP和Process没有区别,有的仅仅是资源的共享。如果独享资源则是HWP,共享资源则是LWP。而在真正内核实现的

NPTL的实现是在kernel增加了futex(fast userspace mutex)支持用于处理线程之间的sleep与wake。futex是一种高效的对共享资源互斥访问的算法。kernel在里面起仲裁作用,但通常都由进程自行完成。NPTL是一个1×1的线程模型,即一个线程对于一个操作系统的调度进程,优点是非常简单。而其他一些操作系统比如Solaris则是MxN的,M对应创建的线程数,N对应操作系统可以运行的实体。(N2.Linux线程库pthread

(1). 测试宏
#ifdef _POSIX_THREADS
或者
sysconf(_SC_THREADS)
来检测是否支持宏。
(2). 线程标识pthread_t
pit_t是整个系统中唯一的。而线程pthread_t(struct)不同,只在它所属的进程环境有效。
判断两个线程ID是否相等:

(3).线程创建
程序启动时,它是以单进程的单个控制进程启动的。如果要增加新的线程,可以调用pthread_create来创建。

//若成功则返回0,否则返回错误编号。它不设置errno
C99中的新关键字restrict以及受限指针(restricted pointers)。受限指针作为一种编译器优化代码的方式,是由编译器厂商提供特定的实现.
restrict只可以用在指针身上。如果一个指针被restrict修饰,那么就在它(这个指针)和它所指向的对象之间建立了一种特殊的联系──只能用这个指针或者这个指针的表达式来访问这个对象的值.一个指针指向一个内存地址。同一块内存可以由多个指针来访问并在程序运行时修改它(这块内存)。restrict告诉编译器,如果一块由一个被restrict修饰的指针所指向的内存被修改了,那么没有其它的指针可以来访问这块内存。编译器可能会选择一种方式来优化代码中调用被restrict修饰的指针的部分,这可能导致错误发生。程序员有责任来确保正确地按照他们所设想的来使用被restrict修饰的指针,否则的话,可能会发生意想不到的结果。
如果一块特定的内存区没有被修改,那么它可以被多个restrict指针(被restrict修饰的指针)所指代(或者叫做引用或访问)。另外,restrict指针的赋值是有限制的,这一点在函数调用和嵌套块(nested block)之间是没有区别的。在包含restrict指针的块中,只能将外层的restrict指针的值赋给内层的restrict指针,在同层内不可以相互赋值,当然在外层不可能赋以内层的值。一个例外是,当一个声明restrict指针的代码快执行完后,这个restrict指针所指的内存就可以被其它的指针访问了(因为那个restrict是那个代码块的局部变量,当执行完那个代码块后,这个指针也就不复存在了)。
(4). 线程终止
如果进程中的任一线程调用了exit, __EXIT虎或_exit,那么整个进程就会终止。与之类似如果信号的默认动作是终止进程,那么信号发送到线程就会终止整个进程。
线程的退出方式:
(1).线程只从启动函数中返回,返回值是线程的退出码。
(2).线程可以被同一进程中其他线程取消。
(3).线程调用pthread_exit()。

void pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与传给启动函数的单个参数类似。进程中其他进程可以调用pthread_join函数访问到这个指针。如果线程从启动函数返回,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。如果对线程的返回值不感兴趣,那么就可以把rval_ptr设置为NULL。在样调用pthread_join函数将等待指定的线程终止,但并不获取线程的终止状态。例如在线程启动函数中返回:return ((void *) 1)这样pthread_join的函数可以通过rval_ptr这个指针得到线程的退出状态。如果线程调用ptread_exit((void *2)),那么pthread_join依然可以得到退出状态2.这个指针可以是复杂的结构类型以便保存更多信息。

int pthread_join(pthread_t thread, void **rval_ptr);
/* 成功返回0,否则返回错误编号 */
调用pthread_join的线程将被阻塞,直到指定的线程(thread)调用pthread_exit,从启动函数或者被取消。另外,void *rval_ptr可以作为一个int直接返回。例如return (void *)1;或者pthread_exit((void *)1);那么从pthread_join获得该指针直接可以用(int)ptr来输出。
这个void *rval_ptr指向的内存地址不应该在栈上,其内容会被下面调用的函数覆盖。应该是在堆上(通过malloc来创建)或者在静态数据段(用静态,全局变量)。
int pthread_cancel(pthread_t tid);
/* 成功返回0,否则返回错误编号 */
行为相等于参数值等同于PTHREAD_CANCELED的pthread_exit函数。
注意:线程可以自行选择忽略取消或者控制取消方式。注意pthread_cancel并不等待线程终止,仅仅是提出请求。
线程可以自己安排退出时需要执行的函数,这与进程atexit函数安排退出时要执行的函数是类似的。这样的函数称为线程清理处理程序。线程可以建立多个线程处理函数,存放在栈中,执行顺序与他们注册的顺序相反。

pthread_cleanup_pop删除上一次注册的线程清理处理程序。如果execute非0,则执行这个程序。
关于参数的使用方法的一个例子:

参考:
APUE
ULK
fork vfork clone pthread_create http://blog.csdn.net/sandflee/article/details/5189312

Posted in C|C++, Linux.