终于要讲到线程部分,线程和进程让人够头痛的内容。

一、线程概念

老样子,我们还是按我们讲进程时的方式说起,参看:UNIX再学习 -- 进程环境
首先需要了解下,什么是线程。
Linux 下的线程,可能我们比较陌生,但是我们一直在玩 Windows 系统。应用程序文件、任务管理器,这些东西应该是很溜的。比如:

查看详细信息,最上一列,右击选择列;找到线程打对勾。如下图,就可看到线程数了。

通过上图我们可以看到 一个进程中有多个线程。但是到底什么是线程呢?
参看:进程与线程的一个简单解释  

大神举得例子很有意思。
进程好似车间,线程好似车间工人;任务是有很多工人协同完成;车间内的空间设施工人都是共享的;有些房间比如厕所一次只能容纳一人,进入需加锁(互斥锁)。
进程,是资源分配单位。线程,是CPU调度基本单位。
线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
一个进程可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。
一个进程的所有线程都共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户 ID 和组 ID 等。
一个进程的每个线程都拥有独立的 ID、寄存器值、栈内存、调度策略和优先级、信号掩码、errno变量以及线程私有数据等。
也可以说线程是包含在进程中的一种实体。它有自己的运行线索,可完成特定任务。可与其他线程共享进程中的共享变量及部分环境。可通过相互之间协同来完成进程所要完成的任务。
之前有转载一篇文章,可当扩展来看,参看:进程与线程及其区别
1.进程和线程的区别
什么是进程(Process):普通的解释就是,进程是程序的一次执行,而什么是线程(Thread),线程可以理解为进程中的执行的一段程序片段。在一个多任务环境中下面的概念可以帮助我们理解两者间的差别:进程间是独立的,这表现在内存空间,上下文环境;线程运行在进程空间内。一般来讲(不使用特殊技术)进程是无法突破进程边界存取其他进程内的存储空间;而线程由于处于进程空间内,所以同一进程所产生的线程共享同一内存空间。 同一进程中的两段代码不能够同时执行,除非引入线程。线程是属于进程的,当进程退出时该进程所产生的线程都会被强制退出并清除。线程占用的资源要少于进程所占用的资源。 进程和线程都可以有优先级。在线程系统中进程也是一个线程。可以将进程理解为一个程序的第一个线程。
线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:
(1)地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的
地址空间;而进程有自己独立的地址空间;
(2)进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是.
(4)二者均可并发执行.
1.进程和线程的差别。
答:线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于
进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销
明显大于创建或撤消线程时的开销。

二、POSIX 线程

早期 UNIX 厂商各自提供私有的线程库版本,无论是接口还是实现,差异都非常大,代码移植非常困难。
我们将要讨论的线程接口来自 POSIX.1-2001。线程接口也称为“pthread”或“POSIX 线程”
POSIX 线程的功能测试宏是 _POSIX_THREADS。应用程序可以把这个宏用于 #ifdef 测试,从而在编译时确定是否支持线程,也可以把 _SC_THREADS 常数用于调用 sysconf 函数,进而在运行时确定是否支持线程。遵循 SUSv4 的系统定义符号 _POSIX_THREADS 的值为 200809L。
查看 /usr/include/i386-linux-gnu/bits/posix_opt.h 可以看到 _POSIX_THREADS 的定义
 69 /* Tell we have POSIX threads.  */70 #define _POSIX_THREADS  200809L

使用 pthread 需要包含一个头文件:pthread.h

同时连一个共享库:libpthread.so  即 gcc 编译时加选项 -lpthread
#include <pthread.h>gcc ... -lpthread

三、线程标识

就像每个进程有一个进程 ID 一样,每个线程也有一个线程 ID。进程 ID 在整个系统中是唯一的,但线程 ID 不同,线程 ID 只有在它所属的进程上下文中才有意义。
线程 ID 是用 pthread_t 数据类型表示的.
查看 /usr/include/i386-linux-gnu/bits/pthreadtypes.h 可以看到 pthread_t 类型为无符号长整型

typedef unsigned long int pthread_t;
实现的时候可以用一个结构来代表 pthread_t 数据类型,所以可移植的操作系统实现不能把它作为整数处理。
因此必须使用一个函数来对两个线程 ID 进行比较。
参看:Pthreads and Semaphores

1、函数 pthread_equal

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
返回值:若相等,返回非 0 数值;否则,返回 0

