Linux 进程

一 环境变量

1 常见环境变量

#/home/ypw/bin:/home/ypw/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
PATH 
# /home/ypw
HOME 
# /bin/bash
SHELL 
# xterm-256color
TERM 
# en_US.UTF-8
LANG

2 程序获取环境变量

#include<stdio.h>
//导入存放环境变量的数组
extern char** environ;

int main(){
    //NULL作为哨兵结尾
	for(int i=0 ; environ[i]!=NULL ; i++){
		printf("%s\n",environ[i]);
	}
	return 0;
}

3 函数操作环境变量

  • 函数文档
char *getenv(const char *name);
//overwrite不为0重写
int setenv(const char *name, const char *value, int overwrite); 
int unsetenv(const char *name);
#include<stdio.h>
#include<stdlib.h>
// 获取环境变量,添加环境变量,删除环境变量
int main(){
    //获取环境变量
	const char* name = "VAR";
	char* val = getenv(name);
	printf("%s=%s\n",name,val);
	//添加环境变量
	setenv(name,"hello world",1);
	//获取环境变量
	val = getenv(name);
	printf("%s=%s\n",name,val);
	//删除环境变量
	int ret = unsetenv(name);
	printf("%d\n",ret);
	return 0;
}

二 进程控制

1 fork函数

  • 使用fork创建子进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
// 使用fork创建子进程,子进程的fork()返回值为0,父进程的fork()返回值为子进程pid
int main(){
	pid_t pid;
	printf("---before fork---\n");
	pid = fork();
	if(pid == -1){
		perror("fork:");
		exit(1);
	}else if(pid == 0){
		printf("child process: pid:%u,ppid:%u\n",getpid(),getppid());
	}else{
        sleep(1);
		printf("parent process: pid:%u,ppid:%u\n",getpid(),getppid());
	}
	printf("---after fork---\n");
	return 0;
}
  • 使用fork循环创建子进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
# 使用fork循环创建子进程
int main(){
	pid_t pid;
	printf("--before fork--\n");
	int i;
	for(i=0 ; i<5 ; i++){
		pid = fork();
		if(pid == -1){
			perror("fork: ");
			exit(1);
		}else if(pid == 0){
             // 避免子进程再创建子进程的子进程,需要跳出循环,表示子进程不再fork
			break;
		}
	}
	// 子进程
	if(i<5){
		sleep(i);
		printf("%dth child process ,pid=%u\n",i+1,getpid());
	}else{ // 父进程
		sleep(i);
		printf("parent process,pid=%u\n",getpid());
	}

	return 0;
}
# result
--before fork--
1th child process ,pid=60180
2th child process ,pid=60181
3th child process ,pid=60182
4th child process ,pid=60183
5th child process ,pid=60184
parent process,pid=60179
  • 进程共享

读时共享写时复制

  • gdb调试带有子进程的程序
# 默认跟踪父进程,在fork()函数调用之前设置,可以修改gdb跟踪的进程
set follow-fork-mode child
set follow-fork-mode parent

2 exec函数族

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec不会创建新进程,所有调用exec前后该进程的id未被改变。

exec函数一旦调用成功即立即执行新的程序,不返回。只有失败才返回,错误值为-1。故通常在exec函数调用后直接调用perror()和exit(),无需if判断

/*
	l(list):命令行参数列表
	p(path):搜索file时,使用path变量
	v(vector):使用命令行参数数组
	e(environment):使用环境变量数组
*/
int execl();
int execlp();
int execle();
int execv();
int execvp();
int execve();
  • execlp函数(加载一个进程,借助PATH环境变量)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// 在子进程中执行ls命令
int main(){
	pid_t pid;
    //创建子进程
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
        //子进程执行execlp函数
		execlp("ls","ls","-l","-a",NULL);
	}else{
		sleep(1);
		printf("parent\n");
	}
	return 0;
}
  • execl函数 (加载一个进程,通过 路径+函数名 加载)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// 在子进程中执行ls命令
