POSIX thread IDs are not the same as the thread IDs returned by the Linux-specific gettid() system call. POSIX thread IDs are assigned and maintained by the threading implementation. The thread ID returned by gettid() is a number (similar to a process ID) that is assigned by the kernel.
I would go with pthread_setaffinity_np but be aware that the manual says:
gettid() returns the caller's thread ID (TID). In a single-threaded process, the thread ID is equal to the process ID (PID, as returned by getpid(2)). In a multithreaded process, all threads have the same PID, but each one has a unique TID.
线程拥有自己的堆栈,和自己的寄存器上下文切换时,需要挂起线程,存储被切换线程的堆栈和寄存器上下文,然后设置新的线程的寄存器上下文和堆栈,调整这两个线程的级别。
两者之间的区别:
·作用域
信号量: 进程间或线程间(linux仅线程间)
互斥锁: 线程间
·上锁时
信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一。一句话,信号量的value>=0。
互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源。如果没有锁,获得资源成功,否则进行阻塞等待资源可用。一句话,线程互斥锁的vlaue可以为负数。
==========
semGive(semSync); //信号量释放,有效。
semTake(semSync,WAIT_FOREVER); //等待信号量
使用二进制信号量可以很方便的实现互斥,互斥是指多任务在访问临界资源时具有排他性。为使多个任务互斥访问临界资源,只需要为该资源设置一个信号量,相当 于一个令牌,哪个任务拿到这个令牌即有权使用该资源。把信号量设为可用,然后将需要资源的任务的临界代码置于semTake()和SemGive()之间 即可。
同步即任务按照一定顺序先后执行,为了实现任务A和B的同步,只需要让任务A和B共享一个信号量,并设初始值为空,即不可用,将semGive()置于任务A之后,而在任务B之前插入semTake()即可.
--思考--
while(1){
semTake(SEMID);
( u# u7 o: R3 ?* w
1st statement;
# x0 ?. `; Q1 N3 y1 O
2nd statement;
3rd statement;
- X2 |6 e4 {8 V# I& o
}
! E6 a, \& w: x5 t% h
问个问题:如果上面的task执行到2nd statement时,下一个semGive(SEMID)到来,上面这个task还会相应这个semGive(SEMID)吗?
不会立即响应,在执行完3rd statement返回到semTake(SEMID)才响应
下面的图来自:
http://software.intel.com/zh-cn/blogs/2010/07/20/400004478/?cid=sw:prccsdn1223
Unix中有两类特殊的进程:孤儿进程orphan和僵尸进程zombie
Orphan:当父进程比子进程先终止时,子进程的将被init进程接管,
Zombie:当子进程终止之后(exit仅仅是将进程的状态改变),父进程没有获取子进程的退出状态时(watipid),内核进程空间表中还存在子进程的记录,资源也未被释放。(只有父进程获取子进程终止状态之后,内核才会从进程空间表中将子进程记录删除并释放资源)
僵尸进程的处理
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 那么不会因为父进程太忙来不及wait子进程,或者说不知道 子进程什么时候结束,而丢失子进程结束时的状态信息呢?
不会.因为UNIX提供了一种机制可以保证 只要父进程想知道子进程结束时的状态信息, 就可以得到. 这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等. 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等), 直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果你进程不调用wait / waitpid的话, 那么保留的那段信息就不会 释放,其进程号就会一定被占用,但是系统所能使用的进程号是有限的,如果大量的产生 僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免.
僵尸进程的避免 1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起 2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收 3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号 4. 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信 号,那么它就一直保持僵尸状态; 存在的问题:如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程,系统的性能可能会收到影响。 ** 如果这时父进程结束了,那么自 动会接手这个子进程,为它收尸,它还是能被清除的。
4、子进程结束后为什么要进入僵尸状态?
* 因为父进程可能要取得子进程的退出状态等信息。
5、僵尸状态是每个子进程比经过的状态吗?
是的。
* 任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
* 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
6、如何查看僵尸进程
$ ps -el 其中,有标记为Z的进程就是僵尸进程 S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;Z代表僵死状态;T代表停止或跟踪状态
7.如何查看孤儿进程??
问10.10.10.126/25和10.10.10.10.127/25是否可以通信,感觉主考官是在考察对单播地址和广播地址的考察,两个单播地址如 果同一网段走二层转发就可以了,如果不同网段有路由的话走三层转就可以了,但这些不是主考官想问的。10.10.10.10.127/25掩码25位的情 况下,这个地址就成了广播地址(最后7bit全是1)。感觉这是他给的陷阱。
8. linux中的进程0 和 进程1
1. 进程0是所有其他进程的祖先, 也称作idle进程或swapper进程。
2. 进程0是在系统初始化时由kernel自身从无到有创建。(过程集中在start_kernel函数内)
3. 进程0的数据成员大部分是静态定义的,即由预先定义好的(如上)INIT_TASK, INIT_MM等宏初始化。
进程0的描述符init_task定义在arch/arm/kernel/init_task.c,由INIT_TASK宏初始化。 init_mm等结构体定义在include/linux/init_task.h内,为init_task成员的初始值,分别由对应的初始化宏如INIT_MM等初始化,
进程1:
进程0最终会通过调用kernel_thread创建一个内核线程去执行init函数,这个新创建的内核线程即Process 1(这时还是共享着内核线程0的资源属性如地址空间等)。init函数继续完成剩余的内核初始化,并在函数的最后调用execve系统调用装入用户空间的可执行程序/sbin/init,这时进程1就拥有了自己的属性资源,成为一个普通进程(init进程)。
9 读写锁 一个细节:来自【1】
如果在写者锁等待队列中有一个或多个写线程正在等待获得写者锁时,新加入的读线程会被放入读者锁的等待队列。这是因为,尽管这个新加入的读线程能与正在进行读操作的那些读线程并发读取共享资源,但是也不能赋予他们读权限,这样就防止了写线程被新到来的读线程无休止的阻塞。
10 粗粒度锁与细粒度锁 来自【1】
为了减少串行部分的执行时间,我们可以通过把单个锁拆成多个锁的办法来较小临界区的执行时间,从而降低锁竞争的性能损耗,即把“粗粒度锁”转换成“细粒度锁”。但是,细粒度锁并不一定更好。这是因为粗粒度锁编程简单,不易出现死锁等Bug,而细粒度锁编程复杂,容易出错;而且锁的使用是有开销的(例如一个加锁操作一般需要100个CPU时钟周期),使用多个细粒度的锁无疑会增加加锁解锁操作的开销。
在计算机系统设计领域,没有哪种设计是没有缺点的,需要具体对待。例如,Linux内核在初期使用了Big Kernel Lock(粗粒度锁)来实现并行化。从性能上来讲,使用一个大锁把所有操作都保护起来无疑带来了很大的性能损失,但是它却极大的简化了并行整个内核的难度。当然,随着Linux内核的发展,Big Kernel Lock已经逐渐消失并被细粒度锁而取代,以取得更好的性能。
11. linux中进程的优先级
默认值为80,要想更改,通过命令renice
例子:
ian@attic4:~$ renice +3 1537;ps -l 1537
1537: old priority 1, new priority 3
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 R 1000 1537 32408 99 83 3 - 1001 - pts/3 0:18 sh count1.sh 100
vxworks内核有256个优先级,编号是0-255,0的优先级最高,255最低。任务的优先级可以在创建时设定,系统默认的任务优先级为100。
12
========CSDN上的一个关于进程,线程堆栈区别的分析=======
很 久很久以前,是没有进程这个东西的。那时候的操作系统只能把要做的工作排好队,做完这件再做下一件,最多也就加个优先级,哪个关系好就先做哪一个。于是那 些想一边听歌一边写程序的程序员们便不干了,开始修理那个操作系统,使之能同时运行多个程序。于是进程就出现了:它就是一个程序在数据集合上的一次执行。
因为突然变得僧多粥少了,所以每个进程只好做一些额外的事情:在别人使用之前把自己的东西收拾好,下次轮到自己时再摆出来。为了保存这些额外的东西,进程的结构也发生了相应的变化。一个进程被分成三大部分:代码段、数据段和PCB(进程控制块)。
在多出来的PCB中,我们保存了如下信息:
l 进程标识符(操作系统用于识别进程的唯一标识)
l 处理机状态(主要是通用寄存器,指令寄存器,PSW和用户栈指针)
l 进程调度信息(状态、优先级,被阻塞原因和其他一些乱七八糟的东西)
l 进程控制信息(同步信息、代码段和数据段的信息、资源清单和指向下一个PCB的指针)
操作系统正是通过PCB来管理这多个进程。在这样的系统里,进程既是操作系统独立调度和分派的基本单位,又是一个可拥有资源的独立单位。
线程:进程的再分身
好 了,现在程序员可以一般听歌一边写程序了。可是不幸的或者说是幸运的,新的问题出现了。如果多个进程间使用很多相同数据的话,实在是太浪费了。我们当然不 能允许这样的事情持续下去,所以线程出现了。同一个进程下可以拥有多个线程,它们共享这个进程的资源,它们之间的切换也不再需要PCB,而只需要极少一点 资源就可以了。在这样的操作系统里,线程变成了系统调度和分派的基本单位。
简单的说进程和线程有如下不同:
l进程可以拥有资源,线程共享进程拥有的资源
l进程间的切换必须保存PCB,同个进程的多个线程间的切换不用那么麻烦
最后我们以一个实例来作为本文的结束:
当你在一台PC上打开两个QQ时,每一个QQ是一个进程;而当你在一个QQ上和多人聊天时,每一个聊天窗口就是一个线程。
& y0 Q0 a X. i" c
===============
【线程】是一组特定的指令序列的实体。它可以被操作系统安排
去运行(调度)。在
单CPU系统上,很显然多个线程不可能
真正同时运行,只能按照某种顺序依次轮流运行。这个顺序是
由操作系统的调度算法决定的。
线程在其存在期间有状态,例如:
运行状态:正在运行;
ready状态:等待操作系统安排它去运行;
阻塞状态:等待特定事件发生,例如I/O操作完成等;
睡眠状态:等待一个定时器超时。
线程只有在运行状态才会消耗CPU时间。在其它状态时,仅
需要调度程序的管理。
线程拥有自己的
CPU寄存器和堆栈。在它占用CPU运行时,
如果OS的调度程序决定‘赶走’它,系统会保存CPU寄存器到
一个和这个线程有关的数据结构中;如果OS决定让某线程
再次运行,则OS从该数据结构中恢复CPU寄存器。这样达到
切换的目的。
有些操作系统如VxWorks,没有进程和线程的概念,
只有任务。任务之于OS,大致相当于线程之于进程。所有
任务,包括OS,共享系统中唯一的地址空间。所有任务共享
系统中所有资源。一个任务打开一个设备后,别的任务可以
向这个设备上读或写。
然而还是有些不同的。例如,进程中的线程,它们所打开的
文件描述符会被记录在该进程的进程控制块(PCB)中,这样,
当进程终止时,OS会去关闭这些文件(如果还没关闭的话)。
然而在VxWorks中,
别指望在OS关机前给你关闭文件【
要显式调用close函数???】,所以
可能出现文件数据丢失(如果任务不关闭该文件)。
参考:
【1】