(1)函数功能

比较线程 ID

(2)示例说明

//示例一
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>  int main(){  pthread_t thread_id;  thread_id=pthread_self(); // 返回调用线程的线程ID  printf("Thread ID: %lu.\n",thread_id);  if (pthread_equal(thread_id,pthread_self()))  {  printf("Equal!\n");  } else {  printf("Not equal!\n");  }  return 0;
}
编译:# gcc test.c -lpthread
输出结果:
Thread ID: 3075704512.
Equal!
//示例二
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>void* routine1 (void* arg)
{pthread_t thread = pthread_self ();printf ("子线程1 ID:%lu\n", thread);return (void*)thread;
}void* routine2 (void* arg)
{pthread_t thread = pthread_self ();printf ("子线程2 ID:%lu\n", thread);if (pthread_equal (thread, (pthread_t)arg))printf ("两个线程ID相同\n");elseprintf ("两个线程ID不同\n");return (void*)thread;
}int main()
{pthread_t thread1;int error = pthread_create (&thread1, NULL, routine1, NULL);if (error){perror ("pthread_create");exit (EXIT_FAILURE);}pthread_join (thread1, NULL);pthread_t thread2;error = pthread_create (&thread2, NULL, routine2, (void*)&thread1);if (error){perror ("pthread_create");exit (EXIT_FAILURE);}//sleep (1);pthread_join (thread2, NULL);return 0;
}
输出结果:
子线程1 ID:3076029248
子线程2 ID:3076029248
两个线程ID不同

(3)示例解析

pthread_equal 函数比较两个线程 ID,这个没什么可讲的。

示例二是我讲完以后又添加的,创建了两个子线程。它们的线程 ID 不同,可以理解。
我要说的是 sleep 和 pthread_join 有什么区别呢? 还有为什么在线程中使用 usleep 不合适? 
留个疑问,后续我们讲多线程会一起讲到的!!

2、函数 pthread_self

#include <pthread.h>
pthread_t pthread_self(void);
返回值:调用线程的线程 ID

(1)函数功能

获取线程自身的 ID

(2)示例说明

当线程需要识别以线程 ID 作为标识的数据结构时,pthread_self 函数可以与 pthread_equal 函数一起使用,如上例
#include <pthread.h>
#include <stdio.h>void* thread_func(void *arg)
{printf("thread id=%lu\n", pthread_self());return arg;
}int main(void)
{pid_t pid;pthread_t tid;pid = getpid();printf("process id=%lu\n", pid);pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid,NULL);return 0;
}
输出结果:
process id=2933
thread id=3076057920

(3)示例解析

创建线程,查看线程自身的线程 ID

四、线程创建

1、函数 pthread_create

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
返回值:若成功,返回 0;否则,返回错误编号

(1)函数功能

创建新线程

(2)参数解析

thread:输出线程 ID。pthread_t 即 unsigned long int。
attr:线程属性,NULL 表示缺省属性。pthread_attr_t 可能是整型也可能是结构体,因实现而异。
start_routine:线程过程函数指针。参数和返回值的类型都是 void*。启动线程过程其实就是调用一个函数,只不过是在一个独立的线程中调用,函数一旦返回,线程即告结束。
arg:传递给线程过程函数的参数。线程过程函数的调用者是系统内核,因此需要预先将参数存储到系统内核中。

(3)函数解析

main 函数可以被视为主线程的线程过程函数。main函数一旦返回,主线程即告结束。主线程一旦结束,进程即告结束。进程一旦结束,其所有的子线程统统结束。
应设法保证在线程过程函数执行期间,传递给它的参数 arg 所指向的目标持久有效。
注意,pthread 函数在调用失败时通常会返回错误码,它们并不像其他的 POSIX 函数一样设置 errno。每个线程都提供 errno 的副本,这只是为了与使用 errno 的现有函数兼容。子啊线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,这样可以把错误的范围限制在引起出错的函数中。