int main(){
	pid_t pid;
    //创建子进程
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
        //子进程执行execl函数,可以执行自定义的程序
		execl("/bin/ls","ls","-l","-a",NULL);
	}else{
		sleep(1);
		printf("parent\n");
	}
	return 0;
}
  • execlp函数实现将当前系统的进程信息打印到文件中
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
//将当前系统的进程信息打印到文件中
int main(){
    //打开文件,只写,没有创建,有则截断
	int fd = open("ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);
	if(fd == -1){
		perror("open");
		exit(1);
	}
    //将fd的文件描述符复制到标准输出,标准输出写入到fd指向的文件
	dup2(fd,1);
    //执行ps
	execlp("ps","ps","aux",NULL);
	//成功不返回
    perror("ps");
	exit(1);
	//close(fd);
	return 0;
}

三 回收子进程

1 孤儿进程

孤儿进程是表示父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

由于我使用的环境是带有图形界面的ubuntu系统,所以最终并不是被我们所熟知的init进程收养,而是被一个名为/sbin/upstart的进程所收养。另外还可以观察到,该进程也是其他系统进程的父进程。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
//当父进程死亡后,子进程成为孤儿进程,被1685进程收养
int main(){
	pid_t pid;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
		while(1){
			sleep(1);
			printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
		}
	}else{
		sleep(3);
		printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
	}
	return 0;
}

2 僵尸进程

僵尸进程表示当进程终止后,父进程尚未回收,子进程残留资源(PCB)存放在内核中,变为僵尸进程。僵尸进程不能使用kill命令清除,因为kill只是用来终止进程的,而僵尸进程已经终止。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
	pid_t pid;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
			sleep(3);
			printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
	}else{
		while(1){
			sleep(1);
			printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
		}
	}
	return 0;
}
# result
ypw   62275  0.0  0.0   4352   656 pts/2    S+   19:46   0:00 ./zoombie
ypw   62276  0.0  0.0      0     0 pts/2    Z+   19:46   0:00 [zoombie] <defunct>

3 wait 函数

父进程调用wait函数可以回收子进程终止信息。三个功能:

  • 阻塞等待子进程退出
  • 回收子进程残留资源
  • 获取子进程结束状态(退出原因)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main(){
	
	pid_t pid,wpid;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
			sleep(3);
			printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
	}else{
		wpid = wait(NULL); //阻塞,等待子进程结束,回收该僵尸进程,成功返回子进程的pid
		if(wpid == -1){
			perror("wait error");
			exit(1);
		}
		while(1){
			sleep(1);
			printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
		}
	}
	return 0;
}

增强版,可以获取进程的状态信息

WIFEXITED(status):非零,进程正常结束
WEXITSTATUS(status):上面的宏为真,则使用该宏可以获取进程退出状态 (exit/return 的状态)
WIFSIGNALED(status):非零,进程异常终止
WTERMSIG(status):取得使进程终止的那个信号的编号 (例:kill -9 pid 获取到编号为9)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main(){
	
	pid_t pid,wpid;
	int status;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}else if(pid == 0){
			sleep(30);
			printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
			exit(76); // return 100;
	}else{
		wpid = wait(&status);
		if(WIFEXITED(status)){
			printf("return or exit success: %d\n",WEXITSTATUS(status));
		}
		if(WIFSIGNALED(status)){
			printf("kill by the number: %d\n",WTERMSIG(status));
		}
		if(wpid == -1){
			perror("wait error");
			exit(1);
		}
		while(1){
			sleep(1);
			printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
		}
	}
	return 0;
}

4 waitpid 函数

作用同wait,但可以指定清理的pid进程,可以非阻塞

// 参数:pid:>0 回收指定ID的子进程,-1 回收任意子进程(同wait)
// 返回:0,参数3为宏 WNOHANG,表示子进程正在运行
pid_t waitpid(pid_t pid, int *status, int options);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main(){

	pid_t pid,pid_three,wpid;
	int i,n=5;
	for(i=1 ; i<=n ; i++){
		pid = fork();
		if(pid == -1){
			perror("fork");
			exit(1);
		} else if(pid == 0 ){
			break;
		} else{
			if(i==3){ //当fork第三个进程时候,记录进程号
				pid_three = pid;
			}
		}
	}
	
	if(i == n+1){
		sleep(i);
//		pid_three = waitpid(pid_three,NULL,0); //阻塞回收第三个进程
//		printf("waitpid=%d\n",pid_three);
		do{ //循环回收全部进程
			wpid = waitpid(-1,NULL,WNOHANG); //非阻塞式
			if(wpid>0){
				n--;
			}
		}while(n>0);
		printf("wait finish...\n");
		printf("parent id=%d\n",getpid());
		while(1);
	}else{
		sleep(i);
		printf("child%d id=%d\n",i,getpid());
	}
	return 0;
}

