Linux-C语言编程-Day-1

末蓝、 2022-06-13 10:20 305阅读 0赞

进程概念

 进程是程序在计算机上执行一次的过程,也就是一个执行中的程序,进程是一个独立的逻辑控制流,独占处理器,相当于工人和机器,进程具有私有的地址空间,独占存储器系统,相当于工厂;进程的本质是程序在地址空间中按照代码逻辑控制流执行的过程,同时进程是资源分配的最小单位;

进程和程序的区别

 进程是动态的,并且具有生命周期,并且一个进程只能对应于一个程序;相反,程序是动态的指令集合,一个程序可以对应于多个进程;

进程的状态

 就绪:Ready,当进程获得出CPU以外的所有的必要的资源时,进程进入就绪状态,也就是说当进程得到CPU就可以立即运行;
 运行:Runing,进程正在占据CPU资源;
 阻塞(Blocked):进程犹豫等待某个事件发生而无法执行时,进程被迫放弃CPU执行资源;

进程的创建

进程的查看

Windows可以使用tasklist 例如:tasklist / F1 “PID eq 进程PID”; 在Linux底下可以使用ps命令进行查看,ps命令的相关选项:user:表示进程所属的用户;PID:表示进程ID;ppid:表示父进程ID;%CPU:表示CPU占有率;%mem:表示内存占有率;VSZ:表示进程虚拟大小;RSS:表示常驻内存,也就是内存中页面的数量;tty:表示进程使用的终端;stat:表示进程的状态;start:表示启动进程的时间;TIME:表示进程消耗CPU的时间;command:表示打开进程的命令和参数;  进程状态标识符:   D:表示不可中断进程,Uninterruptible;   R:表示正在运行,或者在队列中的进程;   S:处于休眠状态;   T:进程处于停止或者被追踪状态;   Z:僵尸进程;   W:表示进入内存交换,从内核2.6以后开始失效;   X:表示死掉的进程;   <:表示高优先级;   n:表示低优先级;   s:表示包含子进程;   +:表示位于后台的进程组;  查看某个具体的进程:ps -p pidps -C在命令行进行查看;同样的还可以使用pstree进行查看,但是可能需要安装yum install psmisc -y`;同样的的还可以使用top命令动态的查看系统进程;

进程的杀死

 windows:task kill /F /PID 或者task kill /F /IM 程序名
linuxkill 进程标识PID
 与进程相关的文件一般在/proc/底下,通常是进程id作为目录名的文件;

进程的操作

进程的标识通常使用pid来完成,获取当前进程的id使用函数pid_t getpid();获取当前进程父进程的ID使用函数pid_t getppid();

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(){
  4. printf("PID:%dPPID:%d\n",getpid(),getppid());//分别用于获得当前进程ID和父进程ID;
  5. for(;;);
  6. }

关于进程的创建

fork()函数

 函数失败时返回值是-1,返回值为0表示子进程逻辑控制流,如果返回值为其他,表示父进程控制流;
 函数的特点:
  1、调用一次,返回两次;
  2、相同但是具有独立的地址空间;
  3、可以并发执行;
  4、这个函数的本质是函数堆栈数据,text文本的复制;
函数的本质是父进程函数相关数据的复制,然后分叉出另一个函数;

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(){
  4. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  5. pid_t pid = fork();
  6. fork();
  7. if(pid == 0){
  8. // child
  9. printf("this is child\n");
  10. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  11. }else{
  12. printf("this is father\n");
  13. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  14. }
  15. for(;;);
  16. }

 子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
fork02.c:这个函数是用来构建子进程,用来输出一些值;

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int i = 100;
  4. int main(){
  5. int j=100;
  6. pid_t pid = fork();
  7. if(pid == 0){
  8. // child
  9. int k;
  10. for(k=0;k<10000;k++)
  11. printf("this is childi%d\t j%d\n",++i,++j);
  12. }else{
  13. int k;
  14. for(k=0;k<10000;k++)
  15. printf("this is fatheri%d\t j%d\n",--i,--j);
  16. }
  17. }

fork03.c:这个函数父进程在增加变量的值,子进程在减小函数的值,用来模拟两个进程的运行情况;

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int i = 100;
  4. int main(){
  5. int j=100;
  6. FILE* fd = fopen("./test","w+");
  7. pid_t pid = fork();
  8. if(pid == 0){
  9. // child
  10. int k;
  11. for(k=0;k<10000;k++)
  12. fprintf(fd,"this is childi%d\t j%d\n",++i,++j);
  13. }else{
  14. int k;
  15. for(k=0;k<10000;k++)
  16. fprintf(fd,"this is fatheri%d\t j%d\n",--i,--j);
  17. }
  18. }
exec函数簇
  1. char **environ;
  2. int execl (const char *path, const char *arg0, ..., (char*)0);
  3. int execlp(const char *file, const char *arg0, ..., (char*)0);
  4. int execle(const char *path, const char *arg0, ..., (char*)0, char *const envp[]);
  5. int execv (const char *path, char *const argv[]);
  6. int execvp(cosnt char *file, char *const argv[]);
  7. int execve(const char *path, char *const argv[], char *const envp[]);

execl.c

  1. #include<stdio.h>
  2. #include <unistd.h>
  3. extern char ** environ;
  4. int main(int argc,char **argv){
  5. execl("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0);
  6. return 0;
  7. }

execle.c

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. extern char **environ;
  4. int main(int argc,char **argv){
  5. execle("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0,environ);
  6. return 0;
  7. }

execlp.c

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(int argc,char **argv){
  4. execlp("ps","ps","-a","-o","pid,ppid,cmd,stat",0);
  5. return 0;
  6. }

execv.c

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(){
  4. char *args[]={
  5. "ps","-a","-o","pid,ppid,cmd,stat",0};
  6. execv("/bin/ps",args);
  7. return 0;
  8. }

execve.c

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. extern char **environ;
  4. int main(){
  5. char *args[]={
  6. "ps","-a","-o","pid,ppid,cmd,stat",0};
  7. execve("/bin/ps",args,environ);
  8. return 0;
  9. }

execvp.c

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. int main(){
  4. char *argv[] = {
  5. "ps","-a","-o","pid,ppid,cmd,stat",0};
  6. execvp("ps",argv);
  7. return 0;
  8. }

函数命名的规律组总结
v:表示第二个参数是数组;
l:第二个参数之后是变参;
p:第一个参数是文件名;
e:最后一个参数是环境变量;
常见的字符串数组函数
execv(),execvp(),execve()
可变参数函数
execle(),execlp(),execl();
exec.c函数:
 exec函数簇函数的返回值,-1表示失败,执行成功时,是不返回任何值的,这些函数具有以下特点:一次调用,失败返回;改朝换代,取而代之;但是函数的PID是不会变化的,变化的是程序地址空间的内容,exec函数簇函数的本质是覆盖原有函数,执行新的函数;

system(shell字符串)

 这个函数是系统函数int system(shell 字符串或者命令);函数的返回值为-1时,表示失败;函数的返回值是127时,表示无法启动shell;其它返回值用于表示函数的退出码;系统调用函数一次调用,一次返回;这个函数本质上是在执行shell命令;
*system.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(int argc,char** argv){
  5. printf("PID:%d\n",getpid());
  6. system("sleep 3&");
  7. printf("PID:%d\n",getpid());
  8. }

进程的结束

 对于returnexit来说,函数的执行属于寿终正寝,并且自己能够了结后事。_exit()函数属于自杀的情况,但是没有料理后事。abort()函数属于意外的死亡,也没有料理后事;信号终止属于他杀;料理后事表示的意思是程序结束以后,相关堆栈资源的回收工作;
main()函数return 0 是语言级别的退出,使用这种情况的退出,函数仍然需要调用exit(0)函数,exit(0)是函数级别的退出;
exit(0),表示释放所有的静态的全局对象,缓存,关掉所有的IO通道,然后终止程序;abort()会直接终止程序,但是不会释放任何资源;
exit(0)函数在调用_exit()系统调用之前要检查文件的打开情况,吧文件缓冲区中的内容写回文件,清理IO缓冲,exit(0)是标准的C函数;
_exit()不做清理IO缓冲函数,_exit()Linux系统调用;
 main函数退出:
main.c

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(){
  4. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  5. return 100;
  6. }

exit函数退出
exit函数一般用在main函数以外的函数退出

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(){
  5. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  6. exit(EXIT_FAILURE);
  7. abort();
  8. }

_exit函数一般用来结束子进程
这个函数暂时没有说明
abort()函数一般用来异常退出

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(){
  5. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  6. abort();
  7. }

还有一种方式–信号终止
pause.c

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. void test(int sig){
  5. printf("revc a signal%d",sig);
  6. }
  7. int main(){
  8. signal(SIGINT,test);
  9. printf("before pause\n");
  10. pause();
  11. printf("after pause\n");
  12. }

进程的停止

int sleep(unsigned int secs)函数 ,sece用于表示休眠的秒数,如果指定为-1表示永久休眠,函数的返回值是未休眠的秒数;如果没有信号中断,休眠指定的秒数返回0,否则马上返回未休眠的秒数;

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <time.h>
  4. #include <strings.h>
  5. int main(){
  6. int i = 0;
  7. for(;;){
  8. time_t t;
  9. time(&t);
  10. struct tm* ptm = gmtime(&t);
  11. char buf[BUFSIZ];
  12. bzero(buf,BUFSIZ);
  13. strftime(buf,BUFSIZ,"%P %T",ptm);
  14. printf("\r%s",buf);
  15. fflush(stdout);
  16. sleep(1);
  17. }
  18. }

int pause()函数返回值一定是-1,如果函数没有处理信号,直接中断,执行默认的信号处理,程序后续代码不再执行。如果程序存在信号处理,执行处理信号后,执行后续代码。

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. void test(int sig){
  5. printf("revc a signal%d",sig);
  6. }
  7. int main(){
  8. signal(SIGINT,test);
  9. printf("before pause\n");
  10. pause();
  11. printf("after pause\n");
  12. }

pid_t wait(int * status):函数等价于pid_t waitpid(-1,status,0);这个函数的参数包括:pid:小于-1时,表示的是等待进程组为pid的所有进程,-1:表示等待任何子进程;0:表示等待同组的进程;大于0:表示等待进程为pid的子进程;
status:参数的含义:
  正常结束,WIFEXITED(status)参数为0时,表示的是正常结束任何子进程,0表示的是非正常结束子进程;WEXITSTATUS()表示取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才是用这个宏定义;
  异常结束:WIFSIGNALED(ststus)非0值表示异常结束子进程,0表示非异常结束子进程;WTERMSIG(status),取得子进程因信号而终止的信号代码,一般先会用WIFSIGNALED来判断后才能使用宏;
  暂停:WIFSTOPPED(status),非0值表示暂停结束子进程,0表示暂停结束子进程;WSTOPSIG(status),取得引发子进程暂停的信号代码,一般先用
WIFSTOPPED来判断后才能使用此宏;
option,选项WNOHANG,若子进程没有结束,返回0,不予等待。若子进程结束,返回该子进程的ID;WUNTRACED,若子进程进入暂停状态,则马上返回,但子进程的结束状态不予理会。
 返回值,-1表示失败;其他值表示等待的PID;
wait.c:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <wait.h>
  4. void handler(int sig){
  5. pid_t cpid = wait(NULL);
  6. printf("child %d exit",cpid);
  7. }
  8. int main(){
  9. signal(SIGCHLD,handler);
  10. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  11. pid_t pid = fork();
  12. if(pid == 0){
  13. // child
  14. sleep(2);
  15. printf("this is child\n");
  16. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  17. }else{
  18. printf("this is father\n");
  19. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  20. }
  21. }

wait02.c:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <wait.h>
  5. void handler(int sig){
  6. int status;
  7. pid_t cpid = wait(&status);
  8. if(WIFEXITED(status)){
  9. printf("child exit by %d\n",WEXITSTATUS(status));
  10. }
  11. if(WIFSIGNALED(status)){
  12. printf("child exit by signal %d\n",WTERMSIG(status));
  13. }
  14. printf("child %d exit\n",cpid);
  15. }
  16. int main(){
  17. signal(SIGCHLD,handler);
  18. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  19. pid_t pid = fork();
  20. if(pid == 0){
  21. // child
  22. sleep(2);
  23. printf("this is child\n");
  24. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  25. //exit(0);
  26. }else{
  27. printf("this is father\n");
  28. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  29. printf("leave:%d\n",sleep(5));
  30. //exit(200);
  31. }
  32. for(;;);
  33. }

孤儿进程与僵尸进程

 孤儿进程:父进程先于子进程退出,init进程作为新的父进程,但是对于系统无害,init进程会回收这些系统资源;
 僵尸进程:子进程退出,父进程没有获得子进程的状态信息,可以通过调用wait(status)或者调用waitpid函数来实现;
waitpid.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <wait.h>
  5. int main(){
  6. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  7. pid_t pid = fork();
  8. if(pid == 0){
  9. // child
  10. sleep(2);
  11. printf("this is child\n");
  12. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  13. //exit(0);
  14. }else{
  15. printf("this is father\n");
  16. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  17. printf("pid:%d exit\n",waitpid(pid,NULL,0));
  18. }
  19. }

zomble.c:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <wait.h>
  4. void handle(int sig){
  5. //wait(NULL);
  6. printf("this is child exit %d",sig);
  7. }
  8. int main(){
  9. signal(SIGCHLD,handle);
  10. printf("PID:%d,PPID:%d\n",getpid(),getppid());
  11. pid_t pid = fork();
  12. if(pid == 0){
  13. // child
  14. printf("this is child\n");
  15. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  16. }else{
  17. printf("this is father\n");
  18. printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
  19. for(;;);
  20. }
  21. }

一个模拟简单shell的程序:
shell.c

  1. //这个程序是一个简单的shell程序,可以用于执行shell命令中不带有参数选项的命令;
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <signal.h>
  7. void handle(int sig){
  8. wait(NULL);
  9. }
  10. int main(){
  11. printf("welcome to sim shell\n");
  12. //
  13. signal(SIGCHLD,handle);
  14. for(;;){
  15. printf(">");
  16. fflush(stdout);
  17. char buf[BUFSIZ];
  18. bzero(buf,BUFSIZ);
  19. fgets(buf,BUFSIZ,stdin);
  20. size_t n = strlen(buf);
  21. if(buf[n-1] == '\n'){
  22. buf[n-1] = '\0';
  23. }
  24. if(strcmp(buf,"quit")==0)
  25. break;
  26. //system(buf);
  27. char* argv[128];
  28. bzero(argv,128);
  29. char* p = buf;
  30. int index = 0;
  31. char* temp = NULL;
  32. do{
  33. temp =strtok(p," ");
  34. p = NULL;
  35. argv[index++] = temp;
  36. }while(temp != NULL);
  37. if(fork() == 0){
  38. execvp(argv[0],argv);
  39. }
  40. //sleep(-1);
  41. pause();
  42. }
  43. printf("sim shell exit\n");
  44. return 0;
  45. }

发表评论

表情:
评论列表 (有 0 条评论,305人围观)

还没有评论,来说两句吧...

相关阅读

    相关 LinuxC/C++网络爬虫(1

      网络爬虫(Web Crawler),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本,有时候又称网络蚂蚁,是搜索引擎(例如:Baidu、Google……)的重要组成

    相关 Linux-C编程-Day-1

    进程概念  进程是程序在计算机上执行一次的过程,也就是一个执行中的程序,进程是一个独立的逻辑控制流,独占处理器,相当于工人和机器,进程具有私有的地址空间,独占存储器系统,

    相关 Linux-C语言编程-Day-1

    进程概念  进程是程序在计算机上执行一次的过程,也就是一个执行中的程序,进程是一个独立的逻辑控制流,独占处理器,相当于工人和机器,进程具有私有的地址空间,独占存储器系统,