(4)示例说明

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *task (void* arg)
{printf ("进入子线程\n");sleep (5);printf ("子线程进程ID是:%lu\n", getpid ());printf ("22子线程ID是:%lu\n", pthread_self ());printf ("退出子线程\n");
}int main (void)
{printf ("主线程启动\n");pthread_t thread;int error = pthread_create (&thread, NULL, task, NULL);if (error)perror ("pthread_create"), exit (1);sleep (10);printf ("退出主线程\n");printf("11子线程ID是:%lu\n",thread);printf ("主线程进程ID是:%lu\n", getpid ());printf("主线程ID是:%lu\n",pthread_self());return 0;
}
编译:# gcc test.c -lpthread
输出结果:
主线程启动
进入子线程
子线程进程ID是:3117
22子线程ID是:3076180800
退出子线程
退出主线程
11子线程ID是:3076180800
主线程进程ID是:3117
主线程ID是:3076183744

(5)示例解析

主线程需要等待子线程结束后再结束,如果不等待不如把上例的 sleep (10); 注释掉,则不执行子线程。

子线程和主线程是同一个进程,thread 为输出子线程 ID,pthread_self函数可以得到线程自身的线程ID
再有 pthread 函数在调用失败时通常会返回错误码。

五、线程的等待

1、函数 pthread_join

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
返回值:成功返回 0,失败返回错误号

(1)函数功能

主要用于等待一个线程的结束,并且回去退出码

(2)参数解析

第一个参数:线程的 ID

第二个参数:二级指针,用于获取线程的退出码

(3)函数解析

该函数根据参数 thread 指定的线程进程等待,将目标线程终止时的退出状态信息拷贝到 *retval 这个参数指定的位置上。
pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是 joinable 的。

(4)示例说明

//示例一
#include <stdio.h>
#include <pthread.h>void *task (void *p)
{//ps 指向只读常量区 ps本身在栈区char *ps = "hello";return ps;
}int main (void)
{//启动一个线程pthread_t tid;pthread_create (&tid, NULL, task, NULL);//主线程等待子线程的处理,并且获取返回值char *pc = NULL;//pc 指针指向了 上面字符串的首地址pthread_join (tid, (void**)&pc);printf ("pc = %s\n", pc);return 0;
}
输出结果:
pc = hello
//示例二
//使用pthread_join函数获取线程的返回值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>void* task(void* p)
{int i = 0;//静态局部变量,生命周期变长static int sum = 0;for(i = 1; i <= 100; i++){sum += i;}return (void*)∑
}int main(void)
{//1.启动一个线程pthread_t tid;pthread_create(&tid,NULL,task,NULL);//2.主线程进行等待,获取返回值int* pi = NULL;pthread_join(tid,(void**)&pi);printf("子线程返回的数据是:%d\n",*pi);return 0;
}
输出结果:
子线程返回的数据是:5050

(5)示例解析

上例中主要需要注意的是 pthread_join 获取返回值。
从线程过程函数中返回值的方法:
线程过程函数将所需返回的内容放在一块内存中,返回该内存的地址,同时保证这块内存,在函数返回即线程结束以后依然有效。
若 retval 参数非 NULL,则 pthread_join 函数将线程过程函数所返回的指针,拷贝到该参数所指向的内存中。
若线程过程函数所返回的指针指向动态分配的内存,则还需保证在用过该内存之后释放它。

2、函数 pthread_detach

#include <pthread.h>
int pthread_detach(pthread_t thread);
返回值:成功返回 0;失败返回一个错误码

(1)函数功能

主要用于将参数指定的线程标记为分离状态,对于分离状态的线程来说:当该线程终止后,会自动将资源释放给系统,不需要其他线程的加入/等待,也就是说分离的线程无法被其他线程使用 pthread_join 进行等待

建议:对于新启动的线程来说,要么使用 pthread_detach 设置为分离状态,要么使用 pthread_join 设置为可加状态。

(2)示例说明

#include <stdio.h>
#include <pthread.h>void *task (void *p)
{int i = 0;for (i = 0; i <= 10; i++)printf ("子线程中:i = %d\n", i);
}
int main (void)
{//启动一个子线程,打印1~10之间的数pthread_t tid;pthread_create (&tid, NULL, task, NULL);//设置子线程为分离的状态pthread_detach (tid);//主线程进行等待,然后打印1~10之间的数pthread_join (tid, NULL);int i = 0;for (i = 1; i <=10; i++)printf ("主线程中:i = %d\n", i);return 0;
}
输出结果:
主线程中:i = 1
主线程中:i = 2
主线程中:i = 3
主线程中:i = 4
主线程中:i = 5
主线程中:i = 6
主线程中:i = 7
主线程中:i = 8
主线程中:i = 9
主线程中:i = 10

