b:块文件、位进制文件
c:字符设备文件
d:目录
-:普通文件
l:符号文件
s:套接字文件
p:管道文件
缓存类型:
全缓存:当填满I/O缓存后才进行实际I/O操作(或者执行fflush、floce、exit、return),4K大小
行缓存:当填满I/O缓存后才进行实际I/O操作或者遇到新行符‘\n’(或者执行fflush、floce、exit、return),1K大小
无缓存:标准错误输出strerr
FILE *fp;
fp = fopen("./test.c","w");
if(fp == NULL)
{
perror("fail to fopen");
return -1;
}
printf("success to fopen\n");
printf("stdout:%d\n",stdout->_IO_buf_end -stdout-> _IO_buf_base );
//有缓存 需要再次 才能将内容刷出
printf("stdout:%d\n",stdout->_IO_buf_end -stdout-> _IO_buf_base );
freopen("./test.c","w+",stdout);
printf("after:stdout:%d\n",stdout->_IO_buf_end -stdout-> _IO_buf_base);
//有缓存 需要再次 才能将内容刷出
printf("after:stdout:%d\n",stdout->_IO_buf_end -stdout-> _IO_buf_base);
//fflush(fp_r);
//fclose(fp_r); //close不返回 exit和_exit其实最终都是一样的
//return -1;
//exit(1); //先刷完 在退出
//_exit(1); 不刷 直接退出
#include<stdio.h>
#include<stdlib.h>
typedef struct msg{
char a;
int b;
}msg_t;
int main(int argc, const char *argv[])
{
msg_t mymsg[3] = {{'a',100},{'c',105},{'b',100}};
FILE *fp = fopen("test.c","w+");
fwrite(mymsg,sizeof(mymsg[0]),3,fp);
rewind(fp); //将文件内部的位置指针重新指向一个流(数据流/文件)的开头 void rewind(fp) = fseek(fp,0,SEEK_SET)
msg_t mymsg_r[3]={};
fread(mymsg_r,sizeof(mymsg_r[0]),3,fp); // 这个3 很重要, 是个数 sizeof(mymsg)/sizeof(mymsg[0])
int i=0;
for(i=0;i<3;i++)
printf("a:%c\nb:%d\n",mymsg_r[i].a,mymsg_r[i].b);
return 0;
}
直接IO(二进制IO):
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:读取一个结构
参数:ptr:存放读取到的结构的地址
size:每个结构的大小
nmemb:结构的数量
stream:文件流指针
返回值:成功返回需要读取到的结构的数目,失败返回实际读取到的结构的数目(小于nmemb值);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:写入一个结构
参数:ptr:需要写入的结构的地址
size:每个结构的大小
nmemb:结构的数量
stream:文件流指针
返回值:成功返回需要写入的结构的数目,失败返回实际写入的结构的数目(小于nmemb值);
文件描述符:从0顺序分配的非负整数
内核自动为我们打开0、1、2
0 =》标准输入(stdin) FILE* int STDIN_FILENO
1 =》标准输出(stdout)
2 =》标准错误输出(stderr)
int open(const char *pathname, int flags, mode_t mode);
功能:打开一个文件(可以打开设备文件)
参数:pathname:打开文件的路径名
flags:O_RDONLY:只读方式打开文件。
O_WRONLY:可写方式打开文件。
O_RDWR:读写方式打开文件。
以上三个参数互斥
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。
O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。
mode:权限,八进制数表示 0666
返回值:成功返回一个新的文件描述符,失败返回-1.
int close(int fd):关闭一个文件描述符
ssize_t write(int fd, const void *buf, size_t count);
功能:向一个文件描述符中写入数据
参数:fd:文件描述符
buf:写入数据的首地址
count:希望写入数据的数量
返回值:成功返回实际写入的数量,失败返回-1。 ///实际写入的数量 是read的返回读到的数目 send
ssize_t read(int fd, void *buf, size_t count);
功能:从一个文件描述符中读取数据
参数:fd:文件描述符
buf:读取数据存放的地址
count:希望读多少个 //希望读到的数目 到最后的时候返回的是实际读到的数量 >0 recv
返回值:成功返回实际读到的数量,失败返回-1;
//扩展文件 之后必须写入'\0'
off_t lseek(int fd, off_t offset, int whence);
功能:设置文件读写位置指针
参数:fd:文件描述符
offset:偏移量
whence:偏移的基准
SEEK_SET:以文件开头为基准
SEEK_CUR:以文件当前的位置为基准
SEEK_END:以文件末尾为基准
返回值:返回偏移后的文件读写指针位置,失败返回-1;
获取文件属性函数
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
三个函数的返回:若成功则为0,若出错则为-1,并且设置errno.
给定一个pathname的情况下:
stat函数返回一个与此命名文件有关的信息结构
fstat函数获得已在描述符filedes上打开的文件的有关信息
lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息。
int mkdir(const char *pathname, mode_t mode);
功能:创建一个目录
参数:pathname:目录的路径名
mode:目录的权限 //0777
返回值:成功返回0,失败返回-1
DIR *opendir(const char *name);
功能:打开一个目录
参数:name:目录路径名
返回值:成功返回一个目录流,失败返回NULL
struct dirent *readdir(DIR *dirp); //先定义一个struct dirent *dir // dir=readdir(DIR *dirp)
功能:读取一个目录内容
参数:dirp:目录流
返回值:成功返回一个结构体指针,结构体内存放着目录所包含文件的信息,失败返回NULL,
动态库和静态库:
区别:
1、静态库在编译的时候被加载,动态库在执行的之后被加载
2、加载静态库编译所生成的可执行文件比使用动态库的方式要大
3、静态库以.a为后缀,动态库以.so为后缀
静态库的制作:
1.通过gcc -c add.c 生成目标文件 =》add.o
2.通过ar crs -o libadd.a add.o 生成静态库 =》libadd.a //重点
3.通过gcc main.c -L. -ladd 链接静态库编译,生成可执行文件a.out
4.执行a.out即可
动态库的制作:
1.通过gcc -c add.c 生成目标文件 =》add.o
2.通过gcc -fPIC -shared -o libmyadd.so add.o 生成动态库文件 =》libmyadd.so //重点
3.通过gcc main.c -L. -lmyadd 生成可执行文件 =》a.out ///-L. 是去哪里找
4,此时,运行找不到库文件,我们需要
(1)将libmyadd.so放到/usr/lib或/lib目录下
(2)通过export LD_LIBRARY_PATH=/home/linux/jn16081/day02/lib 将库所在的绝对路径添加至环境变量中(通过echo $LD_LIBRARY_PATH查看此环境变量的值;通过unset LD_LIBRARY_PATH来删除此环境变量里的值)
(3)在/etc/ld.so.conf.d/下新建一个sudo vi my.conf文件,在里面写入库所在的绝对路径,保存并执行sudo ldconfig执行配置文件。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc, const char *argv[])
{
struct stat st;
if(lstat(argv[1],&st) == -1)
{
perror("fail to lstat");
return -1;
}
printf("inode:%lu\n",st.st_ino);
if(S_ISREG(st.st_mode))
{
putchar('-');
}else if(S_ISDIR(st.st_mode)){
putchar('d');
}
if(st.st_mode & S_IRUSR )
{
putchar('r');
}else{
putchar('-');
}
if(st.st_mode & S_IWUSR )
{
putchar('w');
}else{
putchar('-');
}
if(st.st_mode & S_IXUSR )
{
putchar('x');
}else{
putchar('-');
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<dirent.h>
int main(int argc, const char *argv[])
{
DIR *dp;
dp = opendir(".");
if(dp == NULL)
{
perror("fail to opendir");
return -1;
}
struct dirent *dir;
while(1)
{
dir = readdir(dp);
if(dir == NULL)
{
break;
}
printf("ino:%lu name:%s\n",dir->d_ino,dir->d_name);
}
return 0;
}
ps axj:查看进程的信息
top:动态显示进程信息
nice -5 ./a.out : 以nice值为5的优先级方式运行a.out
nice --5 ./a.out : 以nice值为-5的优先级方式运行a.out
nice值为(-20~19,值越低,优先级越高)
renice -5 PID :改变正在运行进程的nice值,设为-5
renice 5 PID :改变正在运行进程的nice值,设为5.
//kill -l 是查看信号的(小写L)
kill -9 pid 杀死一个进程 本质是发送一个SIGKILL
通过jobs查看后台运行或者挂起进程对应的号码
然后通过bg 对应的号码 来使挂起的进程放入后台执行
通过fg 对应的号码 把后台运行的进程放到前台运行
写时拷贝技术:它通过允许父子进程可访问相同物理内存从而伪装了对进程地址空间的真实拷贝,当子进程需要改变内存中数据时才拷贝父进程。也就是说当子进程对数据进行更改操作时,系统才会为更改的数据分配实际的物理地址空间。
pid_t fork(void)
功能:创建一个新进程
参数:无参
返回值: 成功: 1、在父进程里面返回一个大于0的值(子进程PID) //fork出来的是pid是子的pid>0
2、在子进程里面返回0
出错:返回-1
pid_t pid=fork();
-1 :perror
0:子进程 只是fork的返回值 子进程的getpid()=123是自己的pid ,getppid是父进程的pid
>0:返回值是123 父进程getpid()是自己的pid pid=123就是子进程的,
pid_t getpid(void):函数返回调用进程本身的PID
pid_t getppid(void):函数返回调用进程的父进程的PID
pid_t wait(int *status) //NULL 或者定义一个 int status status是退出状态
功能:阻塞接收子进程的退出状态,回收子进程的资源(收尸)
参数:status:用来接收子进程退出状态的值
返回值:成功返回回收子进程的PID,失败返回-1(在调用进程无子进程的情况下失败)
//exit(1) status是128 exit(2)的话 stats是256 改变的是第8位开始的值
int WEXITSTATUS(int num):函数返回num值第八位的值 // 相当于 status>>8
//fork出来的是pid是子的pid>0
pid_t waitpid(pid_t pid, int *status, int options); //pid 是子进程getpid 是>0的那个pid
功能:回收子进程的退出状态
参数:pid:
pid>0:只等待进程ID等于pid的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1:等待任何一个子进程退出,此时和wait作用一样。
pid=0:等待其组ID等于调用进程的组ID的任一子进程。
pid<-1:等待其组ID等于pid的绝对值的任一子进程。
status:同wait
options:
WNOHANG:若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时返回值为0
WUNTRACED:若某实现支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态。
0:同wait,阻塞父进程,等待子进程退出。
返回值:
正常:结束的子进程的进程号
使用选项WNOHANG且没有子进程结束时:0
调用出错:-1
wait(NULL) ==> waitpid(-1,NULL,0) //wait(NULL) ==> waitpid(-1,NULL,0)
exec函数族:
l:以列举的方式传参
v:以指针数组方式传参 //二级指针 定义的时候需要定义指针数组
p:自动加载环境变量
e:自定义环境变量 //二级指针 定义的时候需要定义指针数组
在使用exec函数族时,一定要加上错误判断语句 //一定要加上错误判断语句
成功执行,整个进程就换成了其他的程序
失败返回-1
exec中调用shell的命令,就是忽略第二个参数。就是命令后面一个参数
exec中调用自己的命令,就不会忽略第二个参数,因为自己的命令可能会用arg[0],如果不用,开始是arg[1]那也忽略了arg[0]
所以说argv[0]不一定是自己的名字
创建守护进程的步骤:
1、创建子进程,父进程退出
此时,子进程为孤儿进程,与终端脱离的部分关系
2、在子进程中创建新会话 =》 setsid()
此时,子进程为新建会话组的组长,彻底脱离于原bash会话组的关系
3、改变当前目录为根目录 =》chdir(“/”);
保证守护进程工作环境的安全性
4、重设umask码为0,不屏蔽任何权限 =》umask(0);
5、关闭终端相关的文件描述符 0,1,2 =》close(0);close(1);close(2);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fail to fork");
exit(1);
}else if(pid == 0) //在父进程中返回的pid是==0的
// { //getpid是自己的 pid
//child
printf("im child:%d,my parent:%d\n",getpid(),getppid());
exit(3);
}else{
//parent //在父进程返回的pid是>0的,是child 的pid
// 所以父进程getpid是自己的 pid
int status;
printf("wait:%d\n",waitpid(pid,&status,0));
printf("status:%d\n",WEXITSTATUS(status));//相当于status>>8
printf("im parent:%d,my parent:%d\n",getpid(),getppid());
}
exit(1);
return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<syslog.h>
int main(int argc, const char *argv[])
{
//1。创建子进程,父进程退出
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fail to fork");
exit(1);
}else if(pid > 0)
{
exit(1); //return 是返回 exit是退出
}else{
setsid(); //2。在子进程中创建新会话
umask(0); //3。将umask置为0
chdir("/"); //4。将工作目录改为根目录
int fdNum = getdtablesize();//关闭文件描述符
int i = 0;
for(; i < fdNum; i++)
{
close(i);
}
i = 0;
while(1)
{
syslog(LOG_INFO,"i = %d\n",i++) ;
sleep(1);
}
}
return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
printf("before exec\n");
/* if(execl("/home/linux/g1711/process/day01/main","-p","-l","-a","-i",NULL) == -1)
{
perror("fail to execl");
exit(1);
}
*/
char *buf[] = {"-l","-a","-p",NULL};
char *env[] = {"XXX=hello",NULL};
if(execvpe("/home/linux/g1711/process/day01/main",buf,env) == -1)
{
perror("fail to execl");
exit(1);
}
//export
printf("success\n");
return 0;
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, const char *argv[], char *env[])
{ //*argv[]指针数组
printf("argc:%d\n",argc);
int i= 0;
while(*argv) //*arg是地址,地址为空的时候就失败 while(p!=p->next)
{
printf("argv[%d]:%s\n",i++,*argv++);
}
while(*env)
{
printf("%s\n",*env++);
}
puts("++++++++++++++++++++++++++++++");
printf("XXX:%s\n",getenv("XXX"));
puts("++++++++++++++++++++++++++++++");
return 0;
}
进程和线程:
1、线程是参与内核调度最小基本单位,进程是拥有资源的最小基本单位
2、进程间相互独立,而同一个进程内的线程间共享进程内所有的资源
3、多线程间通信简单,但是需要对临界资源进行互斥与同步操作,多进程间通信较难
4、多线程安全性差,因为其中一个线程崩溃可能会对其他线程造成影响,多进程间相互独立,安全性高。
线程相关函数是由第三方库支持,所以编译时需要加上 -lpthread选项
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
功能:创建一个新线程
参数:thread:线程名
attr:线程的属性,通常设为NULL
void *(*start_routine) (void *):函数指针,指向一个参数为void *,返回值为void *类型的函数。这里我们就填入函数名,此函数为线程执行内容的主体。(函数名为函数入口地址的别称)
arg:传入线程主体函数start_routine的参数,不传则设为NULL
返回值:成功返回0,失败返回errno
pthread_attr_t attr;
pthread_attr_init(&attr);//要求初始化
pthread_attr_setstacksize(&attr,1*1024*1024);
void pthread_exit(void *value_ptr) //给地址
功能:使调用线程退出
参数:线程退出时返回的值
返回值:成功返回0,失败返回errno
//线程中设置属性
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//不cancel
//被动回收某个线程
// 多个线程中使用的时候可以和pthread_cancel一起使用
int pthread_join(pthread_t thread, void **value_ptr) //int *p ; pthread_join(tid,(void**)&p)
功能:回收退出线程的资源
参数:thread:指定回收线程的资源
value_ptr:接收线程退出时返回的值
返回值:成功返回0,失败返回errno
//主动强制回收某个线程。
// 多个线程中使用的时候可以和pthread_cancel一起使用
int pthread_cancel(pthread_t thead)
功能:指定取消某个线程(此函数需要对线程的属性进程设置,相应cancel信号,并设置对相应cancel信号的操作)
参数:指定的线程名
返回值:成功返回0,失败返回errno
同步(synchronization):指的是多个任务(线程)
按照约定的顺序相互配合完成一件事情
同步机制用信号量来实现:
信号量代表某一类资源,其值表示系统中该资源的数量.. //
//sem_t sem
//sem_init(&sem,0,0)
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化一个信号量
参数:sem:信号量
pshared:0表示线程间,1表示进程间 //0
value:初始化信号量的值 //0 二值操作
返回值:成功返回0,失败返回-1
int sem_wait(sem_t *sem) // -1
功能:P操作,申请资源 //不要记
参数:需要申请的信号量
返回值:成功返回0,失败返回-1
int sem_post(sem_t *sem) //+1
功能:V操作,释放资源 //不要记
参数:释放的信号量
返回值:成功返回0,失败返回-1
互斥机制:
引入互斥(mutual exclusion)锁的目的是用来保证共享数据操作的完整性。
互斥锁主要用来保护临界资源 //重要
每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源
线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止
//&mutex NULL
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
功能:初始化一把锁
参数:mutex:需要初始化的锁名称
attr:锁的属性,通常设为NULL
返回值:成功返回0,失败返回-1
int pthread_mutex_lock(pthread_mutex_t *mutex)
功能:获取锁
参数:获取哪一把锁
返回值:成功返回0,失败返回errno
int pthread_mutex_unlock(pthread_mutex_t *mutex)
功能:将锁释放
参数:释放的锁
返回值:成功返回0,失败返回errno
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁一把锁
参数:销毁的锁
返回值:成功返回0,失败返回errno
条件变量: //&cond NULL
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:初始化一个条件变量
参数:条件变量对象的指针,条件变量的属性(一般设置为NULL)
返回值:成功返回0,失败设置errno
//&cond &mutex
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
功能:条件等待,默认阻塞,当锁和条件均获取的时候函数返回
参数:条件的指针,锁的指针
返回值:返回的时候返回0值
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
void *thread_func(void *arg)
{
// printf("im : %lu\n",*(pthread_t *)arg);
static int a = 200;
pthread_exit(&a); //void pthread_exit(void *retval);
}
int main(int argc, const char *argv[])
{
// char buf[9*1024*1024] = {};
pthread_t tid; //线程的tid
if(pthread_create(&tid,NULL,thread_func,(void *)&tid) != 0)
{
perror("fail to pthread_create");
exit(1);
}
int *p; //用来接收a的值
pthread_join(tid,(void **)&p);//传入地址才能改变*p的值(用二级指针来改变)
printf("join:%d\n",*p);
return 0;
}
//pthread_create最后一个void *arg传值给前一个函数指针
//函数指针中 pthread_exit(&a) 传值给pthread_join接收 a必须定义static
//pthread_join(tid, &p), 接收pthread_exit(&a)的值,
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
void *thread_func(void *arg)
{
// printf("im : %lu\n",*(pthread_t *)arg);
//char buf[9*1024*1024] = {};
//static int a = 200;
//pthread_exit(&a);
pthread_self();
}
int main(int argc, const char *argv[])
{
// char buf[9*1024*1024] = {};
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);//要求初始化
pthread_attr_setstacksize(&attr,1*1024*1024);
int i = 0;
while(1)
{
if(pthread_create(&tid,&attr,thread_func,&tid) != 0)
{
perror("fail to pthread_create");
exit(1);
}
printf("i : %d\n",++i);
}
int *p;
pthread_join(tid,(void **)&p);
printf("join:%d\n",*p);
return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
int flags = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void *hello(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
while(flags != 0) // 需要的是外面的 使用
{
pthread_cond_wait(&cond,&mutex);
}
printf("hello\n"); // flag = 0 的时候 没有执行while 就打印了
flags = 1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
}
void *the(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
while(flags != 1)
{
pthread_cond_wait(&cond,&mutex);
}
printf("the\n");
flags = 2;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
}
void *world(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
while(flags != 2)
{
pthread_cond_wait(&cond,&mutex);
}
printf("world\n");
flags = 0;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
}
int main(int argc, const char *argv[])
{
pthread_t tid[3];
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&tid[0],NULL,hello,NULL);
pthread_create(&tid[1],NULL,the,NULL);
pthread_create(&tid[2],NULL,world,NULL);
getchar();
return 0;
}
条件变量:
//必须定义一个pthread_cond_t cond 这个在全局变量或者 结构体中都可以
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:初始化一个条件变量
参数:条件变量对象的指针,条件变量的属性(一般设置为NULL)
返回值:成功返回0,失败设置errno
//只有当锁和条件变量都满足的情况下才能使用
//所以这里必须有4个函数
//pthread_cond_t cond
//pthread_mutex_t mutex
//对他们初始化 pthread_cond_init(&cond,NULL);
// pthread_mutex_init(&mutex,NULL);
//需要一个广播 pthread_cond_broadcast(&cond)
//真正的第一个是加锁,锁住了才能操作资源,所以下面要去判断是否是轮到自己的条件
//如果不是就去释放锁,让别人去竞争
//下面是pthread_cond_wait(&cond,&mutex)的使用, 该函数成立的条件是 获得cond 和mutex
//如果执行到该函数,第一步是去释放锁 (锁住才能操作变量)
//然后去睡觉,释放了锁,说明还不是轮到我,所以就去睡觉 // 默认阻塞
//等待pthread_broadcast这个条件 线程池中,是大家都有条件了,就差锁
//获取锁,当锁和条件变量都成立了,就能操作临界值了
//之后广播,释放锁
//
void *hello(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex); //抢到锁,并锁住,去操作资源
while(flags != 0) //判断标志位是不是我
{
pthread_cond_wait(&cond,&mutex); //不是我的话,去释放锁,让大家竞争,阻塞
} //阻塞的话,就会等到信号,就去while判断标志位
printf("hello\n"); //标志位是0 是我的话就打印hello,此时是锁住的
flags = 1; //标志位置1 按顺序打印
pthread_cond_broadcast(&cond); //广播所有的人 pthread_cond_wait就能收到广播
pthread_mutex_unlock(&mutex); //释放锁,让他们去抢
}
}
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
功能:条件等待,默认阻塞,当锁和条件均获取的时候函数返回
参数:条件的指针,锁的指针
返回值:返回的时候返回0值
进程间通信:
进程在用户空间内是相互独立的,无法在用户空间内进行通信。但是它们都可以访问内核,那么它们可以通过在内核中创建一个对象,然后共同访问同一个对象来进行通信。
进程间的通信方式:
无名管道、有名管道、信号、共享内存、消息队列、信号灯集(基于同一个内核之间的通信)
socket套接字(基于多个内核之间的通信方式)
无名管道:半双工通信机制,只能用于父子进程间(具有亲缘关系的进程间)通信,没有名字,没有对应的文件节点,存储在内存中。
//操作管道的文件描述符,fd[0]表示读端,fd[1]表示写端。
//是在内存中写的
int pipe(int pipefd[2]);
功能:创建一个无名管道
参数:操作管道的文件描述符,fd[0]表示读端,fd[1]表示写端。
返回值:成功返回0,失败返回-1.
无名管道的特点:
1、当管道中没有数据的时候,读阻塞;//直到有人去写
2、当写端关闭时,读操作不阻塞(没有必要),而是直接返回。
当读端关闭时,对管道进行写操作会管道破裂,程序直接退出。//读相当于管道出水,出水堵住,在往里面灌水就爆
3、管道里面的东西,读完就没有了 ,,读完了,就会阻塞
4、当管道被写满时,写阻塞(管道大小为65536) ,写满了就阻塞,之后等有人去读
有名管道:有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见
//但是文件大小总是0,也是在内存中写的,且不能复制到windows
// 0777
int mkfifo(const char *pathname, mode_t mode); //类似 mkdir(const char *pathname,mode_t mode)
功能:创建一个有名管道
参数:pathname:有名管道的路径名
mode:权限,八进制数表示
返回值:成功返回0,失败返回-1
有名管道需要注意函数执行失败的情况,如果失败返回的errno为EEXIST,那么表示有名管道已经存在,那么我们只需要打开它使用就可以了。
信号通信 kill -l
SIGHUP 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。
ctrl+c
SIGINT 该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。
ctrl+\
SIGQUIT 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。
SIGILL 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。
SIGFPE 该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。
//两个进程不能被阻塞 处理和忽略 SIGKILL 和 SIGSTOP
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。
SIGALRM 该信号当一个定时器到时的时候发出。
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。
ctrl+z
SIGTSTP 该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。
SIGCHLD 子进程改变状态时,父进程会收到这个信号
SIGABORT 该信号用于结束进程
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
//向pid发送一个singno 可以是宏也可以是是数字
信号的发送:
int kill(pid_t pid, int sig);
功能:发送一个信号
参数:pid:发送的目标进程
sig:发送的信号名称 可以是数字也可以是宏
返回值:成功返回0,失败返回-1;
//向自己发送一个xxx的信号 随便上面信号
int raise(int sig);
功能:向调用进程发送一个信号
参数:sig:信号的种类
返回值:成功返回0,失败返回非0
//两次调用的话,也只会返回第一次所剩余的时间 例如alarm(5) sleep(2) alarm剩下3
unsigned alarm(unsigned seconds);
功能:定时发送SIGALRM信号
参数:定时的秒数
返回值:如果首次调用alarm,那么返回0;否则返回上一个alarm函数所剩余的时间。
信号的接收 //一般和signal 一起使用 想象中断
int pause(void)
功能:将进程挂起接收任意一个信号;
参数:无
返回值:捕捉到信号或者信号处理函数返回时,返回-1;
信号的处理
//signal(接收到的信号,跳到handler去处理函数) 参数就是接收到的信号,可以对其进行判断
//一般和pause一起用
void (*signal)(int signum, void (*handler)(int)))(int);
功能:自定义对信号的处理
参数:signum:需要设定信号的名字
handler:一个函数指针,指向一个返回值为void,参数为int型的这么一个函数
返回值:返回handler函数的地址
if(mkfifo("fifo",0666) == -1)
{
if(errno == EEXIST)
{
printf("the fifo is already exist\n");
}else{
perror("fail to mkfifo");
return -1;
}
}
printf("success to mkfifo\n");
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sem;
void *hello(void *arg)
{
printf("hello\n");
sem_post(&sem); //++操作
}
void *kitty(void *arg)
{
sem_wait(&sem); //--操作 开始是0 是不能--
printf("kitty\n");
}
int main(int argc, const char *argv[])
{
pthread_t tid[2];
sem_init(&sem,0,0); //中间0 1 是线程 和进程
pthread_create(&tid[0],NULL,hello,NULL);
pthread_create(&tid[1],NULL,kitty,NULL);
getchar();
return 0;
}
//这个程序执行一次是没有问题的
//但是循环的话 会出问题 因为 线程不是随机执行的 不是交叉执行的
#include<stdio.h>
#include<signal.h>
void handler(int signo)
{
sleep(2);
}
int main(int argc, const char *argv[])
{
signal(SIGINT,handler);//收到ctrl+c 就会去处理handler的函数
pause();
return 0;
}
//程序运行 开始运行到signal 并不执行
//运行到pause 就挂起 相当于while(1)
// 但是比while(1) 好在不会浪费资源,只要有信号就会去相应signal设置的中断
// 没有信号是不会一直循环浪费的
项目中 :需要 取数据或者发送数据的话 用共享内存
需要发送命令的话,还是用消息队列
pipe: 具有亲缘关系的进程间,单工,数据在内存中
fifo: 可用于任意进程间,双工,有文件名,数据在内存 //文件大小也是0 操作是在内存的 不能拷贝到windows
signal: 唯一的异步通信方式 //等待接收到信号的时候去干某事 中断设置函数
msg:常用于cs模式中, 按消息类型访问 ,可有优先级
shm:效率最高(直接访问内存) ,需要同步、互斥机制
sem:配合共享内存使用,用以实现同步和互斥
IPC对象:共享内存,消息队列,信号灯集
可以通过命令
ipcs -a查看所有的IPC对象使用情况 //哇靠
ipcs -m 查看共享内存 //都是进程间通讯
ipcs -q 查看消息队列 //都是进程间通讯
ipcs -s 查看信号灯集 //都是进程间通讯
ipcrm -m shmid 删除shmid的共享内存
ipcrm -q msgid 删除消息队列
ipcrm -s semid 删除信号灯集
共享内存:进程间通信效率最高的通信方式,为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间,进程可以直接读写内存,而不需要任何数据的拷贝。
//key_t key=ftok("/",'k'); //一种密钥
//四个函数 //创建/打开内存shmget //映射内存 shmat //撤销shmdt //删除shmctl(操作)
//IPC_CREAT|IPC_EXCL|0666)
int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开一段共享内存
参数:key:ftok函数返回值或者IPC_PRIVATE // "\"绝对路径 +char 'f' 后8位+st_inod(后4位)+st_dev(后2位)
size:申请共享内存的大小
shmflg:打开方式及权限 IPC_CREAT IPC_EXCL 0666
返回值:成功返回创建的共享内存的shmid,失败返回-1并且设置errno。
//需要 定义一个类型去接收 是char * 或者 struct *类型 都可以 //返回的就是这个定义的类型 就是地址
//NULL一般 0
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存
参数:shmid:共享内存ID
shmaddr:一般设为NULL,否则需要自定义映射地址
shmflg:SHM_RDONLY为只读,0表示可读可写 //一般写0
返回值:成功返回映射后的地址,失败返回-1
//该地址 就是上个映射的函数
int shmdt(const void *shmaddr);
功能:取消映射共享内存
参数:取消映射的地址
返回值:成功返回0,失败返回-1;
//一般用来删除 设置 IPC_RMID NULL
//其他两个需要操作其结构体
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:对共享内存进行操作
参数:shmid:共享内存的ID
cmd:IPC_STAT表示获取共享内存的信息
IPC_SET表示设置共享内存的信息
IPC_RMID表示删除共享内存
返回值:成功返回0,失败返回-1
//dest是销毁, nattch是映射数
在还有进程映射共享内存的情况下,对共享内存进行IPC_RMID操作会使这段共享内存变为dest状态,key值变为0,当nattch映射数为0的时候自动被删除。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<signal.h>
#include<errno.h>
#include<string.h>
typedef struct msg{
pid_t pid; //传pid //shmget的时候设置了128字节 pid占4个
char buf[124]; //传正文 //buf占 128-4
}msg_t;
msg_t *p; //shmat返回的是一个void*的地址,所以可以是任何类型
pid_t pid;
void handler(int signo)
{
if(signo == SIGUSR1)
{
return; //返回到signal(SIGUSR1,handler); 向下执行
}else{
printf("%s\n",p->buf);
kill(pid,SIGUSR2); //向pid发送信号SIGUSR2(另一个进程的pid)
}
}
int main(int argc, const char *argv[])
{
int shmid;
key_t key;
key = ftok("/",'l');
if((shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
shmid = shmget(key,0,0); //成功之后获取shmid的值
}else{
perror("fail to shmget");
return -1;
}
}
printf("shmid:%d\n",shmid);
p = shmat(shmid,NULL,0);
if(p == (msg_t *)-1) //p是映射内存成功之后返回的地址
{
perror("fail to shmat");
return -1;
}
signal(SIGUSR1,handler); //接收到SIGUSR1 处理 handler
signal(SIGUSR2,handler); //接收到SIGUSR2 处理 handler
p->pid = getpid(); //设置pid 向结构体中写入自己的pid传过去
pause(); //挂起 等待有信号,有信号就返回 就向下执行
pid = p->pid; //获取结构体中的存放的pid
kill(pid,SIGUSR2); //向pid发送SIGUSR2信号
while(1)
{
pause();
}
//kill 和 pause 就是搭档
//kill 发送信号 pause负责挂起 接收信号,收到向下走
//return 是返回, exit是退出
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<signal.h>
#include<errno.h>
#include<string.h>
typedef struct msg{
pid_t pid;
char buf[124];
}msg_t;
msg_t *p;
pid_t pid;
void handler(int signo)
{
// printf("input:");
// fgets(p->buf,124,stdin);
// p->buf[strlen(p->buf) - 1] = '\0';
strcpy(p->buf,"hello");
kill(pid,SIGUSR2);//向另一个进程的pid发送SIGUSR2 表示我写好数据了
return;
}
int main(int argc, const char *argv[])
{
int shmid;
key_t key;
key = ftok("/",'l');
if((shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
shmid = shmget(key,0,0);
}else{
perror("fail to shmget");
return -1;
}
}
printf("shmid:%d\n",shmid);
p = shmat(shmid,NULL,0);
if(p == (msg_t *)-1)
{
perror("fail to shmat");
return -1;
}
signal(SIGUSR2,handler); //接收到SIGUSR2就去handler执行
pid = p->pid; //取出 p->pid的值
p->pid = getpid(); //设置p->pid的值为自己的pid
kill(pid,SIGUSR1); //向另一个进程的pid(另一个进程自己的pid)发送SIGUSR1
//表示我设置好了pid
while(1)
{
pause();
}
//超重点
//signal 相当于把信号和函数绑定 哪个信号去哪个函数去执行
//pause相当于没有信号就挂起,有信号,相当于中断,跳到相应的函数去执行
//没有while(1)就执行一次就返回了,就结束,
//没有pause只有while(1),就会一致while,浪费资源,其实和pause一样,只是浪费资源
return 0;
}
先运行该程序read
//1、没有收到信号,没有处理handler程序(信号和handler绑定)
//2、设置p->pid是自己的pid,用共享内存通信
//3、pause挂起 等待信号
//
//8、接收到信号,pause返回
//9、取出结构体的里的write的pid
//10、向write发送SIGUSR2的信号
//11、while(1) pause挂起等待
//
//15、接收到信号处理handler程序
//write
//4、开始没有接收到SIGUSR2信号,就没有处理handler(绑定)
//5、取出结构体中的pid,为read的pid
//6、设置结构体的中的pid为自己的pid //共享内存用来通信,对方就能拿到write的pid
//7、向read发送SIGUSR1信号 //注意这里pid是read的
//
//其实程序会在任何地方处理信号,而不是只在pause处挂起等待信号 //理解重点
//12、write接收到SIGUSR2信号 就会向中断一样去相应函数,而不是调到signal再去执行
//13、打印buf到屏幕上
//14、并向pid发送SIGUER2信号(该pid为read的pid 见7)
消息队列:消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列可以按照类型来发送/接收消息。
//消息队列四个函数 //创建/打开队列 msgget //发送msgsnd //接收 msgrcv //删除msgctl
//key_t key=ftok("/",‘k') 一般这样设计 IPC_PRIVATE是用来父子进程间通讯的(类似私有资源)
//msgflg IPC_CREAT|IPC_EXCL|0666)
int msgget(key_t key, int msgflg);
功能:创建或打开一个消息队列
参数:key:ftok返回值或者是IPC_PRIVATE
msgflg:八进制权限
返回值:成功返回msgid,失败返回-1
//一般会定义一个void* msgp的结构体,(啥类型都行,是void*类型)
//一般是 struct {long xxx ; int a ; char b ; char c[] 都可以};
//消息正文的daxiao 是 sizeof(struct)-sizeof(long) //long以下是正文
//消息类型 可以是 0 1 2 相同即可
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列中发送消息
参数:msgid:消息队列的ID
msgp:存放消息的结构体指针
msgsz:消息正文的大小
msgflg:设为0则直到发送完成函数才返回,IPC_NOWAIT 消息没有发送完成函数也会立即返回。
返回值:成功返回0,失败返回-1
//一般会定义一个void* msgp的结构体, 必须是和snd类型一致才行
//消息正文的daxiao 是 sizeof(struct)-sizeof(long) //long以下是正文
//消息类型 和msgsnd 0 1 2 相同即可
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列中读取指定类型的消息
参数:msgid:消息队列的ID
msgp:存放消息的结构体指针
msgsz:消息正文的大小
msgtyp:消息的类型
msgflg:0:若无消息函数会一直阻塞,IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。
返回值:成功返回读取到的消息长度,失败返回-1
//改功能和shmctl是差不多的 一般用来删除消息队列 IPC_RMID NULL
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列进行操作
参数:msgqid:消息队列的ID
cmd:IPC_STAT表示获取消息队列的信息
IPC_SET表示设置消息队列的信息
IPC_RMID表示删除消息队列
返回值:成功返回0,失败返回-1
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<errno.h>
typedef struct msg{
long mtype;
int a;
char b;
}msg_t;
int main(int argc, const char *argv[])
{
int msgid;
key_t key;
key = ftok("/",'x');
if((msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
msgid = msgget(key,0);
}else{
perror("fail to msgget");
return -1;
}
}
printf("msgid:%d\n",msgid);
system("ipcs -q");
msg_t mymsg;
mymsg.mtype = 1;
mymsg.a = 100;
mymsg.b = 'a';
msgsnd(msgid,&mymsg,sizeof(msg_t) - sizeof(long),0);
system("ipcs -q");
mymsg.mtype = 2;
mymsg.a = 200;
mymsg.b = 'b';
msgsnd(msgid,&mymsg,sizeof(msg_t) - sizeof(long),0);
system("ipcs -q");
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<errno.h>
typedef struct msg{
long mtype;
int a;
char b;
}msg_t;
int main(int argc, const char *argv[])
{
int msgid;
key_t key;
key = ftok("/",'x');
if((msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
msgid = msgget(key,0);
}else{
perror("fail to msgget");
return -1;
}
}
printf("msgid:%d\n",msgid);
system("ipcs -q");
msg_t mymsg;
msgrcv(msgid,&mymsg,sizeof(msg_t) - sizeof(long),1,0);
printf("mymsg.mtype:%ld\nmymsg.a:%d\nmymsg.b:%d\n",mymsg.mtype,mymsg.a,mymsg.b);
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
//信号灯集就是信号灯或者叫信号量的集合
//三个函数 //创建/打开信号量 semget //控制信号灯semctl //pv操作 semtop
//key_t key=ftok("/",'k')
// 2 表示2个信号灯 //IPC_CREAT|IPC_EXCL|0666)
创建/打开信号量集合
int semget(key_t key, int nsems, int semflg);
参数:
key, ftok()返回值
nsems, 指定的信号量集合中的信号量个数
semflg,
IPC_CREAT
IPC_EXCL
返回值:返回该集合的标识符,失败-1.
//一般需要在main的外部定义一个联合体
//semnum, 要操作的信号量的编号,编号从0开始
//cmd 如果定义SETVAL:设置信号灯的值,需要用到第四个参数
//需要定义一个联合体 union semun{ int val ;} 即刻 选最大的或者
//后面一个参数把结构体地址传进去
比如:
union semun{
int val;
};
union semun mysenum;
mysemum.val = 10;
semctl(semid, 0, SETVAL,mysemnum);
//cmd如果定义GETVAL:获取信号灯的值, 返回值是获得值。
比如:value = semctl(semid, 0, GETVAL);
//cmd如果定义 IPC_RMID:从系统中删除信号灯集合
比如: semctl(semid, 0, IPC_RMID);
int semctl(int semid, int semnum, int cmd, ...);
功能:信号量集合的控制
参数:
semid, 指定要操作的集合
semnum, 要操作的信号量的编号,编号从0开始
cmd,
GETVAL:获取信号灯的值, 返回值是获得值。
比如:value = semctl(semid, 0, GETVAL);
SETVAL:设置信号灯的值,需要用到第四个参数。
第四个参数类型如下:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
比如:
union semun{
int val;
};
union semun mysenum;
mysemum.val = 10;
semctl(semid, 0, SETVAL,mysemnum);
IPC_RMID:从系统中删除信号灯集合
比如: semctl(semid, 0, IPC_RMID);
返回值:和cmd有关,失败-1.
//在main中定义一个结构体struct sembuf *opsptr
//sem_num 表示要操作的信号量的编号
//sem_op, 表示进行P或者V操作
// sem_flg,0(最常用的),表示semop函数的操作是阻塞的,直到成功为止。
//nops, 调用一次semop要操作的信号量的个数
//其实就是结构体的偏移量 所以必须是从头开始算 //不能写1 操作第二个buf[2]这个结构体,该需要填2,同时操作两个才行。
int semop ( int semid, struct sembuf *opsptr, unsigned nops);
功能:就是对信号量集合中的信号量进行PV操作
参数: semid,
opsptr,
struct sembuf{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
成员分析:
sem_num 表示要操作的信号量的编号
sem_op, 表示进行P或者V操作, 比如:sem_op = 10(+10)
sem_op = -10(-10)
sem_op = 0, 那么semop函数会等到该信号量的值变为0为止。
sem_flg,
0(最常用的),表示semop函数的操作是阻塞的,直到成功为止。
IPC_NOWAIT,表示semop函数的操作是非阻塞的,如果操作没有成功,立刻返回。
SEM_UNDO(不常用),设置只对当前进程有效,不会保存到系统的信号量集合中。
nops, 调用一次semop要操作的信号量的个数
返回值:成功0, 失败-1
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<errno.h>
typedef union semun{
int val;
}semun_t;
int main(int argc, const char *argv[])
{
int semid;
key_t key;
key = ftok("/",'a');
if((semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST) //等于-1表示 xxx存在
{
semid = semget(key,0,0);
}else{
perror("fail to semget");
return -1;
}
}else{ //如果不存在,就创建 并初始化(下面)
semun_t mysem;
mysem.val = 10;
semctl(semid,0,SETVAL,mysem);
mysem.val = 25;
semctl(semid,1,SETVAL,mysem); //设置2个信号灯
}
printf("semid:%d\n",semid);
printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
struct sembuf buf[2];
buf[0].sem_num = 0;
buf[0].sem_op = -5; //这个是针对0这个-5
buf[0].sem_flg = 0;
buf[1].sem_num = 1;
buf[1].sem_op = 5; //这个是针对1 这个+5
buf[1].sem_flg = 0;
semop(semid,buf,2); //后面2 只的是偏移或者操作的数量 是2个同时操作
printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<errno.h>
typedef union semun{
int val;
}semun_t;
int main(int argc, const char *argv[])
{
int semid;
key_t key;
key = ftok("/",'a');
if((semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
semid = semget(key,0,0);
}else{
perror("fail to semget");
return -1;
}
}else{
semun_t mysem;
mysem.val = 0; //初始化都是0
semctl(semid,0,SETVAL,mysem);
mysem.val = 0; //初始化都是0
semctl(semid,1,SETVAL,mysem);
}
//printf("semid:%d\n",semid);
//printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
struct sembuf buf[2];
buf[0].sem_num = 0;
buf[0].sem_op = 1; //对0 进行+1 初始是1
buf[0].sem_flg = 0;
// buf[1].sem_num = 1;
// buf[1].sem_op = 5; //对1 进行+5 初始是5
// buf[1].sem_flg = 0;
//上面这个没有用
printf("hello\n");
sleep(1);
semop(semid,buf,1); //只操作buf[0]
//printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
return 0;
}
//
//原理就是 hello 和the 用信号灯0
// the 和world 用信号灯1
//
// buf[0] buf[1]
//hello 1 5 初始值
//the 1-1=0 必须灯hello+1 the才能-1 0是阻塞的
// 0 1 设置第二个信号灯初始值是1
//world 0 1-1=0 必须是the+1 world才能-1 0是阻塞的
// 等待the+1 其实就是the给第二个信号灯赋值1
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<errno.h>
typedef union semun{
int val;
}semun_t;
int main(int argc, const char *argv[])
{
int semid;
key_t key;
key = ftok("/",'a');
if((semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
semid = semget(key,0,0);
}else{
perror("fail to semget");
return -1;
}
}else{
semun_t mysem;
mysem.val = 0;
semctl(semid,0,SETVAL,mysem);
mysem.val = 0;
semctl(semid,1,SETVAL,mysem);
}
// printf("semid:%d\n",semid);
// printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
struct sembuf buf[2];
buf[0].sem_num = 0;
buf[0].sem_op = -1; //对第0个 -1
buf[0].sem_flg = 0;
semop(semid,buf,1); //只操作第一个 (数组的第一个)
printf("the\n");
sleep(1);
// buf[0].sem_num = 0;
// buf[0].sem_op = 0; //对第一个
// buf[0].sem_flg = 0;
//
buf[1].sem_num = 1;
buf[1].sem_op = 1;
buf[1].sem_flg = 0;
// semop(semid,buf,2); //要操作两个 这个是同时操作2个
semop(semid,buf+1,1); //这个是只操作第二个
// printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<errno.h>
typedef union semun{
int val;
}semun_t;
int main(int argc, const char *argv[])
{
int semid;
key_t key;
key = ftok("/",'a');
if((semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666)) == -1)
{
if(errno == EEXIST)
{
semid = semget(key,0,0);
}else{
perror("fail to semget");
return -1;
}
}else{
semun_t mysem;
mysem.val = 0;
semctl(semid,0,SETVAL,mysem);
mysem.val = 0;
semctl(semid,1,SETVAL,mysem);
}
// printf("semid:%d\n",semid);
// printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
struct sembuf buf[2];
// buf[0].sem_num = 0;
// buf[0].sem_op = 0;
// buf[0].sem_flg = 0;
buf[1].sem_num = 1;
buf[1].sem_op = -1;
buf[1].sem_flg = 0;
// semop(semid,buf,2);
semop(semid,buf+1,1);
// printf("sem0:%d\nsem1:%d\n",semctl(semid,0,GETVAL),semctl(semid,1,GETVAL));
printf("world\n");
return 0;
}