四 进程间通信

1 Pipe管道

//功能:使用pipe实现,父子进程通信,父写数据,子读数据
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>

void sys_err(const char* str,int errno){
	perror(str);
	exit(errno);
}

int main(){

	int fd[2];
	pid_t pid;
	char str[1024] = "Hello World\n";
	char buf[1024];
	int len;

	if(pipe(fd)<0){ //创建管道 fd[0]读端 fd[1]写端
		sys_err("pipe error",1);
	}
	
	pid = fork(); //创建子进程
	if(pid == -1){
		sys_err("fork error",1);
	}else if(pid > 0){ //父进程
		close(fd[0]); //父关闭读
		write(fd[1],str,strlen(str)); //向管道写数据
		wait(NULL); //回收子进程
		close(fd[1]); 
	}else{ //子进程
		close(fd[1]); //子关闭写
		len = read(fd[0],buf,sizeof(buf)); //从管道读数据
		write(STDOUT_FILENO,buf,len); //将数据写到屏幕
		close(fd[0]);				
	}
	return 0;
}

2 fifo有名管道

# 借助fifo实现非血缘进程间通信
mkfifo myfifo
//扮演写的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void sys_err(const char* str,int errno){
	perror(str);
	exit(errno);
}

int main(){
	
	int fd;
	char* str = "Hello Fifo";
	fd = open("myfifo",O_WRONLY); //打开fifo管道
	if(fd == -1 ){
		sys_err("open error",1);
	}
	write(fd,str,strlen(str)); //向管道写数据
	close(fd);
	return 0;
}
//扮演读的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void sys_err(const char* str,int errno){
	perror(str);
	exit(errno);
}

int main(){
	
	int fd;
	char buf[1024];
	int len;

	fd = open("myfifo",O_RDONLY); //打开fifo管道
	if(fd == -1 ){
		sys_err("open error",1);
	}
	len = read(fd,buf,sizeof(buf)); //读数据
	write(STDOUT_FILENO,buf,len); 
	close(fd);
	return 0;
}

3 mmap内存共享映射

//借助mmap实现非血缘关系进程的通信
//扮演写的程序
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
#include<unistd.h>

#define MAPLEN 0x1000

struct Stu{ //定义传输的数据格式为结构体
	int id;
	char name[20];
	char sex;
};

void sys_err(const char* str, int errno){
	perror(str);
	exit(errno);
}

int main(int argc,char* argv[]){

	int fd,i=0;
	struct Stu* mm;

	if(argc < 2){
		printf("./a.out filename");
		exit(1);
	}

	fd = open(argv[1],O_RDWR | O_CREAT, 0777); //有则打开无则创建
	if(fd < 0 ){
		sys_err("open error",1);
	}

	if(lseek(fd,MAPLEN-1,SEEK_SET)<0){ //提供4KB大写的空文件
		sys_err("lseek error",2);
	}
	if(write(fd,"\0",1)<0){ //lseek写入空洞字符后,需要再追加写入一个字符
		sys_err("write error",3);
	}
	//内存共享映射
	mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
	if(mm == MAP_FAILED){ //映射失败
		sys_err("mmap error",4);
	}
	close(fd); //可以关闭文件描述符,已经建立映射故不影响

	while(1){ //循环向共享内存中写数据
		mm->id = i;
		sprintf(mm->name,"stu-%d",i);
		if(i%2==0)
			mm->sex = 'm';
		else
			mm->sex = 'w';
		i++;
		sleep(1);
	}
	munmap(mm,MAPLEN); //解绑映射
	return 0;
}

//扮演读的程序
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
#include<unistd.h>

#define MAPLEN 0x1000

struct Stu{
	int id;
	char name[20];
	char sex;
};

void sys_err(const char* str, int errno){
	perror(str);
	exit(errno);
}

int main(int argc,char* argv[]){

	int fd,i=0;
	struct Stu* mm;

	if(argc < 2){
		printf("./a.out filename");
		exit(1);
	}

	fd = open(argv[1],O_RDONLY); //只读
	if(fd < 0 ){
		sys_err("open error",1);
	}

	mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ , MAP_SHARED,fd,0);
	if(mm == MAP_FAILED){
		sys_err("mmap error",4);
	}
	close(fd);

	while(1){ //循环读取映射共享的数据
		printf("id:%d ",mm->id);
		printf("name:%s ",mm->name);
		printf("sex:%c\n",mm->sex);
		sleep(1);
	}
	munmap(mm,MAPLEN);
	return 0;
}