(3)示例解析

我们上面有讲到,如果使用 pthread_join 等待,则等待子线程完成后打印主线程1~10之间的数。

但是使用了 pthread_detach 分离,则直接打印主线程1~10之间的数,且 pthread_join 等待也会失效。

六、线程终止

如果进程中的任意线程调用了 exit、_Exit 或者 _exit,那么整个进程就会终止。与此相比类似,如果默认的动作是终止进程,那么发送到线程的信号就会终止整个进程。
单个线程可以通过 3 种方式退出,因此可以在不终止整个进程的情况下,停止它的控制流。
(1)线程可以简单地从启动例程中返回,返回值是线程的退出码。
(2)线程可以被同一进程中的其他线程取消。
(3)线程调用 pthread_exit。

1、我们讲一下第三种调用 pthread_exit。

#include <pthread.h>
void pthread_exit(void *retval);

(1)函数功能

主要用于终止正在运行的线程通过参数 retval 来带出线程的退出状态信息
在同一个进程中的其他线程可以通过调用 pthread_join 函数来获取退出状态信息

(2)函数解析

在线程过程函数或者被线程过程函数直接或间接调用的函数中,调用 pthread_exit 函数,其效果都与在线程过程函数中执行 return 语句效果一样 -- 终止调用线程。
注意,在任何线程中调用 exit 函数,被终止的都是进程。当然随着进程的终止,隶属于该进程的包括调用线程在内的所有线程也都一并终止。

(3)示例说明

