system创建一个进程,管道创建一个进程
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/time.h>#include <sched.h>int main(int argc, char **argv) { /* uid 用户id pid 进程标识id ppid 父进程标识id comm 命令 tty 终端的名称 stime 进程的启动时间 time 进程的cpu时间 ps -e 显示所有进程。 -f 全格式。 -h 不显示标题。 -l 长格式。-w 宽输出。 -a 显示终端上的所有进程,包括其他用户的进程。 -r 只显示正在运行的进程。 -x 显示没有控制终端的进程。 */ // int system(const char *comstring); // system("ps -o uid,pid,ppid,comm,tty,stime,time "); char buf[1024]; FILE *p; char *cmd = "ps -f"; // 使用管道创建一个进程 // FILE *popen(const char *cmd, const char *type); type支持"r","w" 读写 // if ((p = popen(cmd, "r")) == NULL) { printf("popen error!"); exit(1); } // int pclose(File *stream); while(fgets(buf, sizeof(buf), p)) { printf("%s", buf); } pclose(p); pid_t pid, ppid, pgid; // pid_t getpid(void); 获取pid pid = getpid(); // pid_t getppid(void); 获取ppid ppid = getppid(); // pid_t getpgid(pid_t pid); // 如果pid=0返回当前进程的组标识符,如果是其他值,返回指定pid的组标识符号 pgid = getpgid(0); // pid_t getpgrp(void); 相当于getpgid(0); uid_t uid = getuid(); // 获取该进程,进程组,以及用户的进程允许优先级 // int getpriority(int which, int who); // 函数有两个入参,which取值为PRIO_PROCESS,PRIO_PGRP,PRIO_USER; // 参数 who 根据which的值确定其具体含义,分别对应进程标识符,进程组标识符,或用户标识符 // 函数运行成功返回当前进程的优先级,其值介于-20~20之间,值越小代表优先级越高。 // 如果函数执行出错返回-1,错误原因存在变量errno之中 int priority = getpriority(PRIO_USER, uid); // 我的uid是501,所以我填写501 printf("pid=%d,ppid=%d,pgid=%d,uid=%d,priority=%d\n", pid, ppid, pgid, uid, priority); return 0;}
wait 的使用
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <sys/time.h>#include <sched.h>int main(int argc, char **argv){ // 复制当前进程的内容,产生一个新的进程,调用fork函数的进程是父进程,所产生的进程是子进程 // pid_t fork(void); pid_t pid = fork(); int sta, i = -1; if (pid < 0) { printf("创建子进程失败!\n"); exit(1); } if (pid == 0) { printf("这是一个子进程 pid=%d\n", getpid()); // 使用这种方式,子进程是正常结束的 // exit(100); // 使用这个的时候,父进程获取子进程结束状态时,会知道子进程是因为信号终止的 abort(); } else { printf("父进程pid=%d\n", getpid()); // 暂停1s // unsigned int sleep(unsigned int seconds); sleep(1); printf("这是一个父进程\n"); // pid_t wait(int *status); 函数会暂停当前进程的运行。直到有信号到来或子进程结束 // 如果调用时子进程已经结束,则函数立刻返回,返回值为子进程pid,子进程结束状态由*status返回。 if (pid != wait(&sta)) { printf("等待子进程错误。\n"); exit(1); } if (WIFSIGNALED(sta)) { // WIFSIGNALED 如果子进程是因为信号而结束的,返回真 i = WTERMSIG(sta); // 如果WIFSIGNALED(status)返回值为真,返回引起终止的信号代码 printf("子进程是由信号结束的\n"); } // WIFEXITED 正常结束返回非0值 if (WIFEXITED(sta)) { // 判断进程是否为正常结束 i = WEXITSTATUS(sta); // 返回状态值 WEXITSTATUS(sta)状态为真时,返回状态值低8位 printf("子进程是正常结束的\n"); } // 输出子进程标识符 printf("子进程pid=%d\n", pid); // 输出子进程结束状态值 printf("exit status=%d\n", i); // exit status=0 } return 0;}
waitpid 的使用
#include <stdio.h>#include <stdlib.h>#include <unistd.h>//waitpid()使用示例int main(void){ pid_t pid, ret; int status = -1; printf("main process pid = %d\n", getpid()); pid = fork(); //创建子进程 if (pid < 0) { printf("error fork\n"); exit(1); } if (pid == 0) //子进程 { printf("child process pid = %d\n", getpid()); printf("------------------\n"); sleep(5); exit(0); } if (pid > 0) //父进程 { // pid_t waitpid(pid_t pid, int *status, int option); // 这里的pid_t pid 为欲要等待的子进程标识符 // option 的取值范围 // WNOHANG 如果没有任何已经结束的子进程,则立即返回 // WUNTRACED 如果子程序进入暂停状态,则立即返回 // waitpid(...)和wait(...)函数的区别,wait阻塞调用进程直到至少有一个子进程结束,waitpid则可以选择不阻塞立即返回 printf("这是父进程"); // waitpid(pid, &sta, WNOHANG); // 这里没有阻断 // waitpid(pid, &sta, WUNTRACED); // 这里阻断,先执行子进程在执行父进程 do { // 调用waitpid,且父进程不阻塞 ret = waitpid(pid, &status, WNOHANG); //若子进程还没有退出,则父进程暂停1s //等待时候执行下面语句 if (ret == 0) { printf("the child process has no exited\n"); sleep(1); } if (WIFSTOPPED(status)) { int i = WSTOPSIG(status); // WIFSTOPPED结果为真 printf("子进程处于暂停状态 status=%d\n", status); } } while (ret == 0); //子进程退出 if (pid == ret) //ret>0 { printf("child process exited\n"); } else { printf("some error occured.\n"); } } /* main process pid = 26713 the child process has no exited child process pid = 26714 ------------------ 子进程处于暂停状态 status=-1 the child process has no exited 子进程处于暂停状态 status=-1 the child process has no exited 子进程处于暂停状态 status=-1 the child process has no exited 子进程处于暂停状态 status=-1 the child process has no exited 子进程处于暂停状态 status=-1 child process exited */}
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <sys/time.h>#include <sched.h>int main(int argc, char **argv){ // 复制当前进程的内容,产生一个新的进程,调用fork函数的进程是父进程,所产生的进程是子进程 // pid_t fork(void); pid_t pid = fork(); int sta, i = -1; pid_t temp = 0; if (pid < 0) { printf("创建子进程失败!\n"); exit(1); } if (pid == 0) { printf("这是一个子进程 pid=%d\n", getpid()); sleep(5); // 这里进程暂停了,但是没有切换父进程执行 printf("这是一个子进程 pid=%d\n", getpid()); exit(0); } else { printf("这是父进程\n"); temp = waitpid(pid, &sta, WUNTRACED); // 这里堵塞当前进程,必须等待子程序结束 printf("%d\n", temp); if (pid != temp) { printf("等待子进程错误。\n"); exit(1); } // 如果子进程处于暂停执行情况则此宏值为真。一般只有使用 WUNTRACED 时才会有此情况。 if (WIFSTOPPED(sta)) { // WIFSTOPPED 如果子程序处于暂停状态,返回值为真 i = WSTOPSIG(sta); // WIFSTOPPED结果为真 printf("子进程处于暂停状态"); } // 输出子进程标识符 printf("子进程pid=%d\n", pid); // 输出子进程结束状态值 printf("exit status=%d\n", i); // exit status=0 } /* 这是父进程 这是一个子进程 pid=27169 这是一个子进程 pid=27169 27169 子进程pid=27169 exit status=-1 */ return 0;}
写时拷贝
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <sys/time.h>#include <sched.h>int i = 1;void child_process() { printf("子进程:%d i=%d \n", getpid(), i); // 输出1 i++; // 当它改变了i的值时,会有进行写时拷贝,会有自己的i变量,不会影响父进程 printf("子进程:%d i=%d \n", getpid(), i); // 输出2}int main(int argc, char **argv){ pid_t pid; int status; // 复制进程,但是它会有自己的空间 pid = fork(); if (pid < 0) { printf("创建进程失败"); exit(1); } if (pid == 0) { child_process(); } else { sleep(1); printf("父进程:%d i=%d \n", getpid(), i); // 输出1 if (pid != wait(&status)) { printf("等待子进程失败"); exit(1); } } return 0;}
exec
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <sys/time.h>#include <sched.h>int main(int argc, char **argv){ // vfork函数与fork函数的区别,vfork保证子进程先运行,在他exec或exit之后,父进程才可能被调度 // 如果在调用这两个函数之前,子进程的执行依赖父进程的进一步执行,则会导致死锁。 // pid_t pid = vfork(); vfork已经被废弃了,要求使用。 // warning: 'vfork' is deprecated: Use posix_spawn or fork [-Wdeprecated-declarations] execlp("ps", "ps", "-o", "pid,ppid,comm", NULL); // exec 函数有6种不同形式,统称exec函数族,他们一般形式为: // 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, const char *arv[], ...); // int execvp(const char *file, const char *arv[], ...); // int execve(const char *path, const char *arv[], ..., char *const envp[]); // char *argv[] = {"full path", "param1", "param2", ..., NULL}; // char *envp[] = {"name1=val1", "name2=val2", ..., NULL}; // envp 指定新环境的环境变量 return 0;}
终止进程
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/int main(int argc, char **argv){ printf("Linux C\n"); printf("特别棒"); // exit(1); // void _exit(int status); 他直接使进程停止运行,清除其使用的内存空间 // exit函数是在_exit函数上进行了一些包装,它还会检查文件打开状态,清理I/O缓冲操作等,例如把文件缓冲系统的内容写回文件。 _exit(1); // abort异常终止进程函数 return 0;}
守护进程
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <signal.h>#include <sys/types.h>#include <sys/stat.h>int main(int argc, char **argv){ // 创建守护进程 // 将子进程放入后台运行,就说创建守护进程的第一步,在创建过程中调用fork函数创建一个子进程,并把父进程杀死, // 使子进程进入后台执行,进而断开子进程与控制终端的关联 // 在Linux中,父进程先于子进程退出会使子进程成为孤儿进程,而当系统发现孤儿进程之后,会自动由init进程(PID为1)收养它, // 这样原先子进程就会变成init的子进程 pid_t pid; FILE *fp; char *buf = "这是一个示例程序\n"; pid = fork(); if (pid < 0) { printf("创建子进程失败"); exit(1); } if (pid > 0) { // 结束父进程 exit(0); } // pid_t setsid(void); // 创建新会话 setsid(); int filesize = 1024; int i = 0; for (i = 0; i < filesize; i++) { // 子进程会继承父进程打开的文件描述符,在这里子进程全部关掉 close(i); } // 切换目录 chdir("/tmp"); // 重设文件权限掩码 // 守护进程的文件掩码是从创建它的父进程哪里继承下来的,这会给守护进程使用文件带来诸多麻烦。为此, // 有必要将文件的权限掩码设置为0,进而增强守护进程的灵活性。 umask(0); // 处理SIGCHLD信号 // 对于某些进程,尤其是服务器进程,往往在请求到来时,创建子进程处理请求,而父进程则保持循环, // 以接收更多客户连接,在这种情况下,如果父进程等待子进程结束,将增加父进程的负担, // 进而会影响服务器的并发性能;如果父进程不等待子进程结束,子进程将会变成僵尸进程(zombie),继续占有系统资源。 // 在Linux系统中,有几种方式可以绕过这些问题,其中最简单的方法就是,将SIGCHLD信号的操作设置为SIG_IGN signal(SIGCHLD, SIG_IGN); while(1) { fp = fopen("/tmp/test", "a"); if (fp == NULL) { perror("打开文件失败!"); exit(1); } fputs(buf, fp); fclose(fp); sleep(1); } return 0;}
守护进程,打印日志
#include <stdio.h>#include <stdlib.h>#include <unistd.h> /**包含相关函数头文件*/#include <signal.h>#include <sys/types.h>#include <sys/stat.h>#include <time.h>#include <syslog.h>int main(int argc, char **argv){ // 创建守护进程 // 将子进程放入后台运行,就说创建守护进程的第一步,在创建过程中调用fork函数创建一个子进程,并把父进程杀死, // 使子进程进入后台执行,进而断开子进程与控制终端的关联 // 在Linux中,父进程先于子进程退出会使子进程成为孤儿进程,而当系统发现孤儿进程之后,会自动由init进程(PID为1)收养它, // 这样原先子进程就会变成init的子进程 pid_t pid; FILE *fp; char *buf = "这是一个示例程序\n"; pid = fork(); if (pid < 0) { printf("创建子进程失败"); exit(1); } if (pid > 0) { // 结束父进程 exit(0); } // pid_t setsid(void); // 创建新会话 setsid(); int filesize = 1024; int i = 0; for (i = 0; i < filesize; i++) { // 子进程会继承父进程打开的文件描述符,在这里子进程全部关掉 close(i); } // 切换目录 chdir("/"); // 重设文件权限掩码 // 守护进程的文件掩码是从创建它的父进程哪里继承下来的,这会给守护进程使用文件带来诸多麻烦。为此, // 有必要将文件的权限掩码设置为0,进而增强守护进程的灵活性。 umask(0); // 处理SIGCHLD信号 // 对于某些进程,尤其是服务器进程,往往在请求到来时,创建子进程处理请求,而父进程则保持循环, // 以接收更多客户连接,在这种情况下,如果父进程等待子进程结束,将增加父进程的负担, // 进而会影响服务器的并发性能;如果父进程不等待子进程结束,子进程将会变成僵尸进程(zombie),继续占有系统资源。 // 在Linux系统中,有几种方式可以绕过这些问题,其中最简单的方法就是,将SIGCHLD信号的操作设置为SIG_IGN signal(SIGCHLD, SIG_IGN); time_t now; while(1) { time(&now); // void syslog(int priority, char *format, ...) // 写入到/var/log/syslog文件中 syslog(LOG_USER|LOG_INFO, "Current time:\t%s\t\t\n", ctime(&now)); sleep(1); } return 0;}
进程通信
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <sys/wait.h>#define BUFSIZE 256int main(int argc, char **argv){ // 父进程向子进程,单向传递数据 pid_t cpid; int fifod[2]; int sta; char buf[BUFSIZE] = "Linux C is very good!"; // 创建匿名管道 // int pipe(int fd[2]); if (pipe(fifod) < 0) { printf("pipe error.\n"); exit(1); } cpid = fork(); if (cpid < 0) { printf("fork error.\n"); exit(1); } if (cpid == 0) { close(fifod[0]); // 写入时,要关闭读端 write(fifod[1], buf, sizeof(buf)); } else { close(fifod[1]); read(fifod[0], buf, sizeof(buf)); // 读取时,要关闭写端 printf("buf=%s\n", buf); if (cpid != wait(&sta)) { printf("wait error.\n"); exit(1); } } return 0;}