五 信号

1 kill函数

//使用kill函数模拟系统的kill命令,向目标进程pid发送信号
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<signal.h>
int main(int argc,char* argv[]){
	
	int ret;

	if(argc < 3){
		printf("./a.out signalNumber pid");
		exit(1);
	}
	
	ret = kill((pid_t)atoi(argv[2]),atoi(argv[1])); //发送信号,pid,signal
	if(ret < 0 ){
		perror("kill");
		exit(1);
	}
	return 0;
}

2 alarm函数

//定时器,运行规定的时间后自动终止进程
#include<stdio.h>
#include<unistd.h>

int main(){

	int count;

	alarm(1); //定时1s
	for(count = 0 ; 1 ; count++){
		printf("count=%d\n",count);
	}
	return 0;
}

3 信号集处理函数

//通过更改屏蔽字,屏蔽相应的信号
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void printsigset(const sigset_t* set){ //打印当前未决信号集
	int i;
	for(i=1 ; i<32 ; i++){
		if(sigismember(set,i)==1){ //判断i信号是否在未决信号集上激活
			putchar('1');
		}else{
			putchar('0');
		}
	}
	puts("");
}

int main(){
	
	sigset_t s,p;
	int i=0;	
	sigemptyset(&s); //置零
	sigaddset(&s,SIGINT); //SIGINT信号阻塞置1
	sigaddset(&s,SIGQUIT); //SIGQUIT信号阻塞置1
	sigprocmask(SIG_BLOCK,&s,NULL); //使用s来更改当前进程的信号屏蔽字

	while(1){
		sigpending(&p); //获取未决信号集
		printsigset(&p);
		sleep(1);
		if(i == 10){
			sigdelset(&s,SIGQUIT); //删除阻塞信号集的SIGQUIT信号,置零
			sigprocmask(SIG_UNBLOCK,&s,NULL); //将SIGINT信号置零放行SIGINT未决信号
		}
		i++;
	}	

	return 0;
}

4 信号捕捉

  • 给SIGINT信号设置捕捉函数
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){ //捕捉函数
	printf("I am do_sig function\n");
	printf("num=%d\n",num);
}

int main(){

	struct sigaction act; //定义捕捉的结构体

	act.sa_handler = do_sig; //添加捕捉函数
	sigemptyset(&act.sa_mask); //临时信号屏蔽字
	act.sa_flags = 0; //选定旧版本捕捉函数

	sigaction(SIGINT,&act,NULL); //给SIGINT信号添加捕捉函数

	while(1){
		printf("=============\n");
		sleep(1);
	}

	return 0;
}
  • 验证临时信号屏蔽字
    • 当在执行捕捉函数时,再发送SIGINT信号,则会只改变未决信号集置1,等待捕捉函数执行完毕,临时屏蔽字解除后,会被激活
    • 当在执行捕捉函数时,由于设置了对SIGQUIT信号的屏蔽,故执行阶段无法响应SIGQUIT信号
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
	int n=5;
	printf("I am do_sig function\n");
	while(n--){ //添加循环,增加捕捉函数的执行时间
		printf("num=%d\n",num);
		sleep(1);
	}
}

int main(){

	struct sigaction act;

	//act.sa_handler = SIG_DFL; //默认
	//act.sa_handler = SIG_IGN; //忽略
	act.sa_handler = do_sig;
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask,SIGQUIT); //对SIGQUIT信号临时屏蔽
	act.sa_flags = 0;

	sigaction(SIGINT,&act,NULL);

	while(1){
		printf("=============\n");
		sleep(1);
	}

	return 0;
}

5 signal 函数

//捕捉SIGUSR1信号
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
	printf("num=%d\n",num);
}

int main(){
	
	signal(SIGUSR1,do_sig);
	
	while(1){
		printf("***********\n");
		sleep(1);
	}
	
	return 0;
}

6 可重入函数

//将不可重入函数strtok更改为可重入函数strtok_r,用于对字符串的切割
#include<stdio.h>
#include<string.h>