//示例一
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *task (void *p)
{int i = 0;for (i = 1; i < 100; i++){if (i == 10){//return (void*)i;pthread_exit ((void*)i);//exit (100);}printf ("子线程中:i = %d\n", i);}
}int main (void)
{pthread_t tid;pthread_create (&tid, NULL, task, NULL);int res = 0;pthread_join (tid, (void**)&res);printf ("res = %d\n", res);return 0;
}
编译:# gcc test.c -lpthread输出结果:
子线程中:i = 1
子线程中:i = 2
子线程中:i = 3
子线程中:i = 4
子线程中:i = 5
子线程中:i = 6
子线程中:i = 7
子线程中:i = 8
子线程中:i = 9
res = 10
//示例二
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>void circle_area (double r)
{double* s = malloc (sizeof (double));*s = 3.14 * r * r;pthread_exit (s);
}void* start_routine (void* arg)
{circle_area (*(double*)arg);printf("此句将不被执行");return NULL;
}int main()
{double r = 10.0;pthread_t thread;int error = pthread_create (&thread, NULL, start_routine, (void*)&r);if (error){perror ("pthread_create");exit (EXIT_FAILURE);}double* s;if (pthread_join (thread, (void**)&s)){perror ("pthread_join");exit (EXIT_FAILURE);}printf ("圆面积:%g\n", *s);free (s);return 0;
}
输出结果:
圆面积:314

(4)示例解析

该示例说明了,在任何线程中调用 exit 函数,被终止的都是进程。进程终止可其他线程就就同样终止了。
使用 pthread_join 获取退出状态信息,return 可以返回退出状态信息,pthread_exit 同样也可以返回退出状态信息。

七、线程取消

1、函数 pthread_cancel

#include <pthread.h>
int pthread_cancel(pthread_t thread);
返回值:成功返回 0;失败返回错误码

(1)函数功能

主要用于对参数指定的线程发送取消的请求

(2)函数解析

该函数只是向线程发出取消请求,并不等于线程终止。缺省情况下,线程在收到取消请求以后,并不会立即终止,而是仍继续运行,直到其达到某个取消点。在取消点处,线程检查其自身是否已被取消,若是则立即终止。
当线程调用一些特定函数时,取消点会出现。

2、函数 pthread_setcancelstate

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
返回值:成功返回 0,失败返回错误码

(1)函数功能

主要用于设置新的取消状态,返回之前的取消状态

(2)参数解析

state:取消状态,可取以下值
    PTHREAD_CANCEL_ENABLE  接受取消请求(缺省)
    PTHREAD_CANCEL_DISABLE  忽略取消请求
oldstate:输出原取消状态,可取 NULL

3、函数 pthread_setcanceltype

#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);
返回值:成功返回 0,;失败返回错误码

(1)函数功能

主要用于设置新的取消类型,获取之前的取消类型

(2)参数解析

type:取消类型,可取以下值
    PTHREAD_CANCEL_DEFERRED  延迟取消(缺省)
        被取消线程接收到取消请求之后并不立即终止,而是一直等到执行了特定的函数(取消点)之后再终止。
    PTHREAD_CANCEL_ASYNCHRONOUS  异步取消
        被取消线程可以在任意时刻终止,而不是非得遇到取消点。
oldtype:输出原取消类型,可取 NULL

4、示例说明

//线程取消函数的使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>void* task(void* p)
{//设置允许被取消pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//设置为立即取消pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);while(1){printf("I am superman!\n");sleep(1);}
}void* task2(void* p)
{printf("开始取消线程...\n");sleep(5);printf("取消线程结束\n");pthread_cancel(*(pthread_t*)p);
}int main(void)
{//1.启动一个新线程,不断进行打印pthread_t tid;pthread_create(&tid,NULL,task,NULL);//2.启动另外一个新线程,负责取消上述线程pthread_t tid2;pthread_create(&tid2,NULL,task2,&tid);//3.主线程等待子线程的结束pthread_join(tid,NULL);pthread_join(tid2,NULL);return 0;
}编译:# gcc test.c -lpthread输出结果:
开始取消线程...
I am superman!
I am superman!
I am superman!
I am superman!
I am superman!
取消线程结束

5、示例解析

创建了两个线程,线程 2 负责取消线程 1。而设置取消状态为允许被取消,取消类型是立即取消。

八、线程清理处理程序

1、函数 pthread_cleanup_push、pthread_cleanup_pop

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

(1)函数解析

与进程在退出时可用 atexit 函数安排退出时类似的,线程也可以安排它退出时需要调用的函数。这样的函数就称为 线程清理处理程序。一个线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说,它们的执行顺序与它们注册时相反。

(2)参数解析

当线程执行以下动作时,清理函数 routine 是由 pthread_cleanup_push 函数调度的,调用时只有一个参数 arg:
调用 pthread_exit 时;
响应取消请求时;
用非零 execute 参数设置 0,清理函数将不被调用。
不管发生上述哪种情况,pthread_cleanup_pop 都将删除 pthread_cleanup_push 调用建立的清理处理程序。

(3)示例说明

#include "apue.h"
#include <pthread.h>void
cleanup(void *arg)
{printf("cleanup: %s\n", (char *)arg);
}void *
thr_fn1(void *arg)
{printf("thread 1 start\n");pthread_cleanup_push(cleanup, "thread 1 first handler");pthread_cleanup_push(cleanup, "thread 1 second handler");printf("thread 1 push complete\n");if (arg)return((void *)1);pthread_cleanup_pop(0);pthread_cleanup_pop(0);return((void *)1);
}void *
thr_fn2(void *arg)
{printf("thread 2 start\n");pthread_cleanup_push(cleanup, "thread 2 first handler");pthread_cleanup_push(cleanup, "thread 2 second handler");printf("thread 2 push complete\n");if (arg)pthread_exit((void *)2);pthread_cleanup_pop(0);pthread_cleanup_pop(0);pthread_exit((void *)2);
}int
main(void)
{int            err;pthread_t   tid1, tid2;void     *tret;err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);if (err != 0)err_exit(err, "can't create thread 1");err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);if (err != 0)err_exit(err, "can't create thread 2");err = pthread_join(tid1, &tret);if (err != 0)err_exit(err, "can't join with thread 1");printf("thread 1 exit code %ld\n", (long)tret);err = pthread_join(tid2, &tret);if (err != 0)err_exit(err, "can't join with thread 2");printf("thread 2 exit code %ld\n", (long)tret);exit(0);
}
编译:# gcc test.c -lpthread输出结果:
thread 2 start
thread 2 push complete
cleanup: thread 2 second handler
cleanup: thread 2 first handler
thread 1 start
thread 1 push complete
thread 1 exit code 1
thread 2 exit code 2

(4)示例解析

两个线程都正确的启动和退出了,但是只有第二个线程的清理处理程序被调用了,而区别是一个是 return 返回,一个为调用 pthread_exit。因此,可以看出如果线程是通过从它的启动例程中返回而终止的话,它的清理处理程序就不会被调用。还要注意,清理处理程序时按照与它们安装时相反的顺序被调用的。

