C语言进程

目录

目录

一、进程环境

1.虚拟地址空间

2.环境表

 3.进程表项

4.跨函数跳转

 5.进程资源限制

二.进程控制

 1.属性

 2.创建

 3.终止

 4.收尸

 5.注册终止处理程序

 6.替换 

 三.进程间通信

1.管道 

 2.xsi

四.守护进程

1.特点

2.创建

3.日志

4.单实例

五.进程间的关系

 1.会话

2.进程组

3.进程


C语言进程_第1张图片

一、进程环境

C语言进程_第2张图片

1.虚拟地址空间

  • 栈:局部变量,函数的形参;未初始化值随机;生命周期随函数的调用结束而结束
  • 堆:用户自行申请和释放,动态开辟
  • bss段:未初始化的局部静态变量和全局变量;初始值为0
  • data段:已初始化的局部静态变量和全局变量;生命周期从定义开始到进程结束
  • 文本段

2.环境表

 extern char **environ

#include 
#include 
 extern char **environ;
 int main(int argc,char *argv[],char *environ[])
 {
     int i;
     for(i=0;environ[i];i++)
         puts(environ[i]);
     exit(0);
 }

 3.进程表项

C语言进程_第3张图片

4.跨函数跳转

#include 
int setjmp(jmp_buf env);

setjmp 函数的功能是将函数在此处的上下文保存在 jmp_buf 结构体中,以供 longjmp 从此结构体中恢复。

  • 参数 env 即为保存上下文的 jmp_buf 结构体变量;
  • 如果直接调用该函数,返回值为 0; 若该函数从 longjmp 调用返回,返回值为非零,由 longjmp 函数提供。根据函数的返回值,我们就可以知道 setjmp 函数调用是第一次直接调用,还是由其它地方跳转过来的。
void longjmp(jmp_buf env, int val);

longjmp 函数的功能是从 jmp_buf 结构体中恢复由 setjmp 函数保存的上下文,该函数不返回,而是从 setjmp 函数中返回。

  • 参数 env 是由 setjmp 函数保存过的上下文。
  • 参数 val 表示从 longjmp 函数传递给 setjmp 函数的返回值,如果 val 值为0, setjmp 将会返回1,否则返回 val。
  • longjmp 不直接返回,而是从 setjmp 函数中返回,longjmp 执行完之后,程序就像刚从 setjmp 函数返回一样。

#include 
#include 
#include 
static jmp_buf env;
int div2num (int n1,int n2)
{
    if(n2==0)
        longjmp(env,1);//第二个参数随意,按1处理,longjmp带回的返回值
    return n1/n2;
}
int main()
{
    int n1,n2;
    if(setjmp(env)==0)//规定第一次返回值为0
        printf("请输入俩个整形变量值\n");
    else
        printf("请重新输入\n");
//  if(setjmp(env)!=0)
//      printf("跳转2");

    scanf("%d%d",&n1,&n2);
    printf("res:%d/%d=%d\n",n1,n2,div2num(n1,n2));
    exit(0);
}
                                                                         
                                                                         
                                                                         

 5.进程资源限制

命令:ulimit -a

llj@llj-virtual-machine:~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7641
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7641
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

 函数:

#include 
int getrlimit( int resource, struct rlimit *rlptr );//函数查询资源限制
int setrlimit( int resource, const struct rlimit *rlptr );//函数更改资源限制
//两个函数返回值:若成功则返回0,若出错则返回非0值
struct rlimit {
    rlim_t    rlim_cur;    /* 当前限制 */
    rlim_t    rlim_max;    /* 最大值 */
};

 在更改资源限制时,须遵循下列三条规则

  • 任何一个进程都可将一个软限制值更改为小于或等于其硬限制值
  • 任何一个进程都可降低其硬限制值,但它必须大于或等于其软限制值。这种降低对普通用户而言是不可逆的
  • 只有超级用户进程可以提高硬限制值

常量RLIM_INFINITY指定了一个无限量的限制。

这两个函数的resource参数取下列值之一:

RLIMIT_AS                进程可用存储区的最大总长度(字节)。这回影响sbrk函数和mmap函数。