int main(){
	
	char buf[] = "Hello Tom Jerry Smith";
	char *save = buf, *p;

	while((p = strtok_r(save," ",&save))!=NULL){
		printf("%s\n",p);
	}

	return 0;
}

7 时序京态

//自己实现sleep函数,需要考虑时序静态的发生,在alarm前,需要设置信号屏蔽字来屏蔽SIGALRM信号,之后再解除SIGALRM信号的阻塞,通过sigsuspend函数原子化操作实现信号唤醒挂起
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
	//nothing to do
}

unsigned int mysleep(unsigned int nsecs){
	struct sigaction act,oldact;
	sigset_t newmask,oldmask,susmask;
	unsigned int unslept;
	//给SIGALRM信号设置捕捉函数
	act.sa_handler = do_sig; 
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGALRM,&act,&oldact);
	//设置信号屏蔽字,屏蔽SIGALRM信号
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGALRM);
	sigprocmask(SIG_BLOCK,&newmask,&oldmask);
	//定时器
	alarm(nsecs);
	//解除SIGALRM信号的屏蔽
	susmask = oldmask;
	sigdelset(&susmask,SIGALRM);
	sigsuspend(&susmask); //挂起等待SIGALRM信号幻醒

	unslept = alarm(0);
	sigaction(SIGALRM,&oldact,NULL); //恢复SIGALRM信号默认响应状态
	sigprocmask(SIG_SETMASK,&oldmask,NULL); //恢复信号屏蔽字
	return unslept;
}


int main(){
	while(1){
		mysleep(2);
		printf("Two seconds passed\n");
	}

	return 0;
}

SIGCHLD信号处理

//通过对信号的处理实现清理僵尸进程
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>


void sys_err(const char* str,int errno){
	perror(str);
	exit(errno);
}
//捕捉函数
void do_sig_child(int num){
	int status;
	pid_t pid;
	while((pid = waitpid(0,&status,WNOHANG))>0){ //waitpid 处理一个僵尸进程
		if(WIFEXITED(status)){ //正常退出
			printf("child %d exit %d\n",pid,WEXITSTATUS(status));
		}else if(WIFSIGNALED(status)){ //异常退出
			printf("childe %d cancel signal %d\n",pid,WTERMSIG(status));
		}
	}
}

int main(){

	pid_t pid;
	int i;
	for(i=0 ; i<10 ; i++){ //fork 10个子进程
		pid = fork();
		if(pid < 0) {
			sys_err("fork",1);
		}else if(pid == 0 ){
			break;
		}
	}

	if(pid == 0 ){ //子进程
		int n = 15;
		while(n--){
			sleep(1);
			printf("child pid %d\n",getpid());
		}
		return i; //返回退出的值
	}else if(pid > 0){ //父进程
         //设置捕捉SIGCHLD信号
		struct sigaction act;
		act.sa_handler = do_sig_child;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		sigaction(SIGCHLD,&act,NULL);
		while(1){
			sleep(1);
			printf("parent pid %d\n",getpid());
		}
	}

	return 0;
}

六 进程间关系

1 进程组

//获取进程组gid的几种方式
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){

	pid_t pid;

	if((pid = fork())<0){
		perror("fork");
		exit(1);
	}else if(pid == 0) {
		printf("child pid:%d\n",getpid());
		printf("child pgid:%d\n",getpgrp()); //获取组id
		printf("child pgid:%d\n",getpgid(0));//获取组id
		printf("child pgid:%d\n",getpgid(getpid()));//获取组id
	}else{	
		sleep(3);
		printf("parent pid:%d\n",getpid());
		printf("parent pgid:%d\n",getpgid(0));//获取组id
	}
	return 0;
}
//改变进程的进程组id
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        printf("child process PID is %d\n",getpid());
        printf("Group ID of child is %d\n",getpgid(0)); // 返回组id
        sleep(5);
        printf("Group ID of child is changed to %d\n",getpgid(0));
        exit(0);
    }
    sleep(1);
    setpgid(pid,pid); // 父进程改变子进程的组id为子进程本身
    sleep(5);
    printf("parent process PID is %d\n",getpid());
    printf("parent of parent process PID is %d\n",getppid());
    printf("Group ID of parent is %d\n",getpgid(0));
    setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
    printf("Group ID of parent is changed to %d\n",getpgid(0));
    return 0;
}

2 会话设置