UNIX再学习 -- 线程相关推荐

  1. UNIX再学习 -- 线程控制

    留楼以后有时间再讲. 感慨一下,线程部分有点懵逼.线程同步除了互斥量,好像其他的都不熟悉,没怎么用过. 搞的我没有心情看一下去了.APUE 第 12 章先跳过去,抓紧看更重要的东西吧.

  2. UNIX再学习 -- 线程同步

    一.为什么要线程同步 当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图.如果每个线程使用的变量都是其他线程不会读取和修改的,那么就不存在一致性问题.同样,如果变量时只读的,每个线程同 ...

  3. UNIX再学习 -- 记录锁

    APUE第 3 章,参看:UNIX再学习 -- 文件I/O  fcntl 函数它的记录锁功能我们当时没讲.接下来就详细说明下. 一.读写冲突 1.如果两个或两个以上的进程同时向一个文件的某个特定的区域 ...

  4. UNIX再学习 -- exit 和 wait 系列函数

    我们一开始讲进程环境时,就有提到了.进程有 8 种方式使进程终止. 其中 5 种为正常终止,它们是: (1)在 main 函数中执行 return (2)调用 exit 函数,并不处理文件描述符,多进 ...

  5. UNIX再学习 -- 文件描述符

    在 UNIX/Linux 系统中,一切皆文件,这句话想必都有听过.对于文件的操作几乎适用于所有的设备,这也就看出了文件操作的重要性了.在C语言再学习部分有讲过标准I/O文件操作,参看:C语言再学习 - ...

  6. UNIX再学习 -- 进程间通信之管道

    一.进程间通信概念 首先,需要了解一下什么是进程间通信. 进程之间的相互通信的技术,称为进程间通信(InterProcess Communication,IPC). 下图列出 4 种实现所支持的不同形 ...

  7. UNIX再学习 -- 守护进程(转)

    参看:守护进程 一.什么是守护进程 守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程.它是一个生存期较长的进程,通常独立于控制 ...

  8. UNIX再学习 -- 进程关系

    APUE 第 10 章信号讲完,回过头来看一下第 9 章的进程关系.终端登录和网络登录部分,我们只讲 Linux 系统的. 一.终端登录 我记得我们讲 root 登录设置时有提到,参看:C语言再学习 ...

  9. UNIX再学习 -- 函数abort

    abort 函数之前有讲过的,参看:C语言再学习 -- 关键字return和exit ()函数 然后我们在讲 8 中进程终止时,也说过.参看:UNIX再学习 -- exit 和 wait 系列函数 下 ...

最新文章

  1. JavaScript 有多灵活?
  2. python django框架如何导出_python框架django的数据库的正向生成和反向生成
  3. think in java i o_《Thinking in Java》学习——18章Java I/O系统(三)
  4. java实现自动任务_Java实现定时任务的三种方法
  5. 2018.8.26 Spring自学如门
  6. HttpServletRequestWrapper使用技巧(自定义session和缓存InputStream)
  7. jQuery基础教程之如何注册以及触发自定义事件
  8. mysql可以创建多少条数据类型_mysql支持的数据类型
  9. 面试题 31 : 栈的压入、弹出序列
  10. 仿得微博字符限制效果
  11. pc java版什么区别_Java主要有三种版本:用于工作站、PC标准版的是( )。
  12. MODBUS调试工具 C#源码 包含MODBUS主站调试工具和MODBUS从站调试工具
  13. 《测绘综合能力》——海洋测绘
  14. 【助教工作】2021团队项目助教跟班全攻略
  15. java无敌_12 款做Java后台管理系统的项目,超级无敌好用!
  16. ural 1553 树剖+线段树
  17. of vs 和 pj 2013各个版本
  18. python+uiautomation,怎么学习,雪地跪求大佬赐教
  19. 计算机网络第六弹——应用层
  20. Mac IDEA配置阿里云国内镜像

热门文章

  1. leetcode523 Continuous Subarray Sum
  2. .NET C#使用微信公众号登录网站
  3. flash也玩p2p
  4. matlab 仿照案例-目标检测
  5. 40岁后学编程(1)
  6. 读书笔记《单核工作法》_3原理2,3
  7. 可以查到的资料和可以淘到的原件 DIY 四轴
  8. deb和rpm文件安装
  9. OpenMP的环境变量
  10. 【转】用matlab画极坐标图,希望不同的半径点对应不同颜色,应该怎么做?有什么函数