一.查看源代码
1.线程与进程
在一些操作中,等待的时间是比较长的,为避免卡顿,或者反应不及时引入线程和进程。
进程是资源分配的最小单位,线程是CPU调度的最小单位。
单进程单线程:一个人在一个桌子上吃菜。
单进程多线程:多个人在同一个桌子上一起吃菜。
多进程单线程:多个人每个人在自己的桌子上吃菜。
- ubuntu默认没有pthread库,需要安装。
sudo apt-get install -y glibc-doc manpages-posix-dev
- 查看pthrea手册,提示:
$ man pthreads
No manual entry for pthreads
#安装manpages
$ sudo apt-get install manpages-posix manpages-posix-dev
二. 编译各个程序并在Ubuntu运行;理解并解释运行状态和结果
2.多线程
(1)线程的创建与终止
- 函数
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- 编译并执行
gcc -o ptread pthread.c -lpthread
./ptread
#include<unistd.h>
for (index = 0; index < array_num; index++) { /* 循环创建 5个线程 */
printf("In main: creating thread %ld.\n", index);
returned_code_err = pthread_create(&thr_num_array[index], NULL, thread_function, (void *) index); /* 创建线程 */
sleep(1); //创建线程后延时
(2)进程间的连接与分离
- 函数
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- 部分代码
for(index=0; index<NUM_THREADS; index++)
{
printf("Main: creating tid_array %ld\n", index);
err = pthread_create(&tid_array[index], NULL, thread_func, (void *)index); /* 创建线程 */
if (err)
{
printf("ERROR; return code from pthread_create() is %d\n", err);
exit(-1);
}
err = pthread_join(tid_array[index], &status); /*等待线程终止,并获取返回值*/
if (err)
{
printf("ERROR; return code from pthread_join() is %d\n", err);
exit(-1);
}
printf("Main: completed join with tid_array %ld having a status of %ld\n",index,(long)status);
}
- 编译并运行
gcc -o Untitled-2.out Untitled-2.c -lpthread -lm
./Untitled-2.out
(3)使用互斥量保护多线程同时输出
- 编译并运行
gcc -o Untitled-4.out Untitled-4.c -lpthread -lm
./Untitled-4.out
(4)条件变量使用
条件变量用来阻塞一个线程,直到其他的线程通知它条件已经满足为止。
- 编译并执行
gcc -o Untitled-5.out Untitled-5.c -lpthread -lm
./Untitled-5.out
3. 多进程
(1)获取测试环境变量代码
- 通过main()函数的第三个参数env循环获取
#include <stdio.h>
int main(int argc, char * argv[], char *env[])
{
int i = 0;
while (env[i])
puts(env[i++]);
return 0;
}
- 通过environ全局变量获取
#include <stdio.h>
extern char ** environ;
int main(int argc, char * argv[])
{
int i = 0;
while (environ[i]) puts(environ[i++]);
return 0;
}
- 通过getenv函数获取
#include <stdio.h>
#include <stdlib.h>
int main ()
{
printf("PATH : %s\n", getenv("PATH"));
printf("HOME : %s\n", getenv("HOME"));
printf("ROOT : %s\n", getenv("ROOT"));
printf("SHELL : %s\n", getenv("SHELL"));
printf("USERNAME : %s\n", getenv("USERNAME"));
printf("LANG : %s\n", getenv("LANG"));
printf("XDG_SESSION_DESKTOP : %s\n", getenv("XDG_SESSION_DESKTOP"));
printf("OLDPWD : %s\n", getenv("OLDPWD");
return(0);
}
(2)创建进程程序代码
- fork()函数
fork()函数从运行着的进程中分裂出一个子进程,它通过拷贝父进程的方式创建子进程。子进程与父进程有相同的代码空间、文件描述符等资源。
- 用法
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
-
进程创建后,子进程与父进程开始并发执行,执行顺序由内核调度算法来决定。fork()函数如果成功创建了进程,就会对父子进程各返回一次,其中对父进程返回子进程的PID,对子进程返回0;失败则返回小于0的错误码。
-
编译执行
gcc -o fork.out fork.c
./fork.out
(3)子进程加载新程序代码
- 原理
在创建进程后子进程与父进程有相同的代码空间;实际应用中,需要子进程 去执行另外一个程序,可以在子进程中调用exec族函数它的代码段完全替换为 新的程序,并从这个新进程的main函数开始执行。
- 运行结果
(4)使用daemon创建守护进程
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,它不需要用户输入就能运行并提供某种服务。
守护进程的父进程是init进程,因为它真正的父进程在fork出该子进程后就先于核子进 程exit退出了,所以它是-个由init领养的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出(无论是向标准输出设备还是标准错误输出设备的输出)都需要特殊处理
- daemon()函数
#include <unistd.h>
int daemon(int nochdir, int noclose);
- 编译并执行
gcc -o daemon.out daemon.c
./daemon.out
cat /tmp/daemon.log
(5)信号函数sigaction的使用
信号(signal),又称为软中断信号,用来通知进程发生了异步事件。进程之 间可以互相发送信号,内核也可以因为内部事件而给进程发送信号。注意,信号 的作用仅仅是通知进程发生了什么事件,并不向该进程传递任何数据。
- sigaction函数
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
- 编译并执行
gcc -o sigaction.out sigaction.c
./sigaction.out