//子进程创建独立的会话,关闭当前终端不影响该会话的正常运行
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){

	pid_t pid;

	if((pid = fork())<0){
		perror("fork");
		exit(1);
	}else if(pid == 0 ){
		printf("child pid:%d\n",getpid());
		printf("child pgid:%d\n",getpgid(0));
		printf("child sid:%d\n",getsid(0));
		sleep(10);
		setsid(); //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
		printf("Changed\n");
		printf("child pid:%d\n",getpid());
		printf("child pgid:%d\n",getpgid(0));
		printf("child sid:%d\n",getsid(0));
		sleep(20);	
		exit(0);
	}

	return 0;
}

七 线程

1 pthread_create 创建线程

//创建线程,打印一些线程信息
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

void* do_thread_func(void* arg){
	int* p = (int*)arg;
	printf("thread running...\n");
	printf("thread pid:%d\n",getpid());
	printf("thread id:%x\n",(unsigned int)pthread_self());
	printf("thread arg:%d\n",*p);
}

int main(){
	
	pthread_t tid;
	int arg = 233;	
	
	pthread_create(&tid,NULL,do_thread_func,(void *)&arg); //创建线程
	
	printf("main pid:%d\n",getpid());
	printf("main thread id:%x\n",(unsigned int)pthread_self()); //main线程的tid
	printf("main child thread id:%x\n",(unsigned int)tid); //新线程的tid
	sleep(1);	
	return 0;
}

2 pthread_join 回收线程

//回收线程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

void* do_thread_func1(void* arg){
	printf("thread 1 running...\n");
	return (void*)1;
}

void* do_thread_func2(void* arg){
	printf("thread 2 running...\n");
	pthread_exit((void*)2);
}

void* do_thread_func3(void* arg){
	while(1){
		printf("thread 3 writing...\n");
		sleep(1);
	}
}

int main(){

	pthread_t tid;
	void* retval;

	pthread_create(&tid,NULL,do_thread_func1,NULL);
	pthread_join(tid,&retval);
	printf("thread 1 exit code:%d\n",(int*)retval);

	pthread_create(&tid,NULL,do_thread_func2,NULL);
	pthread_join(tid,&retval);	
	printf("thread 2 exit code:%d\n",(int*)retval);
	
	pthread_create(&tid,NULL,do_thread_func3,NULL);
	sleep(3);	
	pthread_cancel(tid);
	pthread_join(tid,&retval);
	printf("thread 3 exit code:%d\n",(int*)retval);

	return 0;
}

3 pthread_detached 分离线程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>

void* do_thread_func(void* arg){
	int n=3;
	while(n--){
		printf("thread running...\n");
		sleep(1);
	}
	return (void*)233;
}

int main(){

	pthread_t tid;
	int err;
	void *retval;
		
	pthread_create(&tid,NULL,do_thread_func,NULL);
	pthread_detach(tid); //分离线程,无法使用join回收,无法接收返回的参数
	while(1){
		err = pthread_join(tid,&retval);
		if(err != 0 ){
			printf("thread %s\n",strerror(err));
		}else{
			printf("thread exit code %d\n",(int)retval);
		}
		sleep(1);	
	}

	return 0;
}

4 设置线程属性

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>

#define SIZE 0x100000

void* do_thread_func(void* arg){
	int n = 3;
	while(n--){
//		printf("Hello thread\n");
		sleep(1);
	}
}

int main(){

	pthread_t tid;
	pthread_attr_t attr;
	int err,detachstate,i=1;
	void* stackaddr;
	size_t stacksize;
	
	pthread_attr_init(&attr);

	pthread_attr_getstack(&attr,&stackaddr,&stacksize);
	printf("origin stackaddr:%p\n",stackaddr);
	printf("origin stacksize:%d\n",(int)stacksize);
	
	pthread_attr_getdetachstate(&attr,&detachstate);
	if(detachstate == PTHREAD_CREATE_DETACHED){
		printf("thread detached\n");
	}else if(detachstate == PTHREAD_CREATE_JOINABLE){
		printf("thread joinable\n");
	}
	
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	while(1){
		stackaddr = malloc(SIZE);
		if(stackaddr == NULL){
			perror("malloc");
			exit(1);
		}
		stacksize = SIZE;
		pthread_attr_setstack(&attr,stackaddr,stacksize);
		err = pthread_create(&tid,&attr,do_thread_func,NULL);
		if(err != 0 ){
			printf("thread:%s\n",strerror(err));
			exit(1);
		}
		printf("%d\n",i++);
	}
	
	pthread_attr_destroy(&attr);
	return 0;
}

