Linux学习-3进程与线程
2020-08-10 21:42
1256 查看
目录
1.1 进程在内核的组织形式:进程控制块(PCB)(初始化信息)
1.1.1 Linux进程控制块:task_struct结构
4.1.4 设置环境变量(putenv、setenv、unsetenv)
1 进程在内核中的组织
CPU载入程序到内存--->CPU初始化程序--->CPU进程调度程序
1.1 进程在内核的组织形式:进程控制块(PCB)(初始化信息)
初始化:进程ID,用户ID,进程状态,调度信息,文件管理,虚拟内存管理,信号,时间和定时器...
1.1.1 Linux进程控制块:task_struct结构
1.1.2 task_struct:进程状态
1.1.3 task_struct:文件管理
1.1.4 task_struct:内存管理
1.1.5 进程在内核中的组织形式
2 进程属性
2.1 进程基本属性-进程ID
2.2 真实用户和有效用户的关系
2.3 有效用户和真实用户实例
2.4 普通用户能够修改自己的密码的原因
3 进程生命周期
3.1 进程的启动
◼C程序的启动函数是main,也是进程代码的入口点- main ( int argc, char *argv[] );
3.2 进程的终止
◼正常终止- 从main函数中返回
- 在任意代码中调用exit函数或_exit函数
- 最后一个线程从其启动例程中返回
- 最后一个线程调用pthread_exit函数
- 在任意代码中调用abort函数
- 接收到终止信号
3.3 进程的生命周期
3.3.1 终止进程的函数
◼头文件stdlib.h ,函数定义:void exit( int status ) ◼头文件unistd.h,函数定义:void _exit (int status ) ◼调用这两个函数均会正常地终止一个进程 ◼调用_exit 函数将会立即返回内核 ◼调用exit 函数:- 执行预先注册的终止处理函数
- 执行文件I/O操作的善后工作,使得所有缓冲的输出数据被更新到相应的设备
- 返回内核
3.3.2 exit与return的区别
- return是C语言关键字,exit是POSIX API函数
- 在main函数中,执行return和调用exit函数会产生相同的效果
- 在子函数中,执行return仅仅从子函数中返回,而调用exit函数将会退出当前进程
3.3.3 注册终止处理函数
- 当进程终止时,程序可能需要进行一些自身的清理工作,如日志登记、资源释放等
- 通过atexit函数或on_exit函数允许进程注册若干终止处理函数,当进程终止时,这些终止处理函数将会被自动调用
- int atexit(void (*func)(void));
- int on_exit (void (*func)(int, void *), void *arg);
4 进程环境
4.1 进程内存空间布局
正文:CPU执行的代码部分,正文段通常是共享、只读的
初始化的数据:包含了程序中需明确赋初值的变量,如全局变量int maxcount=99
未初始化的数据:程序执行之前,将此段中的数据初始化为0,如全局变量long sum[1000]
栈/堆:用于动态分配内存
命令行参数和环境变量:主要用于支撑函数调用存放参数、局部变量等
4.1.1 命令行参数
◼ls [参数] <路径或文件名>- ls –l /home
- mkdir -p /home/xiaokun/src
- cp –r /usr/local/src /root
4.1.2 C程序中main函数参数
4.1.3 环境变量表
◼每个进程都会有自己的环境变量表 ◼通过全局的环境指针(environ)可以直接访问环境变量表(字符串数组)- 头文件unistd.h
- extern char **environ;
4.1.4 设置环境变量(putenv、setenv、unsetenv)
◼设置环境变量的三种方法:- putenv
- setenv
- unsetenv
- 头文件:stdlib.h
- int putenv(char *str);
- 头文件:stdlib.h
- int setenv(const char* name,const char* value, int rewrite);
- setenv将指定环境变量的值设置为参数指定值(更改环境变量字符串)
- 若name已经存在 ,rewrite不等于0时,则删除其原先的定义; rewrite等于0,则不删除其原先的定义。
- 头文件:stdlib.h
- int unsetenv(const char* name);
- 删除指定的环境变量字符串
5 创建进程
5.1 创建进程
◼Linux中创建进程的方式:- 在shell中执行命令或可执行文件(由shell进程调用fork函数创建子进程)
- 在代码中(已经存在的进程中)调用fork函数创建子进程 (通过fork函数创建的进程为已经存在进程的子进程)
5.2 创建子进程
◼函数原型- 头文件:unistd.h
- pid_t fork(void);
- 在子进程中返回值为0(不合法的PID,提示当前运行在子进程中)
- 在父进程中返回值为子进程ID(让父进程掌握所创建子进程的ID号)
5.2.1 创建子进程示例
5.2.2 fork函数工作流程
◼子进程是父进程的副本- 子进程复制/拷贝父进程的PCB、数据空间(数据段、堆和栈)
- 父子进程共享正文段(只读)
- 当父子进程任意之一要修改数据段、堆、栈时,进行复制操作,并且仅复制修改区域
5.2.3 fork函数执行后父子进程的主要异同
5.2.4 父子进程共享文件
◼父子进程对共享文件的常见处理方式- 父进程等待子进程完成。当子进程终止后,文件当前位置已经得到了相应的更新
- 父子进程各自执行不同的程序段,各自关闭不需要的文件
5.2.5 fork的用法
◼父进程希望复制自己(共享代码,复制数据空间),但父子进程执行相同代码中的不同分支- 网络服务程序中,父进程等待客户端的服务请求,当请求达到时,父进程调用fork创建子进程处理该请求,而父进程继续等待下一个服务请求到达
- 子进程从fork返回后,立即调用exec类函数执行另外一个可执行文件
5.2.6 vfork函数
◼vfork用于创建新进程,而该新进程的目的是执行另外一个可执行文件 ◼由于新程序将有自己的地址空间,因此vfork函数并不将父进程的地址空间完全复制到子进程中 ◼子进程在调用exec或exit之前,在父进程的地址空间中运行 ◼vfork函数保证子进程先执行,在它调用exec或者exit之后,父进程才会继续被调度执行(父进程处于TASK_UNINTERRUPTIBLE状态)6 获知子程序运行状态改变
◼当一个进程发生特定的状态变化(进程终止、暂停以及恢复)时,内核向其父进程发送SIGCHLD信号 ◼父进程可以选择忽略该信号,也可以对信号进行处理(默认处理方式为忽略该信号) ◼wait或waitpid函数可以用于等待子进程状态信息改变,并获取其状态信息6.1 僵尸进程
◼进程在退出之前会释放进程用户空间的所有资源,但PCB等内核空间资源不会被释放- 当父进程调用wait或waitpid函数后,内核将根据情况关闭该进程打开的所有文件,释放PCB(释放内核空间资源)
- 对于已经终止但父进程尚未对其调用wait或waitpid函数的进程( TASK_ZOMBIE状态),称为僵尸进程
6.2 wait函数
◼功能:获取任意子进程的状态改变信息(如果是终止状态则对子进程进行善后处理) ◼函数原型- 头文件:sys/wait.h
- pid_t wait(int *statloc);
- statloc:用于获取子进程的状态改变
- 返回值:若成功返回状态信息改变子进程ID,出错返回-1
- statloc可以为空指针,此时父进程不需要具体了解子进程的状态变化,只是为了防止子进程成为僵尸进程,或者因为同步原因需等待子进程终止
- 若statloc不是空指针,则内核将子进程状态改变信息存放在它指向的存储空间中
- 如果所有子进程都还在运行,则父进程被阻塞(TASK_INTERRUPTIBLE状态),直到有一个子进程终止或暂停,wait函数才返回
- 如果已经有子进程进入终止或暂停状态,则wait函数会立即返回
- 若进程没有任何子进程,则立即出错返回-1
- 调用wait,然后将其返回的进程ID和所期望的子进程ID进行比较
- 如果ID不一致,则保存该ID,并循环调用wait函数,直到等到所期望的子进程ID为止
6.3 waitpid函数
◼功能:等待某个特定子进程状态改变 ◼函数原型- 头文件:sys/wait.h
- pid_t waitpid(pid_t pid, int *statloc, int options);
- 成功返回终止子进程ID,失败返回-1
- pid == -1:等待任意子进程执行终止(同wait)
- pid > 0:等待进程ID为pid的子进程执行终止
- pid == 0:等待其组ID等于调用进程组ID的任意子进程
- pid < -1:等待其组ID等于pid绝对值的任意子进程
- WCONTINUED:如果有暂停的进程由于SIGCONT信号的到来而继续运行,则函数将返回
- WUNTRACED:如果有处于终止状态的进程,则函数返回
- WNOHANG:如果没有任何已经终止的子进程则马上返回, 函数不等待,此时返回值为0
7 在进程中运行可执行文件
7.1 exec系列函数
◼进程调用exec系列函数在进程中加载执行另外一个可执行文件 ◼exec系列函数替换了当前进程(执行该函数的进程)的正文段、数据段、堆和栈(来源于加载的可执行文件) ◼执行exec系列函数后从加载可执行文件的main函数开始重新执行 ◼exec系列函数并不创建新进程,所以在调用exec系列函数后其进程ID并未改变,已经打开的文件描述符不变 ◼execl execle execlp execv execve execvp ◼六个函数开头均为exec,所以称为exec系列函数- l:表示list,每个命令行参数都说明为一个单独的参数
- v:表示vector,命令行参数放在数组中
- e:表示由函数调用者提供环境变量表
- p:表示通过环境变量PATH来指定路径,查找可执行文件
7.2 execl函数
◼函数原型- 头文件:unistd.h
- int execl(const char *pathname,const char *arg0, ...,NULL);
- pathname:要执行程序的绝对路径名
- 可变参数:要执行程序的命令行参数,以空指针结束
- 出错返回-1
- 成功该函数不返回!
7.3 execv函数
◼函数原型- 头文件:unistd.h
- int execv(const char *pathname, char *const argv[]);
- pathname:要执行程序的绝对路径名
- argv:数组指针维护的程序命令行参数列表,该数组的最后一个成员必须为空指针
- 出错返回-1
- 成功该函数不返回
7.4 execle函数
◼函数原型- 头文件:unistd.h
- int execle(const char *pathname, const char *arg0,... NULL, char *const envp[]);
- pathname:要执行程序的绝对路径名
- 可变参数:要执行程序的命令行参数,以空指针结束
- envp指向环境字符串指针数组的指针,该数组的最后一个成员必须为空指针
- 出错返回-1
- 成功该函数不返回
7.5 其他exec函数
◼execve函数
- int execve(const char *pathname,char *const argv[], char *const envp[]);
- int execlp(const char *filename,const char *arg0, ...,NULL);
- filename参数可以是相对路径(路径信息从环境变量PATH中获取)
- 例如默认环境变量中包含的PATH=/bin:/usr/bin:/usr/local/bin/
- int execvp(const char *filename,char *const argv[]);
7.6 exec类函数之间的关系
8 Linux线程控制
8.1 线程的基本概念
◼ 进程的概念体现出两个特点:资源(代码和数据空间、打开的文件等)以及调度/执行。线程是进程内的独立执行代码的实体和调度单元 ◼ 一个进程内的所有线程共享进程的很多资源(这种共享又带来了同步问题)8.2 线程与进程的对比
◼线程只拥有少量在运行中必不可少的资源
- PC指针:标识当前线程执行的位置
- 寄存器:当前线程执行的上下文环境
- 栈:用于实现函数调用、局部变量(局部变量是私有的)
8.3 线程ID
◼同进程一样,每个线程也有一个线程ID ◼进程ID在整个系统中是唯一的,线程ID只在它所属的进程环境中唯一 ◼线程ID的类型是pthread_t,在Linux中的定义:- typedef unsigned long int pthread_t( /usr/include/bits/pthreadtypes.h)
- 头文件:pthread.h
- pthread_t pthread_self();
8.4 线程的创建
◼pthread_create函数用于创建一个线程 ◼函数原型- 头文件:pthread.h
- int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
- tidp:指向线程ID的指针,当函数成功返回时将存储所创建的子线程ID
- attr:用于指定线程属性(一般直接传入空指针NULL,采用默认线程属性)
- start_rtn:线程的启动例程函数指针,创建的线程首先执行该函数代码(可以调用其他函数)
- arg:向线程的启动例程函数传递信息的参数
- 成功返回0,出错时返回各种错误码
8.5 线程的终止
◼线程的三种终止方式- 线程从启动例程函数中返回,函数返回值作为线程的退出码
- 线程被同一进程中的其他线程取消
- 线程在任意函数中调用pthread_exit函数终止执行
- 头文件:pthread.h
- void pthread_exit(void *rval_ptr);
- rval_ptr:该指针将参数传递给pthread_join函数(与exit函数参数用法类似)
8.6 取消线程
◼线程调用该函数可以取消同一进程中的其他线程(即让该线程终止) ◼函数原型- 头文件: pthread.h
- int pthread_cancel(pthread_t tid);
- tid:需要取消的线程ID
- 成功返回0,出错返回错误编号
8.7 父线程等待子线程终止
◼函数原型- 头文件:pthread.h
- int pthread_join(pthread_t thread,void **rval_ptr);
- 成功返回0,否则返回错误编号
8.7.1 pthread_join函数
◼参数- thread:需要等待的子线程ID
- rval_ptr:(若不关心线程返回值,可直接将该参数设置为空指针)
相关文章推荐
- linux学习总结进程与线程
- Linux内核学习(二)---Linux中的进程、线程和轻量级进程
- Linux进程与线程学习随笔
- Unix/linux进程及线程间同步技术总结【学习总结,请勿吐槽。。。】
- Linux进程线程学习笔记:进程创建
- Linux进程线程学习笔记:进程控制
- linux线程进程学习
- linux下多线程学习4_打印线程id和进程id
- Linux进程线程学习笔记:进程创建
- Linux进程线程学习笔记 系列转
- 【Linux系统学习】进程与线程
- Linux进程线程学习笔记
- linux学习之进程,线程和程序
- Linux进程线程学习笔记:运行新程序
- Linux进程线程学习笔记
- Linux进程线程学习笔记:进程创建
- Linux进程与线程学习笔记(更新)
- Linux-线程和进程学习
- Linux进程线程学习笔记:运行新程序
- Linux进程线程学习笔记:进程间通信之 管道