进程和线程之面试须知

中断:

软件中断:一个进程给另一个进程发送信号

硬件I/O中断

每一个进程都有一个运行用户程序的用户模式,但是当它的某个线程调用系统调用之后,进程会陷入内核模式并且运行在内核上下文中,它将使用不同的内存映射并且拥有对所有机器资源的访问权

进程

出处:http://www.cnblogs.com/jacklu/p/5317406.html
Linux实例分析:http://blog.csdn.net/a1937935900/article/details/76794986

相关系统调用


fork()、execve()、exit()、kill()、

进程描述符

调度参数、内存映射、信号、机器寄存器、系统调用状态、文件描述符、统计、内核堆栈

进程控制块(PCB)

即task_struct结构体,链表实现
PCB包含:1.进程状态(state);2.进程标识信息(uid、gid);3.定时器(time);4.用户可见寄存器、控制状态寄存器、栈指针(tss)
五种进程状态转换

进程的创建

子进程刚开始,内核并没有为它分配物理内存,而是以只读的方式共享父进程内存,只有当子进程写时,才复制。即“copy-on-write”写时复制。
每个进程拥有一个独立的程序计数器
系统调用fork创建一个与原始进程完全相同的进程副本(为新进程创建一个task_struct结构,然后将父进程的task_struct内容复制到其中)
#include <unistd.h>
pid_t fork(void);  //返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
do_fork-copy_process创建子进程需要的数据结构task_struct-dup_task_struct实际完成内存申请、内容复制(alloc_pid为新进程获取一个新的PID)-kmalloc为了得到一块连续的内存
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
 pid_t pid;
 char *message;
 int n = 0;
 pid = fork();
 while(1){
 if(pid < 0){
 perror("fork failed\n");
 exit(1);
 }
 else if(pid == 0){
 n--;
 printf("child's n is:%d\n",n);
 }
 else{
 n++;
 printf("parent's n is:%d\n",n);
 }
 sleep(1);
 }
 exit(0);
}
fork创建子进程把数据空间、堆、栈复制一份,所以可以发现子进程和父进程之间并没有对各自的变量产生影响。
vfork()
vfork创建子进程,与父进程共享内存数据
vfork先保证子进程先执行,当子进程调用exit()或者exec后,父进程才往下执行
用vfork时,一般都是紧接着调用exec,所以不会访问父进程数据空间,也就不需要在把数据复制上花费时间了,因此vfork就是”为了exec而生“的。
子进程不使用exit退出,使用return的结果

进程执行代码替换

execve()
可执行文件装入内核的linux_binprm结构体。
进程调用exec时,该进程执行的程序完全被替换,新的程序从main函数开始执行。因为调用exec并不创建新进程,只是替换了当前进程的代码区、数据区、堆和栈。

do_execve-open_exec打开可执行文件、获得argv[]、search_binary_handler(execve succeeded)

execve()和waitpid()结合实例:shell执行

在Linux的shell中键入cp file1 file2命令的执行解析

shell从键盘中读入命令read_command(command,params);
创建子进程,因为子进程复制了shell的文件描述符、寄存器和堆栈等,为了执行不同于父进程的代码,需要调用execve()系统调用来替换核心映像??execve(command,params,0);cp主程序包含的函数声明main(argc,argv,envp),argc为3,argv[0]为cp,argv[1]为file1,argv[2]为file2.params为argv[3];
而shell父进程需要等待cp子进程的执行结束和其结果waitpid(-1,&status,0);子进程结束调用exit()中的参数返回给&status,0为正常结束

进程终止

正常终止(5种)
从main返回,等效于调用exit
调用exit
exit首先调用各终止处理程序,然后按需多次调用fclose,关闭所有打开流
调用_exit或者_Exit
最后一个线程从其启动例程返回
最后一个线程调用pthread_exit
异常终止(3种)
调用abort
接到一个信号并终止
最后一个线程对取消请求作出响应

而main()函数return后,通常会调用 exit()或相似的函数(如:_exit(),exitgroup()),exit不是系统调用,是glibc对系统调用 _exit()或_exitgroup()的封装

wait和waitpid函数