八 线程同步

1 互斥量

//通过互斥量mutex实现对临界区资源count的加锁访问,比卖你出现并发问题
#include<stdio.h>
#include<pthread.h>

#define LOOPNUM 5000
int count;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥量静态初始化

void* do_thread_func(void* arg){
	int i,val;
	for(i=0 ; i<LOOPNUM ; i++){
		pthread_mutex_lock(&mutex); //加锁访问临界资源
		val = count;
		printf("%x:%d\n",(unsigned int)pthread_self(),val+1);
		count = val+1;
		pthread_mutex_unlock(&mutex); //解锁临界资源
	}
	return NULL;
}

int main(){

	pthread_t tid1,tid2;
	
	pthread_create(&tid1,NULL,do_thread_func,NULL);
	pthread_create(&tid2,NULL,do_thread_func,NULL);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	return 0;
}

2 死锁

//线程一拥有A锁,请求获得B锁;线程二拥有B锁,请求获得A锁
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

char* str = "Hello World";
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void* do_thread_func1(void* arg){
	pthread_mutex_lock(&lock1);
	sleep(1);
	pthread_mutex_lock(&lock2);
	printf("%s\n",str);
	pthread_mutex_unlock(&lock2);
	pthread_mutex_unlock(&lock1);
	return NULL;
}

void* do_thread_func2(void* arg){
	pthread_mutex_lock(&lock2);
	sleep(2);
	pthread_mutex_lock(&lock1);
	printf("%s\n",str);
	pthread_mutex_unlock(&lock1);
	pthread_mutex_unlock(&lock2);
	return NULL;
}

int main(){

	pthread_t tid1,tid2;

	pthread_create(&tid1,NULL,do_thread_func1,NULL);
	pthread_create(&tid2,NULL,do_thread_func2,NULL);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	return 0;
}

3 读写锁

//3个线程不定时写同一全局资源,5个线程不定时读同一全局资源
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

int count;
pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;

void* do_write_func(void* arg){
	int t;
	while(1){
		usleep(100);
		pthread_rwlock_wrlock(&lock);
		t = count;
		usleep(100);
		printf("%x write: counter=%d,new counter=%d\n",(unsigned int)pthread_self(),t,++count);
		pthread_rwlock_unlock(&lock);
		usleep(100);
	}
	return NULL;
}

void* do_read_func(void* arg){
	while(1){
		pthread_rwlock_rdlock(&lock);
		printf("%x read: counter=%d\n",(unsigned int)pthread_self(),count);
		pthread_rwlock_unlock(&lock);
		usleep(100);
	}
	return NULL;
}

int main(){
	pthread_t tid[8];
	int i;
	for(i=0 ; i<3 ; i++){
		pthread_create(&tid[i],NULL,do_write_func,NULL);
	}
	for(i=0 ; i<5 ; i++){
		pthread_create(&tid[i+3],NULL,do_read_func,NULL);
	}
	for(i=0 ; i<8 ; i++){
		pthread_join(tid[i],NULL);
	}
	return 0;
}

4 条件变量

//生产者-消费者模型
	//pthread_cond_wait前要先加锁
    //pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
    //pthread_cond_wait被激活后会再自动加锁
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>

struct msg{
	int num;
	struct msg* next;
};

struct msg* head = NULL;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;

void* producer(void* arg){
	struct msg* mp;
	while(1){
		mp = (struct msg*)malloc(sizeof(struct msg));
		mp->num = rand()%1000 + 1;
		printf("produce %d\n",mp->num);
		pthread_mutex_lock(&lock);
		mp->next = head;
		head = mp;
		pthread_mutex_unlock(&lock);
		pthread_cond_signal(&has_product);
		sleep(rand()%5);
	}
}

void* consumer(void* arg){
	struct msg* mp;
	while(1){
		pthread_mutex_lock(&lock);
		while(head == NULL){
			pthread_cond_wait(&has_product,&lock);
		}
		mp = head;
		head = mp->next;
		pthread_mutex_unlock(&lock);
		printf("consume %d\n",mp->num);
		free(mp);
		sleep(rand()%5);
	}
}

