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 256
int 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;
}