7 --- Linux 学习笔记之--进程
来源:互联网 发布:淘宝5金冠店铺排行 编辑:程序博客网 时间:2024/04/27 20:46
————————————进程————————————————
1. 什么是进程
执行的程序: 代码 资源 CPU
进程有很多数据维护: 进程状态/进程的属性
所有进程属性采用结构体维护: 树形数据结构
ps 查看进程常见属性
top 查看系统进程执行状况
pstree(ptree)
kell 向进程发送信号
kell -s 信号
kell -l 显示进程能接受的所有信号
进程有很多属性: ps 可以查看属性
--------------------------------------------------------------------
$ ps a -------当前用户的所有的进程
$ ps au --------所有的用户
$man ps
$ top ---------查看进程的执行状况(利用空格刷新进程,类似 window 的任务管理器,
q 退出)
$ pstree ----查看进程树 (树状进程)或者 ptree
$ kill -l -----显示进程能接收的所有信号
$ kill -s 9
224(进程号) ----- 关闭进程
---------------------------------------------------------------------
2.创建进程
1. 代码-加载到内存-分配CPU时间片
代码由独立的程序存在。
2. 进程有关的创建函数:
2.1 #include <stdlib.h>
int system(const char *command);
建立独立进程,拥有独立的代码空间。
等待新的进程执行完毕,system 才返回---阻塞
案例:
使用system调用一个程序。
观察进程ID。
观察阻塞。
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>main(){int r;printf("%d\n",getpid());//r = system("./text");//r = system("ls -l");//r = system("gcc text.c -otext");r = system("make");//printf("%d\n",r>>8 & 255)printf("%d\n",WEXITSTATUS(r));}
结论:
新的返回值与system 返回值有关系。
说明:
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。返回值
=-1:出现错误
=0:调用成功但是没有出现子进程
>0:成功退出的子进程的id
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值>。 如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
-------------------------------------------------------------------------------------------------------
2.2 子进程: 被创建的进程。
父进程: 相对被创建的进程。
popen: 创建子进程。
在父子进程之间建立管道。
函数说明:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen 通过type是r还是w确定command的输入/输出方向,r和w是相对command的管道而言的。r表示command从管道中读入,w表示 command通过管道输出到它的stdout,popen返回FIFO管道的文件流指针。pclose则用于使用结束后关闭这个指针。
案例:
使用 popen 调用 ls -l ,并且建立一个管道读取输出。
// popen.c#include <sys/types.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <stdio.h>int main(){FILE *stream;FILE *wstream;char buf[1024];memset(buf,0,sizeof(buf));// 初始化bufstream = popen("ls -l","r"); // 将"ls -l"命令输出wstream = fopen("test_popen.txt","w+");//新建一个可写的文件fread(buf,sizeof(char),sizeof(buf),stream);//将刚刚FILE* stream的数据流读取到buf中fwrite(buf,1,sizeof(buf),wstream);//将buf中的数据写到FILE *wstream对应的流中,也是写到文件中pclose(stream);fclose(wstream);return 0;}
---------------------------------------------------
$ gcc popen.c -omain
$ ./main
& cat test_popen.txt
------------------------------------------
-------------------------------------------------------------------------------------------
2.3 exec 系列函数
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
作用:替换当前进程的代码空间中的代码数据。
函数本身不创建新的进程。
说明:
fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。),也就是这两个进程做完全相同的事。
在fork后的子进程中使用exec函数族,可以装入和运行其它程序(子进程替换原有进程,和父进程做不同的事)。
fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被 exec 执行的进程的PID不会改变(和调用exec的进程的PID一样)。
int execl(const char *path, const char *arg, ...);第一个参数: 替换程序。
第二个参数……:命令行
命令格式: 命令名 选线参数
案例:使用exec执行一个程序:
体会:
是否创建新的进程? ---- 没有创建新的进程。
execl的参数的命令行格式 ;
execl与execlp的区别; (execl只能是当前路径,execlp使用系统搜索路径)
execl替换当前进程代码。
// text.c#include <stdio.h>#include <unistd.h>int main(){printf("%d\n",getpid());sleep(5);return 0;}
////exec.c#include <stdio.h>#include <unistd.h>int main(){int r = execl("test","mytest",NULL);// 这里的test是可执行文件//int r = execlp("ls","ls","-l",NULL);printf("结束%d\n",r);return 0;}
----------------------------------------------
$gcc text.c -otest
$./test
$gcc exec.c -omain
$./main ---- 和上面的效果一样的
-----------------------------------------------
2.4 fork 函数
#include <unistd.h>
pid_t fork(void);
小案例:
///fork.c#include <stdio.h>#include <unistd.h>int main(){printf("创建进程前!\n");int pid = fork();printf("创建进程后:pid = %d\n",pid);return 0;}
运行结果为:
发现:
printf("创建进程后:pid = %d\n",pid);
这条语句被执行了两次。
结论:
(1).创建了新进程。
(2).新进程的代码是什么?克隆了父进程的代码,而且克隆了执行的位置,
父进程执行的 ,子进程不执行。0是子进程打印的,非0是父进程打印的。。
(3).父子进程同时执行。
3.应用进程:
使用fork创建新的进程有什么意义?
---使用fork实现多任务.
1.进程。
2.线程
3.信号
4.异步
5.进程池与线程池
案例:
进程的主要作用是实现多任务
使用进程创建实现多任务。
1.UI
2.建立多任务框架
显示7为随机数,显示当前时间。
3.分别处理不同的任务。
#include <curses.h>#include <unistd.h>#include <time.h>#include <string.h>#include <stdio.h>#include <stdlib.h>#include <math.h>WINDOW *wtime,*wnumb;main(){initscr();wtime = derwin(stdscr,3,10,3,(COLS-20)/2);wnumb = derwin(stdscr,3,10,(LINES-3)/2,(COLS-11)/2);box(wtime,0,0);box(wnumb,0,0);refresh();wrefresh(wtime);wrefresh(wnumb);if(fork()) // parent shot time {time_t tt;struct tm *t;while(1){tt = time(0);t = localtime(&tt);mvwprintw(wtime,1,1,"%02d:%02d:%02d", t->tm_hour,t->tm_min,t->tm_sec);refresh();wrefresh(wtime);wrefresh(wnumb);sleep(1);}}else // child shot number{int num = 0;int i;while(1){// num = rand()%100000000;num = 0;for(i=0;i<7;++i) { num =num*10 + rand()%10; } mvwprintw(wnumb,1,1,"%06d",num); sleep(1); refresh(); wrefresh(wtime); wrefresh(wnumb);}}endwin();}
截图:
查看进程文件是否相同:
--------------------------------------------------------------------------
$ps a----发现两个 main,两个进程号 5419 5420
$ cd /proc
$ls -d 54*
$ cd 5419
$cd fd
$ ls -l
$cd ../../5420/fd
$ls -l
----------------------------------------------------------------------------------------------
4.理解进程。
1.父子进程的关系
有两个目录,所以是两个独立的进程。
互为父子关系。
2.问题:
2.1 父子进程先结束,子进程怎么办?
子进程就是依托根进程init: 变成孤儿进程
孤儿进程没有危害。
2.2 子进程先结束,父进程怎么办?
子进程先结束,子进程会变成僵尸进程
僵尸进程不占用内存,cup,但是进程任务树上有一个节点。
僵尸进程或造成进程名额资源的浪费,所以必须的杀死僵尸进程。
补充:
-------------------------------------------------------------------------------------------------------------------------------------------------------
一个进程在调用 exit 命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进(Zombie)的数据结构(系统调用 exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在 Linux 进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装 SIGCHLD 信号处理函数调用 wait 或 waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么 init 进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。
Linux 系统对运行的进程数量有限制,如果产生过多的僵尸进程占用了可用的进程号,
将会导致新的进程无法生成。这就是僵尸进程对系统的最大危害。
---------------------------------------------------------------------------------------------------------------------------------------------------------
3.僵尸进程使用wait回收
4.父进程怎么知道子进程退出?
子进程结束通常向父进程发送一个信号。 进程编号17
5.父进程处理子进程退出信号
signal(int sig,void(*)(int));
向系统注册:只要sig信号发送,系统停止进程,并调用wait函数
当函数执行完毕,继续原来进程。
5.1 实现处理函数
5.2使用signal 绑定信号 与 函数
僵尸进程回收模型案例:
// signal.c#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>void deal(int s){int status;wait(&status);// status保存状态吗printf("回收中.....\n");sleep(5);printf("回收完毕:%d\n",WEXITSTATUS(status));}void main(){if(fork() == 0){printf("child!\n");sleep(4);printf("退出!\n");exit(8);}else{// 只要sig这个信号到来,就会调用这个函数,回收僵尸进程signal(SIGCHLD,deal);while(1){printf("parent!\n");sleep(1);}}}
补充:
对 WIFEXITED 这个宏的说明:
1,WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个
非零值。
(请注意,虽然名字一样,这里的参数 status 并不同于 wait 唯一的参数--指向整数的指针
status,而是那个指针所指向的整数,切记不要搞混了。
)
2.WEXITSTATUS(status) 当 WIFEXITED 返回非零值时,我们可以用这个宏来提取子进程
的返回值,如果子进程调用 exit(5)退出,WEXITSTATUS(status)就会返回 5;如果子进程调
用 exit(7),WEXITSTATUS(status)就会返回 7。请注意,如果进程不是正常退出的,也就是
说,WIFEXITED 返回 0,这个值就毫无意义。
--------------------------------
————————————————————
$ man scandir
$ man ps
$ man system
$ man wait
$ man 2 wait
$ man popen
$ man execl
$ man rand
$ man 2 time
$ man 2 wait
$ man signal
--------------------------------------------------
问题:
父进程的全局栈,堆,局部栈,fd 也会克隆吗?
6 父进程的资源访问
6.1 内存资源
6.2 文件资源
说明: 子进程克隆了父进程的整个内存区域(全局区/局部区,内存),但是内存区域指向不同的物理空间。
尽管克隆,但是内存独立,不能相互访问。
映射内存:
MAP_SHARED(公有): 映射到同一个物理内存。
MAP_PRIVATE(私有):映射到不同的物理内存。
- 7 --- Linux 学习笔记之--进程
- Linux学习笔记之---进程
- linux学习笔记之进程
- Linux+C学习笔记之进程管理
- linux学习笔记之进程管理
- Linux学习笔记之守护进程
- 《linux程序设计学习笔记》之----进程
- Linux学习笔记之进程管理
- Linux进程线程学习笔记:进程间通信之 管道
- Linux进程学习笔记
- Linux进程学习笔记
- Linux 进程学习笔记
- 学习笔记之进程
- linux初级学习之进程 第七单元学习笔记
- Linux学习之进程
- linux 学习之 进程
- Linux内核学习笔记之进程管理1
- linux c语言学习笔记之守护进程
- Codeforces Round #256 (Div. 2)A-D
- VS中的路径宏
- VS中c++文件调用c 函数 ,fatal error C1853 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
- Chromium的智能指针/引用计数/Callback/Bind
- ThinkPHP问题记录
- 7 --- Linux 学习笔记之--进程
- cygwin+ctags+taglist 打造vim c语言编辑器(IDE)
- 【HDU】3768 Shopping 最短路+DP
- 敏捷开发之Scrum扫盲篇
- 查看linux文件大小
- WF4.0 基础篇 (五) 数据的传递 Variable变量
- 百度定位sdk Couldn't load locSDK3 from loader dalvik
- 敏捷开发的具体方法
- JDK和JRE的区别