int main(){

	pthread_t ptid,ctid;
	srand(time(NULL));
	pthread_create(&ptid,NULL,producer,NULL);
	pthread_create(&ctid,NULL,consumer,NULL);

	pthread_join(ptid,NULL);
	pthread_join(ctid,NULL);
	return 0;
}

5 信号量

//通过信号量的方式实现生产者和消费者模型
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>

#define NUM 5
sem_t blank_n,product_n; //定义两个信号量
int queue[NUM]; //资源

void* producer(void* arg){
	int p=0;
	while(1){
		sem_wait(&blank_n); //消耗一个blank_n信号量
		queue[p] = rand()%1000 + 1;
		printf("produce %d\n",queue[p]);
		sem_post(&product_n); //归还一个product_n信号量
		p = (p+1)%NUM;
		sleep(rand()%3);
	}
	return NULL;
}

void* consumer(void* arg){
	int p=0;
	while(1){
		sem_wait(&product_n); //消耗一个product_n信号量
		printf("consume %d\n",queue[p]);
		sem_post(&blank_n); //归还一个blank_n信号量
		p = (p+1)%NUM;
		sleep(rand()%3);
	}
	return NULL;
}

int main(){

	srand(time(NULL));
	pthread_t ptid,ctid;
	
	sem_init(&blank_n,0,NUM);
	sem_init(&product_n,0,0);

	pthread_create(&ptid,NULL,producer,NULL);
	pthread_create(&ctid,NULL,consumer,NULL);

	pthread_join(ptid,NULL);
	pthread_join(ctid,NULL);
	
	sem_destroy(&blank_n);
	sem_destroy(&product_n);
	return 0;
}

6 进程间锁-进程间pthread_mutex

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<pthread.h>
#include<string.h>
#include<sys/wait.h>

struct mt{ //控制的进程间共享资源
	int num;
	pthread_mutexattr_t attr;
	pthread_mutex_t mutex;
};

int main(){

	int fd,i;
	pid_t pid;
	struct mt* mm;

	fd = open("my_test",O_RDWR|O_CREAT,0777); //进程间共享数据基于的文件
	ftruncate(fd,sizeof(*mm)); //填入空洞值
	mm = (struct mt*) mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //映射
	close(fd);
	memset(mm,0,sizeof(*mm)); //清零
	pthread_mutexattr_init(&mm->attr); //pthread的属性设置,设置为进程锁
	pthread_mutexattr_setpshared(&mm->attr,PTHREAD_PROCESS_SHARED);
	pthread_mutex_init(&mm->mutex,&mm->attr); //lock为进程锁

	pid = fork(); //fork子进程
	if(pid == 0){ //子进程
		for(i=0 ;i<10 ;i++){
			pthread_mutex_lock(&mm->mutex); //上锁访问进程共享资源
			(mm->num)++;
			printf("num++ %d\n",mm->num);
			pthread_mutex_unlock(&mm->mutex);
			sleep(1);
		}
	}else if(pid > 0 ){ //父进程
		for(i=0 ; i<10 ; i++){
			pthread_mutex_lock(&mm->mutex);//上锁访问进程共享资源
			mm->num += 2;
			printf("num+=2 %d\n",mm->num);
			pthread_mutex_unlock(&mm->mutex);
			sleep(1);
		}
		wait(NULL);
	}

	pthread_mutex_destroy(&mm->mutex); //销毁锁
	pthread_mutexattr_destroy(&mm->attr); //销毁属性
	munmap(mm,sizeof(*mm)); //解除映射绑定
	unlink("my_test"); //清除临时文件
	
	return 0;
}

7 进程间锁-文件锁

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(int argc,char* argv[]){
	int fd;
	struct flock f_lock;
	if(argc < 2){
		printf("./a.out filename\n");	
		exit(1);
	}

	fd = open(argv[1],O_RDWR);
	f_lock.l_type = F_RDLCK;
	f_lock.l_whence = SEEK_SET;
	f_lock.l_start = 0;
	f_lock.l_len = 0;

	fcntl(fd,F_SETLKW,&f_lock);
	printf("get flock\n");
	sleep(5);
	f_lock.l_type = F_UNLCK;
	fcntl(fd,F_SETLKW,&f_lock);
	printf("un flock\n");
	close(fd);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城老湿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值