RLIMIT_CORE           core文件的最大字节数,若其值为0则阻止创建core文件。

RLIMIT_CPU             CPU时间的最大量值(秒),当超过此软限制时,向该进程发送SIGXCPU信号。

RLIMIT_DATA            数据段的最大字节长度。这是初始化数据、非初始化数据以及堆的总和。

RLIMIT_FSIZE           可以创建的文件的最大字节长度。当超过此软限制时,则向该进程发送SIGXFSZ信号。

RLIMIT_LOCKS          一个进程可持有的文件锁的最大数(此数也包括Linux特有的文件租借数)。

RLIMIT_MEMLOCK     一个进程使用mlock(2)能够锁定在存储器中的最大字节长度。

RLIMIT_NOFILE         每个进程能打开的最大文件数。更改此限制将影响到sysconf函数在参数_SC_OPEN_MAX中的返回值。

RLIMIT_NPROC          每个实际用户ID可拥有的最大子进程数。更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX中返回的值。

RLIMIT_RSS               最大驻内存集的字节长度(resident set size in bytes, RSS)。如果物理存储器供不应求,则内核将从进程处取回超过RSS的部分。

RLIMIT_SBSIZE          用户在任一给定时刻可以占用的套接字缓冲区的最大长度(字节)。(Linux 2.4.22不支持)

RLIMIT_STACK           栈的最大字节长度。

RLIMIT_VMEM           这是RLIMIT_AS的同义词。

二.进程控制

C语言进程_第4张图片

 1.属性

  • pid_t:pid_t类似一个类型,就像int型一样,int型定义的变量都是整型的,pid_t定义的类型都是进程号类型
  • getpid: 得到进程的id
  • getppid:得到父进程的id

 2.创建

fork

  • 由进程复制调用进程使用
  • 复制的环境:进程表项,环境表,根目录,当前目录,资源限制,文件描述符,共享内存,存储映射,信号量,管道,消息队列,终端,缓存区,文件屏蔽字,用户id,进程组id,会话id,虚拟地址空间
  • 不复制的环境:pid,ppid,文件锁,时间,fork的返回值
  • 应用:1.网络模型:父进程创建子进程之后,父子进程分别执行不样的代码段                                     2.shell:子进程用新的进程替换

vfork

  • 阻塞调用进程,保证子进程调用完
  • 父子进程共用一个虚拟地址空间 

 3.终止

 正常终止

  • 从main函数返回
  • 调用exit
  • 调用_exit和_EXIT
  • 最后一个线程从启动历程返回
  • 最后一个线程调用pthread_exit

异常终止

  • 调用abort
  •  接到一个信号并终止
  • 最后一个线程对取消请求做出响应

 4.收尸

wait(NULL):为任意一个进程收尸

waitpid

  • pid<-1:为以pid的绝对值为进程组号中的任意一个进程收尸
  • pid=-1:为任意一个收尸
  • pid=0:在当前进程组中任意收一个尸
  • pid>0:为进程id和pid相等的子进程收尸 

 5.注册终止处理程序

 #include 

 int on_exit(void (*function)(int , void *), void *arg);
 #include 

 int atexit(void (*function)(void));

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void f1()
{
	puts("f1() is working.");
}
void f2()
{
	puts("f2() is working.");
}
void f3()
{
	puts("f3() is working.");
}

int main()
{

	puts("Begin!");

	atexit(f1);
	atexit(f2);
	atexit(f3);
	
	puts("End!");

	exit(0);
}
/*执行结果
llj@llj-virtual-machine:~/teacherqin/coder210714/apue/sys$ ./atexit 
Begin!
End!
f3() is working.
f2() is working.
f1() is working.
*/

 6.替换 

int execl(const char *path, const char *arg, ...
                       /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);