wait用于使父进程阻塞,等待子进程退出;waitpid有若干选项,如可以提供一个非阻塞版本的wait,也能实现和wait相同的功能,实际上,linux中wait的实现也是通过调用waitpid实现的。
waitpid返回值:正常返回子进程号;使用WNOHANG且没有子进程退出返回0;调用出错返回-1;


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h> 

int main()
{
        pid_t pid0,pid1;
        pid0 = fork();
        if(pid0 < 0){
                perror("fork");
                exit(1);
        }
        else if(pid0 == 0){
                sleep(5);
                exit(0);//child
        }
        else{
                do{
                        pid1 = waitpid(pid0,NULL,WNOHANG);
                        if(pid1 == 0){
                                printf("the child process has not exited.\n");
                                sleep(1);
                        }
                }while(pid1 == 0);
                if(pid1 == pid0){
                        printf("get child pid:%d",pid1);
                        exit(0);
                }
                else{
                        exit(1);
                }
        }
        return 0;
}



当把第三个参数WNOHANG改为0时,就不会有上面五个显示语句了,说明父进程阻塞了。



a.out 的代码如下:


#include <stdio.h>
void main()

{
        printf("hello WYJ\n");
}

 

process.c的代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/times.h>
#include <sys/wait.h>

int main()
{
        pid_t pid_1,pid_2,pid_wait;
        pid_1 = fork();
        pid_2 = fork();
        if(pid_1 < 0){
                perror("fork1 failed\n");
                exit(1);
        }else if(pid_1 == 0 && pid_2 != 0){//do not allow child 2 to excute this process.
                if(execlp("./a.out", NULL) < 0){
                        perror("exec failed\n");
                }//child;       
                exit(0);
        }
        if(pid_2 < 0){
                perror("fork2 failded\n");
                exit(1);
        }else if(pid_2 == 0){
                sleep(10);
        }
        if(pid_2 > 0){//parent 
                do{
                        pid_wait = waitpid(pid_2, NULL, WNOHANG);//no hang
                        sleep(2);
                        printf("child 2 has not exited\n");
                }while(pid_wait == 0);
                if(pid_wait == pid_2){
                        printf("child 2 has exited\n");
                        exit(0);
                }else{
                //      printf("pid_2:%d\n",pid_2);
                        perror("waitpid error\n");
                        exit(1);

               }
        }
        exit(0);
}


僵尸进程

在进程调用了exit之后,该进程并非马上就消失掉,而是留下了一个成为僵尸进程的数据结构,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

子进程结束之后为什么会进入僵尸状态? 因为父进程可能会取得子进程的退出状态信息。

如何查看僵尸进程?

linux中命令ps,标记为Z的进程就是僵尸进程。

守护进程Daemon

是linux的后台服务进程。它是一个生存周期较长的进程,没有控制终端,输出无处显示。用户层守护进程的父进程是init进程。
守护进程创建步骤:
1、创建子进程,父进程退出,子进程被init自动收养;fork    exit
2、调用setsid创建新会话,成为新会话的首进程,成为新进程组的组长进程,摆脱父进程继承过来的会话、进程组等;setsid
3、改变当前目录为根目录,保证工作的文件目录不被删除;chdir(“/”)
4、重设文件权限掩码,给子进程更大的权限;umask(0)
5、关闭不用的文件描述符,因为会消耗资源;close

进程间通信-IPC

1.管道

shell中的(管道符)管线实现即管道技术

2.消息队列

3.信号

软件中断
4.共享代码

5.socket

详细介绍:http://www.cnblogs.com/CheeseZH/p/5264465.html
关于Linux下多个不相关进程互斥访问同一片共享内存的问题、记录锁
出处:http://www.cnblogs.com/my_life/articles/4538299.html

MFC之进程间通信

1.剪贴板
2.匿名管道
3.命名管道
4.邮槽

线程

线程的实现方式(用户线程和内核线程的区别)

创建线程
实现线程间通信
出处:http://blog.csdn.net/iamherego/article/details/12704881

线程同步

1.互斥对象

2.事件对象

3.关键代码段

相关代码解析:http://write.blog.csdn.net/postedit/76492853?ticket=ST-35518-5qxaIdgN94oMMOXBfncn-passport.csdn.netMFC多线程编程
线程间操作之原子性操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值