Linux 进程
2021-05-28 本文已影响0人
攻城老狮
一 环境变量
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;
}