int execle(const char *path, const char *arg, ...
                       /*, (char *) NULL, 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[]);
  •  execl:可执行文件的路径,可执行文件和其参数
  • execlp:可执行文件名,可执行文件和其参数
  • execv:可执行文件的路径,可执行文件和其参数组成的数组
  • execvp:可执行文件名,可执行文件和其参数组成的数组
  • execvpe:可执行文件名,可执行文件和其参数的数组,环境变量
  • execle:可执行文件的路径,可执行文件和其参数,环境变量
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
	printf("[%d]:begin!\n",getpid());
	fflush(NULL);//解决缓存区带来的问题

	pid_t pid;
	pid=fork();
	if(pid < 0)
	{
		perror("fork()");
		exit(1);
	}
	if(pid == 0)
	{
		//execl("/bin/ls","ls","-l",NULL);
		//execlp("ls","ls","-l",NULL);
		char *arr[]={"ls","-l",NULL};
		//execv("/bin/ls",arr);
		execvp("ls",arr);
//.............................替换功能,成功不返回,失败返回
		perror("execl()");
		exit(1);
	}
	wait(NULL);
	exit(0);
}

 三.进程间通信

C语言进程_第5张图片

1.管道 

匿名管道

  • 有亲缘关系间通信
  • 有读写两端
  • 半双工
  • 由pipe创建
  • 管道有容量 6536
  • 读空管道会阻塞
  • 所有读端关闭,写端异常
  • 写读端关闭的管道,写满后阻塞
  • 读写端关闭的管道,返回0
  • 不能设置文件偏移量seek
​
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define BUFSIZE  128
static int mycpy(int fd1,int fd2);

int main(int argc,char *argv[])
{
	int pfd[2];
	pid_t pid;
	int fd;
	int cnt1,cnt2;
	int err,err2;
	char buf1[BUFSIZE];
	char buf2[BUFSIZE];
	if(pipe(pfd) == -1)
	{
		perror("pipe()");
		exit(1);
	}
	pid=fork();
	if(pid == -1)
	{
		perror("fork()");
		exit(0);
	}
	if(pid == 0)//子进程
	{
		close(pfd[1]);
		err=mycpy(pfd[0],1);
		if(err < 0)
		{
			fprintf(stderr,"%s",strerror(-err));
			exit(1);
		}
		exit(0);
	}

	
	//父
	close(pfd[0]);
	fd=open(argv[1],O_RDONLY);
	if(fd == -1)
	{
		perror("open()");
		close(pfd[1]);
		exit(1);
	}
	err2=mycpy(fd,pfd[1]);
	if(err2 < 0)
	{
		fprintf(stderr,"%s",strerror(-err2));
		exit(1);
	}
	close(pfd[1]);
	wait(NULL);
	exit(0);
}

static int mycpy(int fd1,int fd2)
{
	int fd;
	int cnt;
	char buf[BUFSIZE];
	while(1)
	{
		cnt=read(fd1,buf,BUFSIZE);
		if(cnt == -1)
		{
			close(fd1);
			return -errno;
		}
		if(cnt == 0)
		{
			close(fd1);
			return 0;
		}
		write(fd2,buf,cnt);
	}
}
			

​

命名管道

//进程1
llj@llj-virtual-machine:~/apueqin/few$ cd /tmp
llj@llj-virtual-machine:/tmp$ mkfifo myfifo_name
llj@llj-virtual-machine:/tmp$ date >myfifo_name 
llj@llj-virtual-machine:/tmp$ 

//进程2
llj@llj-virtual-machine:~/apue/ipc/pipe$ cd /tmp
llj@llj-virtual-machine:/tmp$ cat myfifo_name 
2021年 09月 28日 星期二 23:30:47 CST
llj@llj-virtual-machine:/tmp$ 

//注:两个进程要在同一路径下

 2.xsi

  • msg消息队列:数据交互
//proto.h 协议 

#ifndef _PROTO_H
#define _PROTO_H
#define PATHNAME    "/etc/shadow"
#define PATHPID      1
#define NAMESIZE    32
struct std_st
{
    char name[NAMESIZE];
    int pid;
};
struct msgbuf
{
    long mtype;
    struct std_st stu;
};
#endif

//send.c 1号进程发送

 #include 
 #include 
 #include "proto.h"
 int main(int argc,char *argv[])
 {
     key_t key;
     int msgid;
     key=ftok(PATHNAME,PATHPID);
     if(key == -1)
     {
         perror("ftok()");
         exit(1);
     }
     msgid=msgget(key,IPC_CREAT|IPC_EXCL|0600);
    if(msgid == -1)
     {
         if(errno == EEXIST)
         {
             msgid=msgget(key,0);
         }
         else
         {
         perror("msgget()");
         exit(1);
         }
     }
    struct msgbuf sndbuf;
    strncpy(sndbuf.stu.name,argv[2],NAMESIZE);
    sndbuf.stu.pid=atoi(argv[1]);
    sndbuf.mtype=atoi(argv[3]);
    msgsnd(msgid,&sndbuf,sizeof(struct std_st),0);
    exit(0);
 }

//recv.c 2号进程接收

#include 
#include 
#include 
#include 
#include 
#include 
#include "proto.h"
int main()
{
    struct msgbuf rcvbuf;
//.......................................................
    key_t key;
    int msgid;
    key=ftok(PATHNAME,PATHPID);
    if(key == -1)
    {
        perror("ftok()");
        exit(1);
    }
    msgid=msgget(key,IPC_CREAT|IPC_EXCL|0600);
   if(msgid == -1)
    {
        if(errno == EEXIST)
        {
            msgid=msgget(key,0);
        }
        else
        {
        perror("msgget()");
        exit(1);
        }
    }
//.......................................................建立消息队列 
 // struct msgbuf rcvbuf;   
    while(1)
    {
        if(msgrcv(msgid,&rcvbuf,sizeof(struct std_st),0,0) == -1)
        {
            perror("msgrcv()");
            goto ERROR;
                                     perror("msgrcv()");
             goto ERROR;
         }
         fprintf(stdout,"%s",rcvbuf.stu.name);
         printf("%d\n",rcvbuf.stu.pid);
     }
 
     msgctl(msgid,IPC_RMID,NULL);
     exit(0);
 ERROR:
     msgctl(msgid,IPC_RMID,NULL);
     exit(1);
 }
 
 
                                           
  • shm共享内存:共享内存空
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define SHMSIZE   1024
int main()
{
	pid_t pid;
	int shmid;
	char *str;
	shmid=shmget(IPC_PRIVATE,SHMSIZE,IPC_CREAT|IPC_EXCL|0600);
	if(shmid == -1)
	{
		if(errno == EEXIST)
			shmid=shmget(IPC_PRIVATE,SHMSIZE,0);//创建共享内存
		else
		{
			perror("shmget()");
			shmctl(shmid,IPC_RMID,NULL);
			exit(-1);
		}
	}
	pid=fork();
	if(pid == -1)
	{
		perror("fork()");
		exit(0);
	}
	if(pid == 0)//send
	{
		str=shmat(shmid,NULL,0);//映射,NULL,由内核分配空间
		memcpy(str,"good afternoon ",14);
		shmdt(str);//解除映射
		exit(0);
	}
	wait(NULL);

	str=shmat(shmid,NULL,0);
	puts(str);
//	sleep(20);
	shmdt(str);
	shmctl(shmid,IPC_RMID,NULL);
	exit(0);
}
  • sem信号量:多进程资源竞争
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE	64

static int semid;

int childJob();
int main(void)
{
	pid_t pid;
	int i;

	semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);//建立信号集,1个数组,得到信号集的id
	if (-1 == semid) {
		perror("semget()");
		exit(1);
	}

	semctl(semid, 0, SETVAL, 1);//将下标为0的信号集的信号设置为一个

	for (i = 0; i < 10; i++) {
		pid = fork();
		if (-1 == pid) {
			perror("fork()");
			exit(1);
		}
		if (pid == 0) {
			childJob();	
			exit(0);
		}
	}

	for (i = 0; i < 10; i++)
		wait(NULL);

	semctl(semid, 0, IPC_RMID, NULL);//消除信号,下标为0的
	exit(0);
}

static int P()
{
	struct sembuf buf;

	buf.sem_num = 0;//信号集里的成员
	buf.sem_op = -1;//取一个信号
	buf.sem_flg = 0;//特殊要求

	semop(semid, &buf, 1);
}

static int V()
{
	struct sembuf buf;

	buf.sem_num = 0;
	buf.sem_op = 1;//放一个信号
	buf.sem_flg = 0;

	semop(semid, &buf, 1);
}

int childJob()
{
	FILE *fp;
	char buf[BUFSIZE] = {};

	fp = fopen("./test.txt", "r+");

	// 竞争
	// 拿信号
	P();
	// io;
	fgets(buf, BUFSIZE, fp);
	rewind(fp);//将指针偏移量,设置在开头
//	sleep(1);
	fprintf(fp, "%d", atoi(buf)+1);
	fclose(fp); // buf
	V();

	return 0;
}

四.守护进程

C语言进程_第6张图片

1.特点

  • pid=pgid=sid,脱离控制终端

2.创建

  • umask(0);
  • fork一个子进程,父进程exit
  • setsid创建会话
  • chdir("/")切换到根路径
  • 重定向0,1,2,文件描述符到/dev/null(或关闭)
  • 关闭不用的文件描述符(getrlimit)
//创建守护进程
static int mydaemon(void)
{
	pid_t pid;
	struct rlimit limit;
	int fd;
	umask(0);//...............................设置屏蔽字0

	pid=fork();
	if(pid == -1)
	{
		return -errno;
		exit(0);
	}
	if( pid > 0)
		exit(0);//............................fork子进程,符进程exit

	setsid();//...............................建立会话

	chdir("/");//.............................切换到根目录

	fd=open("/dev/null",O_WRONLY);
	if(fd == -1)
	{
		return -errno;
		exit(1);
	}
	dup2(fd,0);
	dup2(fd,1);
	dup2(fd,2);//.............................将0,1,2文件描述符重定向

	getrlimit(RLIMIT_NOFILE,&limit);
	for(int i=0;i

3.日志

  • openlog:建立连接
  • syslog:发送日志
  • closelog:关闭连接
  • 日志文件的地址:/var/null/syslog
int main()
{
	int err,err2;
	time_t tme;
	openlog("mydaemon",LOG_PID,LOG_DAEMON);//.................写日志,建立连接
	err=mydaemon();//创建守护进程的接口函数
	if(err < 0)
	{
		syslog(LOG_ERR,"mydaemon():%s",strerror(-err));//.........发送给syslog,syslog写日志
		exit(1);
	}
	err2=runningInit();//单实例接口函数
	if(err2 < 0)
	{
		syslog(LOG_ERR,"runningInit():%s",strerror(-err2));
		exit(-1);
	}
	while(1)
	{
		system("ps aux > /text.log");
		syslog(LOG_INFO,"mydaemon was sucessful");
		sleep(10);
	}
	closelog();//...................................................断开连接
	exit(0);
}

4.单实例

  • 同时只能运行一次
  • lockf/flock文件加互斥锁
  • 单实例运行路径:/var/run/xxx.pid
//单实例
static int runningInit(void)
{
	int fd;
	char buf[BUFSIZE];
	fd=open(LOCKFILE,O_WRONLY|O_CREAT,0666);//打开单实例文件
	if(fd == -1)
	{
		return -errno;
	}
	if(lockf(fd,F_TLOCK,0) == -1)//上
	{
		if(errno == EACCES ||errno == EAGAIN)
		{
			close(fd);
			return -errno;
		}
		close(fd);
		return -errno;
	}
	//第一次
	ftruncate(fd,0);//.........................清空文件,截断
	snprintf(buf,BUFSIZE,"%d\n",getpid());
	write(fd,buf,strlen(buf)+1);//.............将pid写入单实例文件
	return 0;
}

五.进程间的关系

C语言进程_第7张图片

 1.会话

  • 非组长进程创建
  • getsid
  • setsid

2.进程组

  • getpgid
  • getgrp
  • setpgid
  • setgrp

3.进程

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
	pid_t pid;
	pid=fork();
	if(pid < 0)
	{
		perror("fork()");
		exit(1);
	}
	if(pid == 0)
	{
		printf("[child] pid:%d,pgid:%d,ppid:%d,sid:%d\n",getpid(),getpgrp(),getppid(),getsid(0));
		exit(0);
	}
	wait (NULL);
	printf("[father] pid:%d,pgid:%d,ppid:%d,sid:%d\n",getpid(),getpgrp(),getppid(),getsid(0));
	exit(0);
}

你可能感兴趣的:(c语言)